jekyll-theme-zer0 1.30.0 → 1.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +171 -0
  3. data/_data/backlog.yml +30 -7
  4. data/_data/consumers.yml +99 -0
  5. data/_data/features.yml +1 -1
  6. data/_data/theme-manifest.yml +35 -33
  7. data/_data/ui-text.yml +1 -0
  8. data/_includes/README.md +1 -1
  9. data/_includes/analytics/posthog.html +28 -9
  10. data/_includes/components/README.md +1 -1
  11. data/_includes/components/background-image.html +7 -1
  12. data/_includes/components/cookie-consent.html +30 -30
  13. data/_includes/components/mermaid.html +14 -7
  14. data/_includes/components/preview-image.html +22 -3
  15. data/_includes/components/theme-preview-gallery.html +1 -1
  16. data/_includes/content/intro.html +7 -0
  17. data/_includes/content/seo.html +8 -0
  18. data/_includes/content/sitemap.html +220 -263
  19. data/_includes/core/branding.html +8 -15
  20. data/_includes/core/head.html +29 -5
  21. data/_includes/core/header.html +14 -2
  22. data/_includes/core/tokens-inline.html +16 -1
  23. data/_includes/navigation/navbar.html +9 -6
  24. data/_includes/setup/wizard.html +5 -1
  25. data/_layouts/article.html +15 -3
  26. data/_layouts/home.html +13 -1
  27. data/_layouts/note.html +2 -1
  28. data/_layouts/notebook.html +2 -1
  29. data/_layouts/root.html +22 -0
  30. data/_sass/components/_cookie-banner.scss +48 -0
  31. data/_sass/components/_setup-wizard.scss +19 -1
  32. data/_sass/components/_ui-enhancements.scss +11 -0
  33. data/_sass/core/_navbar.scss +137 -70
  34. data/_sass/core/_sidebar-extras.scss +2 -14
  35. data/_sass/core/code-copy.scss +27 -8
  36. data/assets/css/extension-points-demo.css +27 -0
  37. data/assets/data/wiki-index.json +19 -0
  38. data/assets/js/auto-hide-nav.js +53 -13
  39. data/assets/js/code-copy.js +72 -1
  40. data/assets/js/extension-points-demo.js +48 -0
  41. data/assets/js/mermaid-diagrams.js +128 -22
  42. data/assets/js/obsidian-local-graph.js +27 -5
  43. data/assets/js/setup-wizard.js +7 -2
  44. data/scripts/ci/test_visual_evidence_autogen.py +64 -2
  45. data/scripts/ci/visual_evidence_autogen.py +14 -4
  46. data/scripts/features/install-preview-generator +1 -1
  47. data/scripts/lib/README.md +12 -0
  48. data/scripts/lib/preview_generator.py +2 -2
  49. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d866d7e61b25af04b19f400542928309cf8f8ff413fa5bb7cf1366b39a5c48eb
4
- data.tar.gz: 33d0b54c7b2b957b9cc09d004043bb6c91e180564d1f0866b1b00d67c0862fde
3
+ metadata.gz: ff732799298a7a924f37b0e9cfe891b1f7b4a77e4d87bb915ed5250af1cc996c
4
+ data.tar.gz: 8fa5daa533ca6e9737c73a9d774850b1e75d7620118820045f8f9523de0d04d3
5
5
  SHA512:
6
- metadata.gz: 8e3477647576dd39284770ba4007bbcbaa3cd4f3e2a91de84c2375e9b65650bd217e04a30ac27af2c2ca71be9b64ed7f76f6a4c182664882f4f92d20d3856856
7
- data.tar.gz: e1699b92f82673564a2673d297beed0da91371cfb99d8bc513ce9dc1001460d4d8daf56cc379a6e25402f8f26cdddad003ef406600274c26021f0b505947af14
6
+ metadata.gz: 565df6a9b2259c4ed7874a70f8c0796ce3505124000d32648aca3b7160277f02215b4d4a25bab533d24d3448e9721646995085bcd81e1713ed09724adeff0721
7
+ data.tar.gz: 00e9ad078bafd4f848a6a26a9c8b9fcdd8c30bce679fc54a3b0e0c652ccad9fdc86c2a4e974a778fcdfd7b3dc180646d1cb6542c011b3096698f4a97baabf68a
data/CHANGELOG.md CHANGED
@@ -12,6 +12,133 @@ hand-written prose covering the same releases in more depth; they sit below
12
12
  their version because release-please inserts each new release at the top of the
13
13
  file. Only `## [Unreleased]` describes work that has not shipped yet.
14
14
 
15
+ ## [1.31.0](https://github.com/bamr87/zer0-mistakes/compare/v1.30.0...v1.31.0) (2026-10-07)
16
+
17
+
18
+ ### Features
19
+
20
+ * **navigation:** two clean navbar label tiers, merged chevron, centred grid ([#495](https://github.com/bamr87/zer0-mistakes/issues/495)) ([9ebb29a](https://github.com/bamr87/zer0-mistakes/commit/9ebb29a4662fbc4c62d3fdd8df9c282bdd181204))
21
+ * **theme:** four consumer extension points for notes/knowledge-base builds ([#492](https://github.com/bamr87/zer0-mistakes/issues/492)) ([1e2b228](https://github.com/bamr87/zer0-mistakes/commit/1e2b2283303012b28b9e81209fb66d0344aa0597))
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * **article:** eager-load the hero LCP image and reserve its box ([#498](https://github.com/bamr87/zer0-mistakes/issues/498)) ([529b84b](https://github.com/bamr87/zer0-mistakes/commit/529b84bd0f5ea8e79b7711187b52b2bb80c5b134)), closes [#485](https://github.com/bamr87/zer0-mistakes/issues/485)
27
+ * **ci:** keep a LiveReload push from crashing the generic evidence generator ([#500](https://github.com/bamr87/zer0-mistakes/issues/500)) ([eb1b5ae](https://github.com/bamr87/zer0-mistakes/commit/eb1b5ae201ed0829e0c3fac05e629e55a6ea1040))
28
+ * **ci:** Playwright 1.58 attachment names, docs front matter, test race ([#497](https://github.com/bamr87/zer0-mistakes/issues/497)) ([f1bdc7e](https://github.com/bamr87/zer0-mistakes/commit/f1bdc7ef22091f0506617b11a95e357c439cfa94))
29
+ * **ci:** stop auto-merge guard failing on Dependabot Actions bumps ([#503](https://github.com/bamr87/zer0-mistakes/issues/503)) ([a998b2d](https://github.com/bamr87/zer0-mistakes/commit/a998b2dd9cf96bb1fd09ac3a95644908713afc37))
30
+ * **features:** escape `<en-url>` in the translation card so the Features page renders ([#505](https://github.com/bamr87/zer0-mistakes/issues/505)) ([8e4345f](https://github.com/bamr87/zer0-mistakes/commit/8e4345f28dfc675b5efe95ba66ec72af613cbcf5))
31
+ * **includes:** join preview paths without a leading slash under assets_prefix ([#509](https://github.com/bamr87/zer0-mistakes/issues/509)) ([406250c](https://github.com/bamr87/zer0-mistakes/commit/406250c2b5b94469cae9a0b6f93e8782cb8509f9))
32
+ * **includes:** stop the draft chip shifting the Site Builder on first save ([#489](https://github.com/bamr87/zer0-mistakes/issues/489)) ([d472aa2](https://github.com/bamr87/zer0-mistakes/commit/d472aa267e164212cf8b844e701cfcdad08e4d8d))
33
+ * **layouts:** one main landmark per page on post, notebook and note ([#487](https://github.com/bamr87/zer0-mistakes/issues/487)) ([78b0910](https://github.com/bamr87/zer0-mistakes/commit/78b0910fb2258591eab9ba13efb5ac037eee80c5))
34
+ * **navigation:** stop Liquid whitespace-trim gluing navbar attributes ([#477](https://github.com/bamr87/zer0-mistakes/issues/477)) ([4349ea1](https://github.com/bamr87/zer0-mistakes/commit/4349ea17d690e5e6b8585f1d3a33b3ea4d3bf4fe))
35
+ * **scripts:** default xAI preview renderer to grok-imagine-image-2.0 ([#499](https://github.com/bamr87/zer0-mistakes/issues/499)) ([95cebdb](https://github.com/bamr87/zer0-mistakes/commit/95cebdb535799544f05ed5929e528a074448ef63)), closes [#474](https://github.com/bamr87/zer0-mistakes/issues/474)
36
+ * **tests:** make the Liquid nested-tag and gem-content checks able to fail ([#478](https://github.com/bamr87/zer0-mistakes/issues/478)) ([8510d08](https://github.com/bamr87/zer0-mistakes/commit/8510d0824f79823f66f765f12db9be9b67fff3ae))
37
+ * **tokens:** let the active skin win over site theme_color for skin-owned tokens ([#502](https://github.com/bamr87/zer0-mistakes/issues/502)) ([af468e0](https://github.com/bamr87/zer0-mistakes/commit/af468e072543a448e7be80230f3d3a246b5e758e))
38
+ * **ui:** make the cookie consent banner a compact bottom bar ([#510](https://github.com/bamr87/zer0-mistakes/issues/510)) ([2d7d0db](https://github.com/bamr87/zer0-mistakes/commit/2d7d0db7f9f6b1433b63d8be76103cce2fd7fb05))
39
+
40
+
41
+ ### Performance Improvements
42
+
43
+ * **js:** lazy-load Mermaid and cytoscape, defer PostHog, stop nav reflows ([#508](https://github.com/bamr87/zer0-mistakes/issues/508)) ([702789c](https://github.com/bamr87/zer0-mistakes/commit/702789cecf93d8e9b9f7013168ca47f0294064eb))
44
+ * **sitemap:** keep one page of the index in the DOM and build cards lazily ([#511](https://github.com/bamr87/zer0-mistakes/issues/511)) ([9e42fcb](https://github.com/bamr87/zer0-mistakes/commit/9e42fcbd9421a994fd22a347bbef95573ce340a9))
45
+
46
+ ## [Unreleased]
47
+
48
+ ### Added
49
+
50
+ - **Four consumer extension points** ([#412](https://github.com/bamr87/zer0-mistakes/issues/412)), documented at [`/docs/customization/extension-points/`](pages/_docs/customization/extension-points.md) and [`docs/development/extension-points.md`](docs/development/extension-points.md):
51
+ - `zer0:code-block-ready` — a bubbling `CustomEvent` dispatched once per code block carrying `{ wrapper, header, pre, code, lang }`, plus `window.zer0OnCodeBlock(fn)`, which **replays** already-decorated blocks before subscribing, and `window.__zer0CodeBlocks`. This replaces the `MutationObserver` and wrapper-shape guessing a consumer previously needed — the guess is what silently rendered two buttons on every block downstream.
52
+ - `styles:` / `scripts:` frontmatter — per-page CSS and JS resolved through `relative_url`, with `scripts:` deferred after the theme bundle. A page that declares neither emits nothing at all.
53
+ - `lastmod` and `description` on every `/assets/data/wiki-index.json` entry — ISO-8601 and the authored subtitle, always defined (`null` when the document has neither), no new traversal.
54
+
55
+ ### Changed
56
+
57
+ - **Navbar label tiers reduced from three to two** ([#405](https://github.com/bamr87/zer0-mistakes/issues/405)).
58
+ Top-level nav items now render as icon + full label above 51rem of centre
59
+ track and icon-only below it; `text-overflow: ellipsis` is gone, so a label
60
+ is never cut to "Quicksta…". Icons now survive in **both** tiers — the old
61
+ 41–50.99rem tier dropped them to buy width for bare labels. **Behaviour
62
+ change:** centre-track widths between 41rem and 51rem (roughly 992–1200px
63
+ viewports) previously showed bare labels and now show icons, with the
64
+ existing `.nav-tooltip` carrying the name on hover.
65
+ - **Dropdown chevrons merged into their parent row** ([#405](https://github.com/bamr87/zer0-mistakes/issues/405)).
66
+ The split toggle is now absolutely positioned inside padding the parent link
67
+ reserves, and hover applies to the whole row, so there is no dead zone
68
+ between the label and its chevron. It remains a real `<button>` with
69
+ `aria-expanded`, `aria-haspopup` and its visually-hidden label unchanged.
70
+ (evidence: [`test/visual/evidence/navbar-tiers-405/`](test/visual/evidence/navbar-tiers-405/README.md) — chevron dead zone up to 4px → overlapping; 0 truncated labels across 6 widths)
71
+ - **The navbar carries logo + title only** ([#405](https://github.com/bamr87/zer0-mistakes/issues/405)).
72
+ `site.subtitle` moved out of `_includes/core/branding.html` and onto the home
73
+ hero (`_layouts/home.html`), returning its width to the menubar. It renders
74
+ under the page heading on pages that use the `home` layout, and is hidden
75
+ with it by `hide_title` or `hide_intro`. A homepage on another layout, such
76
+ as `landing`, no longer shows the subtitle at all; add `site.subtitle` to
77
+ that layout's hero to keep it.
78
+ - **The below-`lg` menu toggle is labelled "Menu"** ([#405](https://github.com/bamr87/zer0-mistakes/issues/405)),
79
+ so it is distinguishable from the sidebar hamburger beside it. Override the
80
+ string with `ui.nav_menu_toggle_label`.
81
+ - The desktop navbar grid gives its two side tracks a shared minimum
82
+ (`--zer0-navbar-side-min`, default `9rem`) so the menubar is optically
83
+ centred in the bar rather than only within its own track.
84
+
85
+ ### Fixed
86
+
87
+ - **The cookie banner no longer covers the first screen.** Below 768px `components/cookie-consent.html` stacked a heading, a long sentence and three full-width buttons (the shim in `_sass/core/_sidebar-extras.scss`): 324px of an 850px-tall 400px viewport on it-journey.dev and lifehacker.dev, hiding most of the hero or article; on desktop it was 94px and sat over the lower hero copy. It is now a compact bar: a two-line summary over one row of three equal buttons on phones, and a single row from 768px up. The layout lives in `_sass/components/_cookie-banner.scss` and the stacking shim is gone. The banner is a labelled `region` ("Cookie consent"), and the button ids, the modal and the consent logic are unchanged. Height at 400px drops from 324px to 107px, and at 1366px from 94px to 48px. Pinned by `test/visual/features/cookie-banner-compact.spec.js`.
88
+ - **The active skin now moves the whole `--zer0-color-*` layer, not just Bootstrap's** ([#459](https://github.com/bamr87/zer0-mistakes/issues/459)). `_includes/core/tokens-inline.html` re-set `--zer0-color-primary`, `-link` and `-accent` from `theme_color` on `:root` after `main.css`. `:root` and `[data-theme-skin]` have the same specificity, so the config value won on source order. On any site that sets `theme_color`, components reading the theme layer therefore kept the config color in every skin, and `--zer0-color-primary` disagreed with its own `-rgb` companion. The skin now wins: those three keys are emitted only when no palette skin is active, as with `dark` and `contrast`. `theme_color` still sets `secondary`, `danger`, `warning`, `success` and `info`, and the Appearance-panel override still beats both. The rule is documented in [`docs/ui/design-tokens.md`](docs/ui/design-tokens.md#precedence-skin-vs-theme_color) and checked by `test/visual/features/appearance.spec.js` (evidence: [`test/visual/evidence/agent-issue-459/`](test/visual/evidence/agent-issue-459/README.md) — page overflow 0px at six widths; the nine skin baselines are unchanged).
89
+ - **A preview path without a leading slash no longer 404s.** `preview: images/previews/x.webp` was appended straight onto `assets_prefix`, so `components/preview-image.html`, `components/background-image.html`, `content/intro.html` and `content/seo.html` emitted `/assetsimages/previews/x.webp` for the card image, the hero background and `og:image` (165 it-journey content files use that form). All four now prepend `/` to a site-relative path before joining; URLs and `data:` URIs are left alone, and already-slashed or `/assets/`-prefixed paths render byte-identically. `preview-image.html` also gained optional `width`/`height` parameters (emitted only when passed); the article layout's related-post cards pass 1536×1024 so their box is reserved before the lazy image arrives (0px → 217px at 1366px). Pinned by the `Preview Path Join` unit test in `test/test_core.sh` and `test/visual/features/preview-path-join.spec.js`.
90
+ - **The sitemap index no longer puts every page in the DOM twice.** `content/sitemap.html` (the `/sitemap/` page, the search modal's "View all results" target, and it-journey.dev's `/search/`) kept all rows attached. Its script also eagerly built a second copy of every row as a card for the hidden card view: about 19,900 elements on it-journey's `/search/` (431 entries), with Lighthouse mobile TBT at 9.7 s. Rows are still rendered server-side, so the page works without JS and with `?q=`. The script now keeps only one page of 50 matches in the document and appends the next page with **Show more**. Search, filters and sorting work across every row, and cards are built on the first switch to card view, one page at a time. Card text is set through `textContent` rather than interpolated HTML. On the theme's own `/sitemap/` (386 entries), elements after load drop from 23,063 to 8,086, and local Lighthouse mobile TBT drops from 1,662 ms to 181 ms. Badge contrast is fixed too: the collection pill is `text-bg-info` (was white on `#0dcaf0`, 1.96:1), tag chips use `--bs-primary-text-emphasis`, and `+N` overflow chips (white on transparent) get body text and a border. Pinned by `test/visual/features/sitemap-pagination.spec.js`.
91
+ - **The xAI preview-image renderer defaults to `grok-imagine-image-2.0`** ([#474](https://github.com/bamr87/zer0-mistakes/issues/474)). `XAIProvider.default_model()` in `scripts/lib/preview_generator.py` still returned the retired `grok-2-image`, so a site with `preview_images.provider: xai` and no explicit `model:` sent a retired model upstream. An explicitly configured `grok-2-image` is still accepted as an xAI-family model.
92
+ - **A code-copy partial no longer restyles every button on the site** ([#412](https://github.com/bamr87/zer0-mistakes/issues/412)). `_sass/core/code-copy.scss` shipped a bare `.button, button:not(.copy)` rule — an element selector at specificity (0,1,1) that out-ranked `.btn` and any consumer's own class, applying `padding: 0 20px`, `font-size: 11px` and a hardcoded `#bbb` border site-wide. It is scoped to `.code-block-header` / `pre.highlight` now and uses `--zer0-*` tokens, so `#bbb` is gone from the compiled stylesheet. Every button on the site — the theme's own chrome included — gets its intended metrics back (evidence: [`test/visual/evidence/agent-issue-412/`](test/visual/evidence/agent-issue-412/README.md) — page overflow 0px at six widths; the nine skin baselines moved, and every red region in their diffs is a `<button>`).
93
+ - **The article hero no longer lazy-loads its LCP image or shoves the article down when it arrives** ([#485](https://github.com/bamr87/zer0-mistakes/issues/485)). `figure.featured-hero` inherited `components/preview-image.html`'s `loading="lazy"` default and had no reserved box. It now passes `loading="eager" fetchpriority="high" decoding="async"`, and its box is pinned by `.featured-hero img { aspect-ratio: 3 / 2 }`. The box is set in CSS rather than with `width`/`height` attributes, because preview assets vary in shape and `object-fit: cover` crops a portrait instead of stretching it. `preview-image.html` gained optional `fetchpriority`/`decoding` parameters that are emitted only when passed, so every other consumer renders byte-identically (evidence: [`test/visual/evidence/hero-lcp/`](test/visual/evidence/hero-lcp/README.md) — hero loading lazy → eager + fetchpriority=high; content shift when the hero lands 476px → 0px across 2 widths × landscape/portrait).
94
+ - **Mermaid, cytoscape and PostHog no longer load up front, and the auto-hide navbar stops forcing reflows.** `assets/js/mermaid-diagrams.js` now converts fences into figures straight away but injects the ~1 MB Mermaid bundle only when a diagram exists and the first one comes within 400px of the viewport (IntersectionObserver); `components/mermaid.html` passes the bundle URL as `window.zer0MermaidSrc` (a script global, not DOM text, and only http(s) URLs are injected) instead of a `<script defer>`. The local-graph panel loads cytoscape on first open instead of on every indexed page. `analytics/posthog.html` keeps the queueing stub but fetches `array.js` after `load` + idle, and `core/head.html` no longer includes it a second time (`root.html` already did, so every production page called `posthog.init()` twice). `auto-hide-nav.js` measures the navbar with a ResizeObserver (rAF read/next-frame write fallback) and caches the height instead of reading `offsetHeight` at DOMContentLoaded and on resize. Lighthouse mobile, local static build: home TBT 1,705 → 651 ms and weight 5,581 → 2,327 KiB; `/docs/features/mermaid-diagrams/` TBT 2,017 → 243 ms; `auto-hide-nav.js` gone from forced-reflow. New lazy-load test in `test/visual/features/mermaid.spec.js`.
95
+ ### Fixed
96
+
97
+ - **The Site Builder no longer jumps the first time it saves a draft.**
98
+ `showDraftChip()` cleared the "Draft saved" chip's `hidden` attribute, and the
99
+ 2s timer that follows only removes `is-visible` — an opacity class — so the
100
+ first debounced save moved the chip from `display: none` into layout *for
101
+ good*. Its box is 26px against the 19px "Start over" button beside it, so
102
+ `.wizard-header` grew and took `#wizardTabContent` and every Back/Next row
103
+ below it down with it: 7px where the header's right-hand block had already
104
+ wrapped, a whole wrapped line where those 7px were what tipped it over. The
105
+ chip now holds its box from first paint and is hidden with `visibility`,
106
+ which is what `_setup-wizard.scss` said it did all along ("Kept in the layout
107
+ (no display toggling) so its appearance never shifts the header") and still
108
+ keeps it out of the accessibility tree when it has nothing to say. This is
109
+ what failed the `@critical` stable-height spec on every push to `main`
110
+ (`2029, 2036, 2036, …`), and with it the retry that spec's failure forced
111
+ ([bamr87/bamr87#265](https://github.com/bamr87/bamr87/issues/265)) (evidence:
112
+ [`test/visual/evidence/agent-issue-265/`](test/visual/evidence/agent-issue-265/README.md)
113
+ — first draft save moved the wizard 42px on the base branch, 0px here).
114
+
115
+ ### Changed
116
+
117
+ - The stable-height spec now walks Connect **first and last** and records
118
+ `#wizardTabContent`'s document-y **top** beside each Back/Next offset. The
119
+ contract itself (`spread ≤ 1`) is unchanged; what is new is that a shift
120
+ *above* the panes now fails as a shift above the panes instead of looking
121
+ like one step with bad CSS. The companion `heights` assertion keeps a comment
122
+ saying it is step-invariant by construction, so it is never again read as
123
+ evidence about anything above the container.
124
+ - **Exactly one `main` landmark again on post, notebook and note pages.**
125
+ `_layouts/{article,notebook,note}.html` each rendered
126
+ `<article id="main" role="main">` *inside* root.html's
127
+ `<main id="main-content">`, so every such page exposed two `main` landmarks —
128
+ ambiguous "skip to main content" and landmark-rotor navigation in
129
+ NVDA/JAWS/VoiceOver (WCAG 2.1 SC 1.3.1) — and the `role` additionally
130
+ overrode the element's native `article` landmark, so the post was no longer
131
+ announced as an article. This is the same class of defect as #299, arriving on
132
+ a `role` attribute instead of a nested `<main>` element. The generic
133
+ `id="main"` is gone with it: nothing in the theme referenced it, and the skip
134
+ link targets `#main-content`. Microdata (`h-entry`, `itemscope`,
135
+ `BlogPosting`/`TechArticle`/`Article`) is unchanged, and the rendered pixels
136
+ are identical. Pinned by `test/visual/core/landmarks.spec.js`
137
+ ([#484](https://github.com/bamr87/zer0-mistakes/issues/484)) (evidence:
138
+ [`test/visual/evidence/agent-issue-484/`](test/visual/evidence/agent-issue-484/README.md)
139
+ — base vs head on a post and a note page, page overflow 0px → 0px across 6
140
+ widths, 9 pixel baselines unchanged).
141
+
15
142
  ## [1.30.0](https://github.com/bamr87/zer0-mistakes/compare/v1.29.0...v1.30.0) (2026-09-12)
16
143
 
17
144
 
@@ -75,6 +202,50 @@ file. Only `## [Unreleased]` describes work that has not shipped yet.
75
202
 
76
203
  ### Fixed
77
204
 
205
+ - **Two `test_core.sh` checks could never fail, and one shouted on every clean
206
+ run (#460).** The Liquid nested-tag check grepped for
207
+ {% raw %}`{{.*{{`{% endraw %}, which matches
208
+ any two *sibling* output tags on one line — it flagged 64 of the theme's
209
+ includes, starting with `navigation/sidebar-pagetree.html`'s valid
210
+ {% raw %}`{{ _base }}{{ _section }}`{% endraw %} — and its `return 1` sat inside a `find | while`
211
+ pipeline, i.e. a subshell, so the function printed `[ERROR]` and then reported
212
+ the check as passed. The gem-content check ran `tar -tzf` on the built `.gem`,
213
+ but a gem is an *uncompressed* tar wrapping `metadata.gz`, `data.tar.gz` and
214
+ `checksums.yaml.gz`, so gzip printed `stdin: not in gzip format` four times
215
+ and both branches fell through to "Gem may not contain…" without ever
216
+ inspecting the payload — a gem shipped with no `_layouts/` would have passed.
217
+ The nested-tag pattern is now {% raw %}`{{[^}]*{{`{% endraw %} (cannot cross the first `}`, so
218
+ siblings do not match), both loops read from a process substitution so a
219
+ failure propagates, and the gem check extracts `data.tar.gz` and asserts
220
+ `_layouts/` and `assets/` are really in it. The layout balance check, now
221
+ live, blanks Liquid comment bodies before counting, so prose in a comment that
222
+ quotes a closing delimiter (as `_layouts/root.html`'s per-page-scripts note
223
+ does) is no longer read as an unbalanced tag. New `test/test_core_checks.sh`
224
+ drives both checks against known-good and known-bad fixtures, so a check that
225
+ stops being able to fail is itself a test failure. `./test/test_core.sh` now
226
+ runs clean: 26/26, no `[ERROR]` line, no gzip/tar noise.
227
+ - **Every navbar link emitted invalid HTML — attributes glued together with no
228
+ separating whitespace (#465)** — the Liquid whitespace-trim markers around the
229
+ conditional `aria-current` in `_includes/navigation/navbar.html` ate the
230
+ newlines that separated the surrounding attributes, so the primary nav
231
+ rendered `aria-label="News"aria-current="page"title="News"` (the WHATWG
232
+ `missing-whitespace-between-attributes` parse error). The leading {% raw %}`{%-`{% endraw %}
233
+ stripped the newline after `aria-label` and the trailing {% raw %}`-%}`{% endraw %} the whitespace
234
+ before `title`, which means **two of the four sites were broken on every page,
235
+ not just the current one**: where the conditional sits between two
236
+ unconditional attributes both markers fire even when the `if` emits nothing.
237
+ Parsers recover, so nothing looked broken — but the attributes affected are
238
+ exactly `aria-label`, `aria-current` and `title`, and a stricter parser is
239
+ entitled to drop the "you are here" announcement for screen-reader users. All
240
+ four sites now use the non-trimming {% raw %}`{% if %}`{% endraw %} form already present at line 26
241
+ of the same file, with the separator outside the tag. Two guards in
242
+ `test/test_core.sh`: `test_navbar_attribute_whitespace` renders the real
243
+ include through Liquid across both `aria-current` branches and both nav modes
244
+ (6 of 8 rendered variants were invalid before the fix), and
245
+ `test_attribute_whitespace_in_markup` models the trim rules over every include
246
+ and layout with no gem dependency — it found a second live instance, a literal
247
+ missing space in `components/theme-preview-gallery.html`, fixed here too.
248
+ `test_jekyll_build` re-checks the delivered `_site` bytes. No visual change.
78
249
  - **The weekly UI/UX audit was blind, and reported it as clean.** `sweep.mjs`
79
250
  built its pages with `browser.newPage()`, which `@axe-core/playwright`
80
251
  refuses; the throw was caught by a single per-route `try` that also discarded
data/_data/backlog.yml CHANGED
@@ -866,7 +866,7 @@ tasks:
866
866
 
867
867
  - id: T-033
868
868
  title: "Retire legacy test_installation.sh after equivalence audit against the installer harness"
869
- status: open
869
+ status: done
870
870
  priority: P2
871
871
  area: tests
872
872
  risk: standard
@@ -880,6 +880,10 @@ tasks:
880
880
  uncovered into test_install_*.sh, then delete the legacy suite and remove the
881
881
  `installation` routing from test_runner.sh, ci.yml, and nightly-extended.yml.
882
882
  Until then it runs nightly and on install-path PRs only (2026-07 CI tiering).
883
+ Done 2026-10-02: the suite is deleted, its scenarios live in
884
+ test_install_legacy_flags.sh (marked "Ported from retired
885
+ test_installation.sh"), test_runner.sh routes no `installation` suite, and
886
+ the CI/nightly suite lists no longer name it.
883
887
  acceptance:
884
888
  - "Every test_installation.sh scenario is mapped to a harness equivalent or ported."
885
889
  - "test_installation.sh deleted; test_runner.sh no longer routes an `installation` suite."
@@ -887,7 +891,7 @@ tasks:
887
891
  - "Nightly extended run green after removal."
888
892
  links: { issue: null, pr: null, roadmap: null }
889
893
  created: 2026-07-06
890
- updated: 2026-07-06
894
+ updated: 2026-10-02
891
895
  - id: T-034
892
896
  title: "Remove the dead preview_image_generator.rb Jekyll plugin and its test surface"
893
897
  status: done
@@ -999,7 +1003,7 @@ tasks:
999
1003
 
1000
1004
  - id: T-037
1001
1005
  title: "Navbar width recovery: two clean label tiers, merged chevron, centered grid, no subtitle"
1002
- status: open
1006
+ status: done
1003
1007
  priority: P2
1004
1008
  area: feat
1005
1009
  risk: standard
@@ -1018,13 +1022,32 @@ tasks:
1018
1022
  the navbar subtitle (_includes/core/branding.html — home hero only).
1019
1023
  Clarify the three toggles: labeled "Menu" text button below lg, gear moves
1020
1024
  into the unified drawer on mobile so only two toggles coexist.
1025
+ Done 2026-09-23: collapsed the three container-query tiers to two (icon+label
1026
+ >=51rem, icon-only below — the old 41-50.99rem bare-label tier and its
1027
+ text-overflow: ellipsis safety net are gone), merged the dropdown chevron
1028
+ into the parent link's hover row, gave the desktop grid's side tracks a
1029
+ shared floor (--zer0-navbar-side-min), moved site.subtitle to the home hero,
1030
+ and labeled the mobile menu toggle "Menu" (with aria-label mirroring the
1031
+ visible text for WCAG 2.5.3). `grid-template-columns: 1fr auto 1fr` as
1032
+ literally proposed breaks the container queries (track 2 must stay a
1033
+ definite 1fr for `.bd-navbar-nav-viewport`'s `width: 100%` to resolve) —
1034
+ used a shared side-track minimum instead; documented in-source. A prior
1035
+ attempt (#454) reached the same design but was closed as stale — its author
1036
+ had no Docker/Jekyll to run the Playwright suite or regenerate evidence.
1037
+ This finishes it against a real local build (bundle + jekyll build +
1038
+ Playwright), and additionally caught and fixed a collision the earlier
1039
+ attempt's tooling couldn't check: the new visible "Menu" text reused the
1040
+ already-defined `ui.menu_label` ("Toggle menu"), which would have shipped
1041
+ wrong text — added a dedicated `ui.nav_menu_toggle_label` key instead.
1042
+ 49/49 Playwright checks pass (37 existing navbar.spec.js + 12 new
1043
+ navbar-tiers.spec.js).
1021
1044
  acceptance:
1022
1045
  - "At 1024px and 1280px with 5 nav items every label is fully visible or the whole row is icon-only — no ellipses anywhere."
1023
1046
  - "Dropdown chevron shares the parent row's hover zone; no dead zone between link and chevron."
1024
1047
  - "Navbar carries logo + title only; subtitle appears on the home hero."
1025
1048
  - "regression test added"
1026
1049
  - "before/after evidence committed"
1027
- links: { issue: null, pr: null, roadmap: null }
1050
+ links: { issue: 405, pr: 495, roadmap: null }
1028
1051
  created: 2026-08-18
1029
1052
  updated: 2026-08-18
1030
1053
 
@@ -1354,13 +1377,13 @@ tasks:
1354
1377
 
1355
1378
  - id: T-048
1356
1379
  title: "Site-level theme_color re-pins --zer0-color-primary over the active skin"
1357
- status: open
1380
+ status: done
1358
1381
  priority: P2
1359
1382
  area: feat
1360
1383
  risk: low
1361
1384
  effort: S
1362
1385
  source: issue
1363
- summary: "Discovered while making Mermaid diagrams follow the skin (ZER0-013 rework): the skin mixin (_sass/theme/_skins.scss) writes both --bs-primary and --zer0-color-primary under [data-theme-skin], but _includes/core/tokens-inline.html emits `:root { --zer0-color-primary: <theme_color.main> }` AFTER main.css, and :root ties [data-theme-skin] on specificity, so the later inline rule wins. Result on this site: buttons/links follow the skin (#6fa8dc on air, #8338ec on neon) while every custom component that reads --zer0-color-primary (callouts, code-block accent border, focus rings) stays on the config #007bff regardless of skin. Mermaid works around it by reading --bs-primary first; the token layer should agree with itself instead."
1386
+ summary: "Done 2026-10-02 (#459): the skin wins. tokens-inline.html emits theme_color main/blue/purple only when no palette skin is active, the precedence is documented in docs/ui/design-tokens.md, and appearance.spec.js asserts it. Discovered while making Mermaid diagrams follow the skin (ZER0-013 rework): the skin mixin (_sass/theme/_skins.scss) writes both --bs-primary and --zer0-color-primary under [data-theme-skin], but _includes/core/tokens-inline.html emits `:root { --zer0-color-primary: <theme_color.main> }` AFTER main.css, and :root ties [data-theme-skin] on specificity, so the later inline rule wins. Result on this site: buttons/links follow the skin (#6fa8dc on air, #8338ec on neon) while every custom component that reads --zer0-color-primary (callouts, code-block accent border, focus rings) stays on the config #007bff regardless of skin. Mermaid works around it by reading --bs-primary first; the token layer should agree with itself instead."
1364
1387
  acceptance:
1365
1388
  - "With theme_color.main set, switching skin changes --zer0-color-primary as well as --bs-primary (probe getComputedStyle on <html>)."
1366
1389
  - "Decide and document the precedence: skin over site theme_color (recommended) or the reverse — one rule, applied to every --zer0-color-* the mixin sets."
@@ -1368,7 +1391,7 @@ tasks:
1368
1391
  - "before/after evidence committed"
1369
1392
  links: { issue: null, pr: null, roadmap: null }
1370
1393
  created: 2026-09-05
1371
- updated: 2026-09-05
1394
+ updated: 2026-10-02
1372
1395
 
1373
1396
  - id: T-049
1374
1397
  title: "test_core.sh: Liquid nested-tag check and gem-content check can never fail"
data/_data/consumers.yml CHANGED
@@ -47,6 +47,25 @@
47
47
  # gem_unconstrained `gem 'jekyll-theme-zer0'` with no version — resolves
48
48
  # whatever RubyGems has at `bundle update` time; tracked
49
49
  # only (like resolved_lockfile), never reports drift
50
+ #
51
+ # ------------------------------------------------------------------------------
52
+ # Stack fields (optional; informational — scripts/bin/propagate ignores them)
53
+ # ------------------------------------------------------------------------------
54
+ # How far each consumer is aligned with the zer0 stack (one theme, one image
55
+ # engine, one CMS — templates/consumer/README.md § The zer0 stack). A dated
56
+ # snapshot of each consumer's default branch, not a live reading: refresh it
57
+ # from `zer0-cms doctor <checkout>` (bamr87/zer0-CMS), never from memory.
58
+ #
59
+ # stack:
60
+ # checked date the default branch was read
61
+ # image_gem Gemfile requires zer0-image-generator
62
+ # preview_images _config.yml has a preview_images: block
63
+ # vendored_generator copies of the image engine the gem replaces
64
+ # (_plugins/preview_*.rb forks, scripts/lib/
65
+ # preview_generator.py, scripts/generate-preview-images.sh);
66
+ # a deliberate local generator is NOT listed
67
+ # zer0_json a zer0.json (zer0-CMS project file) is committed
68
+ # doctor .github/workflows/zer0-doctor.yml caller is committed
50
69
  # ==============================================================================
51
70
 
52
71
  version: 1
@@ -77,6 +96,16 @@ consumers:
77
96
  hook, enabled by `user_overrides: true`, not a shadow — and no fix
78
97
  numbered #338 could be substantiated in that repo. Keep this field
79
98
  derived from audit-consumer output, not hand-maintained estimates.
99
+ stack:
100
+ checked: "2026-09-14"
101
+ image_gem: true
102
+ preview_images: true
103
+ vendored_generator:
104
+ - _plugins/preview_image_generator.rb
105
+ - _plugins/preview_generator.rb
106
+ - scripts/lib/preview_generator.py
107
+ zer0_json: false
108
+ doctor: false
80
109
  pins:
81
110
  - file: _config.yml
82
111
  kind: remote_theme
@@ -90,6 +119,14 @@ consumers:
90
119
  risk: >-
91
120
  Unpinned, same exposure as it-journey. Small override surface (2 includes).
92
121
  Recommend pinning and joining the fan-out.
122
+ stack:
123
+ checked: "2026-09-14"
124
+ image_gem: true
125
+ preview_images: true
126
+ vendored_generator:
127
+ - scripts/generate-preview-images.sh
128
+ zer0_json: false
129
+ doctor: false
93
130
  pins:
94
131
  - file: _config.yml
95
132
  kind: remote_theme
@@ -106,6 +143,15 @@ consumers:
106
143
  Gemfile.azure + _config.azure.yml, and local dev mounts a path gem. A bump
107
144
  must move the remote_theme tag and the gem constraint together or the two
108
145
  production stacks render different theme versions.
146
+ stack:
147
+ checked: "2026-09-14"
148
+ image_gem: false
149
+ preview_images: true
150
+ vendored_generator:
151
+ - _plugins/preview_image_generator.rb
152
+ - scripts/generate-preview-images.sh
153
+ zer0_json: true
154
+ doctor: false
109
155
  pins:
110
156
  - file: _config.yml
111
157
  kind: remote_theme
@@ -139,6 +185,15 @@ consumers:
139
185
  render different theme versions with nothing here to notice. Recommend
140
186
  pinning remote_theme and constraining Gemfile.azure together and joining
141
187
  the fan-out.
188
+ stack:
189
+ checked: "2026-09-14"
190
+ image_gem: false
191
+ preview_images: true
192
+ vendored_generator:
193
+ - _plugins/preview_image_generator.rb
194
+ - scripts/generate-preview-images.sh
195
+ zer0_json: true
196
+ doctor: false
142
197
  pins:
143
198
  - file: _config.yml
144
199
  kind: remote_theme
@@ -165,6 +220,14 @@ consumers:
165
220
  so one bump here re-rolls the whole org (12 sites). The hub's own
166
221
  _config.yml and hub.yml must move together — they are read by different
167
222
  code paths and drift silently otherwise.
223
+ stack:
224
+ checked: "2026-09-14"
225
+ image_gem: true
226
+ preview_images: true
227
+ vendored_generator:
228
+ - scripts/generate-preview-images.sh
229
+ zer0_json: false
230
+ doctor: false
168
231
  pins:
169
232
  - file: _config.yml
170
233
  kind: remote_theme
@@ -189,6 +252,14 @@ consumers:
189
252
  Unpinned, and an org hub: a theme break here reaches every auto-discovered
190
253
  member site at once. Recommend pinning both files together and joining
191
254
  the fan-out (dispatch: true) like year-of-ai.
255
+ stack:
256
+ checked: "2026-09-14"
257
+ image_gem: true
258
+ preview_images: true
259
+ vendored_generator:
260
+ - scripts/generate-preview-images.sh
261
+ zer0_json: false
262
+ doctor: false
192
263
  pins:
193
264
  - file: _config.yml
194
265
  kind: remote_theme
@@ -220,6 +291,13 @@ consumers:
220
291
  Gemfile.lock resolved 1.25.0 while the Gemfile constraint (~> 1.25)
221
292
  already permitted 1.28.0 — stale purely because nothing was nudging it.
222
293
  Keeps two deliberate include forks, declared in its .theme-overrides.yml.
294
+ stack:
295
+ checked: "2026-09-14"
296
+ image_gem: true
297
+ preview_images: true
298
+ vendored_generator: []
299
+ zer0_json: false
300
+ doctor: false
223
301
  pins:
224
302
  - file: Gemfile
225
303
  kind: gem_constraint
@@ -246,6 +324,13 @@ consumers:
246
324
  Unpinned, and with no local preview path a contributor cannot see a theme
247
325
  break before pushing. Keeps three deliberate overrides, declared in its
248
326
  .theme-overrides.yml.
327
+ stack:
328
+ checked: "2026-09-14"
329
+ image_gem: false
330
+ preview_images: false
331
+ vendored_generator: []
332
+ zer0_json: false
333
+ doctor: false
249
334
  pins:
250
335
  - file: _config.yml
251
336
  kind: remote_theme
@@ -264,6 +349,13 @@ consumers:
264
349
  risk: >-
265
350
  Unpinned, and its only build runs on push to main (jekyll-gh-pages.yml),
266
351
  so a break ships before anyone sees it.
352
+ stack:
353
+ checked: "2026-09-14"
354
+ image_gem: false
355
+ preview_images: false
356
+ vendored_generator: []
357
+ zer0_json: false
358
+ doctor: false
267
359
  pins:
268
360
  - file: _config.yml
269
361
  kind: remote_theme
@@ -289,6 +381,13 @@ consumers:
289
381
  "unreachable" rather than reading its pin — drift reporting needs a token
290
382
  here even though it is read-only. CI already supplies one; a bare local
291
383
  run will not.
384
+ stack:
385
+ checked: "2026-09-14"
386
+ image_gem: false
387
+ preview_images: false
388
+ vendored_generator: []
389
+ zer0_json: false
390
+ doctor: false
292
391
  pins:
293
392
  - file: _config.yml
294
393
  kind: remote_theme
data/_data/features.yml CHANGED
@@ -2196,7 +2196,7 @@ features:
2196
2196
  docs: "docs/systems/multilingual-translation.md"
2197
2197
  features:
2198
2198
  - "One large full-translation job (workflow_dispatch) plus bounded incremental runs when English content changes"
2199
- - "Manifest-driven SHA change detection, pruning of deleted sources, and generated /fr<en-url> permalinks (GitHub Pages safe-mode friendly)"
2199
+ - "Manifest-driven SHA change detection, pruning of deleted sources, and generated /fr/... permalinks mirroring the English URLs (GitHub Pages safe-mode friendly)"
2200
2200
  - "Markdown-safe segment translation: code, Liquid, wiki-links and URLs masked as placeholders and validated"
2201
2201
  - "Navbar language toggle with availability states, hreflang alternates, per-page html lang, machine-translation notice"
2202
2202