From d16353b9df0e1433f9f376017e1825516ed26ef6 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 6 Aug 2026 17:13:02 -0700 Subject: [PATCH 1/3] Move the two local edits out of the vendored theme The vendored PaperMod copy carried site customization in two of its files, which is what made an update a diff-and-reapply rather than a directory replace, and what put the repo in breach of its own CODESTYLE rule to override a theme template rather than edit the theme in place. Both sat in extension points the theme documents for the purpose, so both move without a fork. Hugo resolves a project's own trees ahead of the theme's: - The Lexend body rule and the gallery shortcode's layout rules move to assets/css/extended/custom.css. PaperMod's head partial globs css/extended/*.css across the union of both assets/ trees, so a project file is picked up by the same glob that finds the theme's empty placeholder. It is named custom.css rather than blank.css so it unions in alongside that placeholder instead of shadowing it. - The Google Fonts preconnect and stylesheet links move to layouts/_partials/extend_head.html, which overrides the theme's comment-only partial outright. themes/PaperMod/ is now byte identical to upstream 154d006e across all 125 files, verified by diffing against a clone at that commit. themes/README.md records the verification command so the claim stays checkable, and records why a submodule is the only fetch mechanism that would gain a bot here: PaperMod tags releases as v8.0, which is not valid semver, so Hugo Modules could only pin a pseudo-version and Dependabot does not upgrade those. The two root layouts/ overrides are untouched. They work around Hugo 0.158 deprecations upstream has not tracked, rather than customizing the site. Rendered output is byte identical to the previous build, hashed stylesheet name included. Verified under hugo --gc --minify --panicOnWarning, with the URL contract passing at 328/328, 778/778, and 1012/1012, and editorconfig-checker and markdownlint clean. Co-Authored-By: Claude Opus 5 (1M context) --- assets/css/extended/custom.css | 63 +++++++++++++++++++ layouts/_partials/extend_head.html | 13 ++++ themes/PaperMod/assets/css/extended/blank.css | 38 ----------- .../layouts/_partials/extend_head.html | 4 -- themes/README.md | 32 +++++++--- 5 files changed, 98 insertions(+), 52 deletions(-) create mode 100644 assets/css/extended/custom.css create mode 100644 layouts/_partials/extend_head.html diff --git a/assets/css/extended/custom.css b/assets/css/extended/custom.css new file mode 100644 index 0000000..e25f6ea --- /dev/null +++ b/assets/css/extended/custom.css @@ -0,0 +1,63 @@ +/* +Site styles, bundled into the theme's stylesheet. + +PaperMod's head partial concatenates `resources.Match "css/extended/*.css"` across the union +of this project's `assets/` and the theme's, so a file here is picked up by the same glob +that finds the theme's own `extended/blank.css` placeholder. Concatenation is in path +order, which puts this file last; upstream's placeholder carries no rules, so nothing here +depends on that ordering. + +These rules lived in the theme's placeholder until they were moved out. Keeping them here +is what leaves `themes/PaperMod/` byte identical to its upstream commit, so an update is a +directory replace with nothing to reapply. See `themes/README.md`. + +The body font is loaded by `layouts/_partials/extend_head.html`, which was moved out of the +vendored tree for the same reason. The two belong together. +*/ + +body { + font-family: 'Lexend', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-size: 18px; + line-height: 1.6; + word-break: break-word; + background: var(--theme); +} + +/* Layout for the `gallery` shortcode at `layouts/shortcodes/gallery.html`. */ +.gallery { + display: flex; + flex-wrap: wrap; +} + +.gallery figure, +.gallery figure img { + text-align: center; +} + +.gallery figure img { + margin: 1rem auto; +} + +.gallery-cols-1 figure { + width: 100%; +} + +.gallery-cols-2 figure { + width: 50%; +} + +.gallery-cols-3 figure { + width: 33.3333333333%; +} + +.gallery-cols-4 figure { + width: 25%; +} + +.gallery-cols-5 figure { + width: 25%; +} + +.gallery-cols-6 figure { + width: 16.666666666%; +} diff --git a/layouts/_partials/extend_head.html b/layouts/_partials/extend_head.html new file mode 100644 index 0000000..ecfb174 --- /dev/null +++ b/layouts/_partials/extend_head.html @@ -0,0 +1,13 @@ +{{- /* Head custom content area start */ -}} +{{- /* Insert any custom code (web-analytics, resources, etc.) - it will appear in the section of every page. */ -}} +{{- /* Can be overwritten by partial with the same name in the global layouts. */ -}} +{{- /* Head custom content area end */ -}} +{{- /* */ -}} +{{- /* This file is that override. It carries the Lexend web font the body rule in */ -}} +{{- /* `assets/css/extended/custom.css` selects, which is why the two move together. */ -}} +{{- /* Holding it here leaves `themes/PaperMod/` byte identical to its upstream commit, */ -}} +{{- /* so an update is a directory replace with nothing to reapply. See `themes/README.md`. */ -}} + + + + diff --git a/themes/PaperMod/assets/css/extended/blank.css b/themes/PaperMod/assets/css/extended/blank.css index 9e2469d..a577295 100644 --- a/themes/PaperMod/assets/css/extended/blank.css +++ b/themes/PaperMod/assets/css/extended/blank.css @@ -3,41 +3,3 @@ This is just a placeholder blank stylesheet so as to support adding custom style Read https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#bundling-custom-css-with-themes-assets for more info */ - -body { - font-family: 'Lexend', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - font-size: 18px; - line-height: 1.6; - word-break: break-word; - background: var(--theme); -} - -.gallery { - display: flex; - flex-wrap: wrap; -} -.gallery figure, -.gallery figure img { - text-align: center; -} -.gallery figure img { - margin: 1rem auto; -} -.gallery-cols-1 figure { - width: 100%; -} -.gallery-cols-2 figure { - width: 50%; -} -.gallery-cols-3 figure { - width: 33.3333333333%; -} -.gallery-cols-4 figure { - width: 25%; -} -.gallery-cols-5 figure { - width: 25%; -} -.gallery-cols-6 figure { - width: 16.666666666%; -} diff --git a/themes/PaperMod/layouts/_partials/extend_head.html b/themes/PaperMod/layouts/_partials/extend_head.html index 550f31d..150cbef 100644 --- a/themes/PaperMod/layouts/_partials/extend_head.html +++ b/themes/PaperMod/layouts/_partials/extend_head.html @@ -2,7 +2,3 @@ {{- /* Insert any custom code (web-analytics, resources, etc.) - it will appear in the section of every page. */ -}} {{- /* Can be overwritten by partial with the same name in the global layouts. */ -}} {{- /* Head custom content area end */ -}} - - - - diff --git a/themes/README.md b/themes/README.md index c1c7189..7dd6613 100644 --- a/themes/README.md +++ b/themes/README.md @@ -14,23 +14,35 @@ Vendoring is the decision; not recording what was vendored was the gap. Without | Describes as | `v8.0-138-g154d006` | | License | MIT, retained at `PaperMod/LICENSE` | -The commit was recovered by matching all 125 tracked blobs against upstream history rather than by reading a version marker, since the copy carries none. Every file matches that commit exactly except the two below, so the identification is not approximate. +The commit was recovered by matching all 125 tracked blobs against upstream history rather than by reading a version marker, since the copy carries none. -### Local edits +### Local edits: none -Both sit in extension points the theme documents for this purpose, so neither is a fork of theme logic. +All 125 files match that commit byte for byte, so the identification is exact rather than approximate and the tree is replaceable wholesale. Verify with a clone of upstream at that commit: -| File | Edit | -| --- | --- | -| `PaperMod/assets/css/extended/blank.css` | The theme's custom-CSS slot, which ships empty. Carries the Lexend body font and the `gallery` and `gallery-cols-*` rules the gallery shortcode needs. | -| `PaperMod/layouts/_partials/extend_head.html` | The theme's head-extension partial, which ships empty. Carries the Google Fonts preconnect and stylesheet links for Lexend. | +```sh +git clone https://github.com/adityatelange/hugo-PaperMod.git /tmp/papermod +git -C /tmp/papermod checkout 154d006e0182dfc7da38008323976b02e6bfab4a +diff -r --exclude=.git /tmp/papermod themes/PaperMod +``` + +Two files did carry edits, in extension points the theme documents for the purpose. They now live outside the vendored tree, where Hugo resolves a project's own `assets/` and `layouts/` ahead of the theme's: + +| Customization | Now at | Replaces the theme's | +| --- | --- | --- | +| Lexend body font, and the `gallery` and `gallery-cols-*` rules the gallery shortcode needs | [`assets/css/extended/custom.css`](../assets/css/extended/custom.css) | `assets/css/extended/blank.css`, an empty slot the theme's head partial globs for | +| Google Fonts preconnect and stylesheet links for Lexend | [`layouts/_partials/extend_head.html`](../layouts/_partials/extend_head.html) | `layouts/_partials/extend_head.html`, an empty partial the theme's head partial calls | -Both could live outside the vendored tree instead: Hugo resolves a project's own `assets/css/extended/` and `layouts/_partials/` ahead of the theme's, so moving them would make an update a clean directory replace with nothing to reapply. Worth doing at the next update rather than as a change of its own. +The two belong together: the font the CSS selects is the font the partial loads. Neither is a fork of theme logic, and moving them changed no rendered byte. -Separately, `layouts/` at the repository root already overrides two theme templates, for the reason recorded in [`TODO.md`](../TODO.md): PaperMod uses APIs Hugo deprecated in 0.158, and `--panicOnWarning` would otherwise fail on the theme rather than on content. Whether those overrides are still needed is answerable by diffing against the commit above, which is what this record exists for. +Separately, `layouts/` at the repository root also overrides two theme templates, for the reason recorded in [`TODO.md`](../TODO.md): PaperMod uses APIs Hugo deprecated in 0.158, and `--panicOnWarning` would otherwise fail on the theme rather than on content. Those are a workaround for upstream lag rather than site customization, which is why they are not in the table above. Whether they are still needed is answerable by diffing against the commit recorded here, which is what this record exists for. ## Updating -Compare against the recorded commit first, so the local edits above are known before anything moves. Replace `PaperMod/` with the new upstream tree, reapply the two edits (or move them out, per the note above), update the table here, and confirm the site still builds under `--panicOnWarning`, which is the gate the theme has failed before. +Nothing is carried, so an update is a replace: delete `PaperMod/`, drop the new upstream tree in its place, update the table above, and confirm the site still builds under `--panicOnWarning`, which is the gate the theme has failed before. Run the `diff -r` above afterwards, so the next reader inherits the same guarantee. + +Check the two root `layouts/` overrides at the same time. They exist only because upstream lags Hugo's deprecations, so an update is the moment one of them may become removable. No bot watches this. `.github/dependabot.yml` covers GitHub Actions only, since a vendored copy has no manifest to track, so an update is a deliberate act. + +Fetching the theme rather than copying it is the way to get a bot, and only one of the two mechanisms would work here. Dependabot's `gitsubmodule` ecosystem tracks a ref and needs no tags, so a submodule would be watched. Hugo Modules would not: PaperMod tags releases as `v8.0`, which is not valid semver, so Go can only pin it as a pseudo-version, and Dependabot does not upgrade pseudo-versions. Either mechanism first requires the tree to carry no local edits, which is now true. Weigh it against what a bot would have found: between 2026-05-10 and 2026-08-06, upstream's only commit edited its own README. From 06477591408855e58b7d118a6187e516243c657b Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 6 Aug 2026 17:39:04 -0700 Subject: [PATCH 2/3] Document the theme's width knobs and where to override them The question "how do I widen the page" has no answer in hugo.yaml, and finding that out costs a read of the theme's CSS. PaperMod exposes no width parameter to any template, so the only levers are four custom properties on :root in core/theme-vars.css, and nothing in the repo said so. themes/README.md gains a "Customization points" section recording the four variables, what consumes each and at what computed width, and two things that are easy to get wrong: - The width is a fixed pixel cap with no responsive term. zmedia.css, the theme's only media-query file, never touches --main-width or --nav-width, so the content column is 720px from a laptop to an ultrawide. The nav is already 304px wider than the content, so the two have to move together. - Overrides belong in assets/css/extended/custom.css, which the head partial concatenates after the core sheet, so they win on source order with no !important. The trap is that media queries add no specificity, so a top-level :root there also beats zmedia inside its own breakpoint. That is safe for the width variables, which zmedia never sets, and wrong for --gap, which it does. The section also records why the default stands: 720px at 18px is about 80 characters per line, already at the top of the readable range. Images and galleries inheriting the same cap is the part that costs something on a wide display, and the fix for that is media breaking out of the column rather than a wider column. TODO.md carries a one-line pointer under Open decisions so the question is findable from the place someone would look for it. OPERATIONS.md is deploy and serving, so it is the wrong home. Docs only. No rendered byte changes; verified under hugo --gc --minify --panicOnWarning against the same baseline, with markdownlint and editorconfig-checker clean. The audit report's themes/README.md:12 citation for hugo.vendored.provenance still lands on the commit row. Co-Authored-By: Claude Opus 5 (1M context) --- TODO.md | 1 + themes/README.md | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/TODO.md b/TODO.md index b75b056..da499b2 100644 --- a/TODO.md +++ b/TODO.md @@ -51,6 +51,7 @@ The reference leaf the hub now ships carries one step this repo's deploy does no ## Open decisions - `/robots.txt/` and `/osd.xml/` currently sit in `slugs.map` pointing at `/`. The first would be better pointing at the real `/robots.txt`. +- Content is capped at a fixed 720px on every screen, because PaperMod's width is four CSS variables with no responsive term and no Hugo parameter. The prose measure is right and should stay; images and galleries inheriting the same cap is the part that costs something on a wide display. The knobs, the override location, and the `--gap` trap are documented under "Customization points" in [`themes/README.md`](./themes/README.md). ## Deliberate deviations from the fleet baseline diff --git a/themes/README.md b/themes/README.md index 7dd6613..aa54153 100644 --- a/themes/README.md +++ b/themes/README.md @@ -37,6 +37,27 @@ The two belong together: the font the CSS selects is the font the partial loads. Separately, `layouts/` at the repository root also overrides two theme templates, for the reason recorded in [`TODO.md`](../TODO.md): PaperMod uses APIs Hugo deprecated in 0.158, and `--panicOnWarning` would otherwise fail on the theme rather than on content. Those are a workaround for upstream lag rather than site customization, which is why they are not in the table above. Whether they are still needed is answerable by diffing against the commit recorded here, which is what this record exists for. +## Customization points + +PaperMod exposes **no Hugo configuration for layout width**. No template reads a width parameter, so `hugo.yaml` cannot change it and the only levers are four CSS custom properties on `:root` in `PaperMod/assets/css/core/theme-vars.css`. + +| Variable | Default | Consumed by | Computed | +| --- | --- | --- | --- | +| `--main-width` | `720px` | `.main`, `.footer`, each as `calc(var(--main-width) + var(--gap) * 2)` | 768px | +| `--nav-width` | `1024px` | `.nav`, as `calc(var(--nav-width) + var(--gap) * 2)` | 1072px | +| `--gap` | `24px` | the outer padding in all three, and spacing throughout | | +| `--content-gap` | `20px` | vertical rhythm inside post content | | + +Two things about this are easy to get wrong. + +**The width is a fixed pixel cap with no responsive term.** `core/zmedia.css` is the theme's only media-query file, and it never touches `--main-width` or `--nav-width`. It changes `--gap` to `14px` below 768px and nothing else. So the content column is 720px from a small laptop to an ultrawide, and widening it means introducing a viewport term the theme does not have. The nav is already 304px wider than the content, so raising `--main-width` past `--nav-width` without raising both puts the content outside the header. + +**Override these in [`assets/css/extended/custom.css`](../assets/css/extended/custom.css), never here.** `PaperMod/layouts/_partials/head.html` concatenates the core sheet, `zmedia.css` last within it, and then the `css/extended/*.css` glob after all of it, so a `:root` block in the extended file wins on source order with no `!important` and no theme edit. + +That ordering carries a trap: media queries add no specificity, so a top-level `:root` in the extended file also overrides `zmedia.css` **inside its own breakpoint**. Redefining `--main-width` or `--nav-width` there is safe, because zmedia never sets them. Redefining `--gap` at top level is not, because it silently restores the 24px gutter on phones. + +The content width is unchanged from the theme default, deliberately. 720px at the 18px body size is roughly 80 characters per line, which is already at the top of the readable range, so widening the prose is a regression dressed as an improvement. The constraint worth revisiting is that images and galleries inherit the same cap, which is a real loss on a wide display for a photo-heavy site; the fix for that is to let media break out of the column rather than to widen the column. + ## Updating Nothing is carried, so an update is a replace: delete `PaperMod/`, drop the new upstream tree in its place, update the table above, and confirm the site still builds under `--panicOnWarning`, which is the gate the theme has failed before. Run the `diff -r` above afterwards, so the next reader inherits the same guarantee. From 07a292ea97bff10089a06d2b82724a401fcf0d16 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 6 Aug 2026 17:55:56 -0700 Subject: [PATCH 3/3] Name the real selector for --nav-width, and record the cols-5 defect Two findings from the review of #40. The width table said `--nav-width` is consumed by `.nav`. It is not; the theme applies it to `.header-nav` in common/header.css, and `.nav` is not a selector in the theme at all, only `.nav-sep`. A reader following the table would have searched for a rule that does not exist. Both rows now name the file as well as the selector, since the point of the table is to be followed. `.gallery-cols-5 figure` really is wrong: the rule's own pattern is 1/N (100, 50, 33.33, 25, ..., 16.67) and the five-column case repeats the four-column 25%, so it renders four per row and wraps the fifth. It predates this branch, and one page uses it. It is recorded under Open decisions rather than fixed here, because this branch's claim is that the move changed no rendered byte and is verified by diffing the built site. A render change would retire the only evidence that the move was safe. It is a one-value fix worth its own change, where it can be looked at rather than diffed. Co-Authored-By: Claude Opus 5 (1M context) --- TODO.md | 1 + themes/README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index da499b2..857856d 100644 --- a/TODO.md +++ b/TODO.md @@ -51,6 +51,7 @@ The reference leaf the hub now ships carries one step this repo's deploy does no ## Open decisions - `/robots.txt/` and `/osd.xml/` currently sit in `slugs.map` pointing at `/`. The first would be better pointing at the real `/robots.txt`. +- `.gallery-cols-5 figure` is `width: 25%` where the rule's own 1/N pattern makes it `20%`, so a five-column gallery renders four per row and wraps the fifth. It is a copy of the `cols-4` value, it predates the move of these rules to [`assets/css/extended/custom.css`](./assets/css/extended/custom.css), and one page uses it: [the Flair smart vents post](./content/posts/2022/10/30/installing-flair-smart-vents-to-keep-room-temperatures-balanced.md). Left alone in the move so that change could claim byte-identical rendered output and be verified by diffing the built site; fixing it is a one-value change that must be seen rendered rather than diffed. - Content is capped at a fixed 720px on every screen, because PaperMod's width is four CSS variables with no responsive term and no Hugo parameter. The prose measure is right and should stay; images and galleries inheriting the same cap is the part that costs something on a wide display. The knobs, the override location, and the `--gap` trap are documented under "Customization points" in [`themes/README.md`](./themes/README.md). ## Deliberate deviations from the fleet baseline diff --git a/themes/README.md b/themes/README.md index aa54153..601beb3 100644 --- a/themes/README.md +++ b/themes/README.md @@ -43,8 +43,8 @@ PaperMod exposes **no Hugo configuration for layout width**. No template reads a | Variable | Default | Consumed by | Computed | | --- | --- | --- | --- | -| `--main-width` | `720px` | `.main`, `.footer`, each as `calc(var(--main-width) + var(--gap) * 2)` | 768px | -| `--nav-width` | `1024px` | `.nav`, as `calc(var(--nav-width) + var(--gap) * 2)` | 1072px | +| `--main-width` | `720px` | `.main` in `common/main.css`, `.footer` in `common/footer.css`, each as `calc(var(--main-width) + var(--gap) * 2)` | 768px | +| `--nav-width` | `1024px` | `.header-nav` in `common/header.css`, as `calc(var(--nav-width) + var(--gap) * 2)` | 1072px | | `--gap` | `24px` | the outer padding in all three, and spacing throughout | | | `--content-gap` | `20px` | vertical rhythm inside post content | |