jekyll-theme-zer0 1.27.0 → 1.29.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +812 -13
  3. data/_data/backlog.yml +356 -2
  4. data/_data/consumers.yml +252 -0
  5. data/_data/features.yml +234 -5
  6. data/_data/i18n/fr.yml +103 -0
  7. data/_data/i18n/manifest.yml +1529 -0
  8. data/_data/ingredient_densities.yml +122 -0
  9. data/_data/navigation/docs.yml +2 -0
  10. data/_data/navigation/main.yml +22 -0
  11. data/_data/recipe_courses.yml +64 -0
  12. data/_data/series.yml +19 -0
  13. data/_data/theme-manifest.yml +387 -277
  14. data/_data/ui-text.yml +8 -0
  15. data/_includes/README.md +36 -3
  16. data/_includes/analytics/google-tag-manager-body.html +11 -2
  17. data/_includes/analytics/google-tag-manager-head.html +16 -6
  18. data/_includes/analytics/posthog.html +33 -4
  19. data/_includes/components/abc-letter.html +43 -0
  20. data/_includes/components/background-customizer.html +2 -2
  21. data/_includes/components/book-card.html +42 -0
  22. data/_includes/components/book-nav.html +80 -0
  23. data/_includes/components/book-plate.html +31 -0
  24. data/_includes/components/book-toc.html +47 -0
  25. data/_includes/components/bookshelf.html +68 -0
  26. data/_includes/components/card-grid.html +60 -0
  27. data/_includes/components/cookie-consent.html +4 -4
  28. data/_includes/components/data-card.html +95 -0
  29. data/_includes/components/halfmoon.html +5 -1
  30. data/_includes/components/info-section.html +8 -3
  31. data/_includes/components/language-toggle.html +168 -21
  32. data/_includes/components/page-feedback.html +65 -2
  33. data/_includes/components/page-views-init.html +55 -0
  34. data/_includes/components/page-views.html +33 -0
  35. data/_includes/components/recipe-card.html +67 -0
  36. data/_includes/components/recipe-duration.html +50 -0
  37. data/_includes/components/recipe-grams.html +58 -0
  38. data/_includes/components/recipe-index.html +96 -0
  39. data/_includes/components/recipe-ingredients.html +90 -0
  40. data/_includes/components/recipe-meta.html +96 -0
  41. data/_includes/components/recipe-nutrition.html +57 -0
  42. data/_includes/components/recipe-qty.html +73 -0
  43. data/_includes/components/recipe-ratio.html +151 -0
  44. data/_includes/components/recipe-scaler.html +73 -0
  45. data/_includes/components/recipe-steps.html +86 -0
  46. data/_includes/components/recipe-temp.html +45 -0
  47. data/_includes/components/search-modal.html +2 -2
  48. data/_includes/components/shortcuts-modal.html +3 -0
  49. data/_includes/components/theme-controls-bar.html +10 -2
  50. data/_includes/components/theme-customizer.html +8 -2
  51. data/_includes/components/theme-info.html +8 -1
  52. data/_includes/content/jsonld-software.html +22 -3
  53. data/_includes/content/seo.html +9 -3
  54. data/_includes/core/color-mode-init.html +13 -4
  55. data/_includes/core/favicon.html +46 -0
  56. data/_includes/core/footer.html +18 -16
  57. data/_includes/core/head.html +21 -0
  58. data/_includes/core/header.html +7 -4
  59. data/_includes/custom/body-end.html +18 -0
  60. data/_includes/custom/body-start.html +17 -0
  61. data/_includes/custom/footer.html +18 -0
  62. data/_includes/custom/head.html +18 -0
  63. data/_includes/navigation/local-graph.html +28 -2
  64. data/_includes/navigation/nav-tree.html +3 -3
  65. data/_includes/navigation/section-sidebar.html +93 -11
  66. data/_includes/navigation/sidebar-config.html +21 -0
  67. data/_includes/navigation/sidebar-left.html +2 -1
  68. data/_includes/navigation/sidebar-nav.html +4 -0
  69. data/_includes/navigation/sidebar-pagetree.html +150 -0
  70. data/_includes/navigation/sidebar-right.html +2 -1
  71. data/_includes/navigation/unified-drawer.html +8 -2
  72. data/_includes/obsidian/full-graph.html +165 -136
  73. data/_includes/setup/wizard.html +173 -86
  74. data/_includes/stats/stats-metrics.html +2 -2
  75. data/_layouts/404.html +260 -0
  76. data/_layouts/README.md +4 -0
  77. data/_layouts/admin.html +2 -2
  78. data/_layouts/article.html +5 -0
  79. data/_layouts/book-abc.html +106 -0
  80. data/_layouts/book-story.html +91 -0
  81. data/_layouts/book.html +113 -0
  82. data/_layouts/collection.html +17 -5
  83. data/_layouts/cookbook.html +88 -0
  84. data/_layouts/default.html +4 -4
  85. data/_layouts/home.html +5 -2
  86. data/_layouts/landing.html +9 -0
  87. data/_layouts/news.html +155 -31
  88. data/_layouts/recipe.html +274 -0
  89. data/_layouts/root.html +38 -5
  90. data/_layouts/section.html +96 -25
  91. data/_sass/components/_book.scss +423 -0
  92. data/_sass/components/_callout.scss +1 -1
  93. data/_sass/components/_footer.scss +37 -1
  94. data/_sass/components/_page-views.scss +36 -0
  95. data/_sass/components/_recipe.scss +506 -0
  96. data/_sass/components/_setup-wizard.scss +235 -0
  97. data/_sass/components/_ui-enhancements.scss +6 -6
  98. data/_sass/core/_navbar.scss +263 -15
  99. data/_sass/core/_obsidian.scss +286 -6
  100. data/_sass/layouts/_landing.scss +2 -2
  101. data/_sass/theme/_backgrounds.scss +21 -8
  102. data/_sass/tokens/_color.scss +6 -0
  103. data/_sass/tokens/_index.scss +2 -0
  104. data/_sass/tokens/_radius.scss +21 -0
  105. data/_sass/tokens/_typography.scss +4 -0
  106. data/_sass/utilities/_focus.scss +14 -0
  107. data/assets/css/main.scss +4 -0
  108. data/assets/js/auto-hide-nav.js +5 -1
  109. data/assets/js/halfmoon.js +26 -0
  110. data/assets/js/modules/navigation/navbar.js +55 -0
  111. data/assets/js/obsidian-graph.js +702 -264
  112. data/assets/js/obsidian-local-graph.js +161 -54
  113. data/assets/js/page-views.js +372 -0
  114. data/assets/js/recipe-scaler.js +501 -0
  115. data/assets/js/search-modal.js +14 -1
  116. data/assets/js/setup-wizard.js +322 -20
  117. data/scripts/README.md +29 -0
  118. data/scripts/bin/audit-consumer +39 -7
  119. data/scripts/bin/giscus-discussions +213 -14
  120. data/scripts/bin/manifest +66 -16
  121. data/scripts/bin/validate +5 -1
  122. data/scripts/ci/agent_review_result.py +164 -0
  123. data/scripts/ci/test_agent_review_result.py +172 -0
  124. data/scripts/design-system-check.rb +170 -0
  125. data/scripts/install/README.md +47 -6
  126. data/scripts/install/ai/client.sh +302 -93
  127. data/scripts/install/ai/prompts/spec.schema.json +1 -1
  128. data/scripts/install/ai/wizard.sh +10 -5
  129. data/scripts/install/apply.sh +7 -3
  130. data/scripts/install/cli.sh +54 -4
  131. data/scripts/install/config.sh +167 -0
  132. data/scripts/install/doctor.sh +38 -0
  133. data/scripts/install/plan.sh +10 -0
  134. data/scripts/install/spec.sh +15 -7
  135. data/scripts/install/template.sh +4 -0
  136. data/scripts/lib/audit.sh +42 -2
  137. data/scripts/lint-liquid-raw.rb +137 -0
  138. data/scripts/propagate.rb +277 -0
  139. data/scripts/test/lib/run_tests.sh +2 -1
  140. data/scripts/test/lib/test_agent_review_result.sh +27 -0
  141. data/scripts/translate.rb +161 -15
  142. metadata +56 -2
data/_data/backlog.yml CHANGED
@@ -62,8 +62,8 @@
62
62
 
63
63
  meta:
64
64
  title: "zer0-mistakes Backlog"
65
- updated: 2026-06-29
66
- next_id: 34
65
+ updated: 2026-08-18
66
+ next_id: 46
67
67
 
68
68
  tasks:
69
69
  # --- Housekeeping (seeded so the loop has work on day one) ------------------
@@ -916,3 +916,357 @@ tasks:
916
916
  links: { issue: 302, pr: null, roadmap: null }
917
917
  created: 2026-07-12
918
918
  updated: 2026-07-16
919
+
920
+ - id: T-035
921
+ title: "Adopt --zer0-radius-* and display-size tokens across the remaining Sass partials"
922
+ status: open
923
+ priority: P2
924
+ area: feat
925
+ risk: low
926
+ effort: M
927
+ source: issue
928
+ route: theme-ui
929
+ summary: >-
930
+ The design-system alignment PR #389 (ZER0-081) introduced --zer0-radius-* and
931
+ --zer0-text-display-* tokens and adopted them in the partials the Claude
932
+ Design system names (callout, landing, ui-enhancements). Hardcoded radii
933
+ remain in _navbar.scss (~15 sites), notebooks.scss, code-copy.scss,
934
+ _docs-code-examples.scss, and others; hero .display-4 mobile overrides in
935
+ _ui-enhancements.scss (2rem and 1.75rem blocks — the two @media blocks
936
+ overlap, later wins) predate the fluid clamp tokens. Sweep them onto the
937
+ tokens value-preservingly where values match the scale, and reconcile the
938
+ duplicate hero override blocks; anything intentionally off-scale keeps a
939
+ literal plus a comment.
940
+ acceptance:
941
+ - "No border-radius literal in _sass/** that duplicates a --zer0-radius-* value (grep-clean or commented as intentional)."
942
+ - "Hero display sizing states one rule set; duplicate @media blocks reconciled."
943
+ - "scripts/design-system-check.rb still green; compiled CSS visually identical (evidence or skip-evidence rationale)."
944
+ - "regression test added"
945
+ - "before/after evidence committed"
946
+ links: { issue: null, pr: null, roadmap: null }
947
+ created: 2026-08-14
948
+ updated: 2026-08-14
949
+
950
+ # --- UI/UX fix list intake (hand-written review, 2026-08-18) ----------------
951
+
952
+ - id: T-036
953
+ title: "Remove hover transforms that cause pointer-edge flicker in the navbar"
954
+ status: open
955
+ priority: P1
956
+ area: feat
957
+ risk: standard
958
+ effort: S
959
+ source: user
960
+ route: theme-ui
961
+ summary: >-
962
+ .nav-link:hover, .nav-search-button:hover and .nav-settings-button:hover in
963
+ _sass/core/_navbar.scss apply transform: translateY(-1px) (icons scale 1.1,
964
+ chevron :hover::after translateY(2px)), so the element moves out from under
965
+ a pointer parked on its edge, hover exits, it returns — infinite flicker.
966
+ Delete every transform from :hover rules (keep background/color
967
+ transitions; a pressed cue may use :active translateY(1px) only) and audit
968
+ _sass/components/_ui-enhancements.scss and card hover styles for the same
969
+ pattern.
970
+ acceptance:
971
+ - "No transform in any :hover rule under _sass/ (grep for translateY/scale in hover context returns nothing)."
972
+ - "Pointer parked on the top edge of any nav item or utility button does not flicker."
973
+ - "regression test added"
974
+ - "before/after evidence committed"
975
+ links: { issue: null, pr: null, roadmap: null }
976
+ created: 2026-08-18
977
+ updated: 2026-08-18
978
+
979
+ - id: T-037
980
+ title: "Navbar width recovery: two clean label tiers, merged chevron, centered grid, no subtitle"
981
+ status: open
982
+ priority: P2
983
+ area: feat
984
+ risk: standard
985
+ effort: M
986
+ source: user
987
+ route: theme-ui
988
+ summary: >-
989
+ Top nav is cramped and truncates labels ("Quicksta…"). Keep per-item icons
990
+ at every tier but only two states — icon + full label (wide container) and
991
+ the existing icon-only tier (narrow) — removing the intermediate ellipsized
992
+ state (_includes/navigation/navbar.html + _sass/core/_navbar.scss container
993
+ queries). Merge each dropdown's separate 22-24px chevron button into the
994
+ parent link's padding box (position:absolute overlay, shared hover
995
+ background on the li, button kept for a11y). Use grid-template-columns:
996
+ 1fr auto 1fr on .navbar-main so the menu stays optically centered. Retire
997
+ the navbar subtitle (_includes/core/branding.html — home hero only).
998
+ Clarify the three toggles: labeled "Menu" text button below lg, gear moves
999
+ into the unified drawer on mobile so only two toggles coexist.
1000
+ acceptance:
1001
+ - "At 1024px and 1280px with 5 nav items every label is fully visible or the whole row is icon-only — no ellipses anywhere."
1002
+ - "Dropdown chevron shares the parent row's hover zone; no dead zone between link and chevron."
1003
+ - "Navbar carries logo + title only; subtitle appears on the home hero."
1004
+ - "regression test added"
1005
+ - "before/after evidence committed"
1006
+ links: { issue: null, pr: null, roadmap: null }
1007
+ created: 2026-08-18
1008
+ updated: 2026-08-18
1009
+
1010
+ - id: T-038
1011
+ title: "Language switcher: icon-only trigger and a compact, positive menu"
1012
+ status: open
1013
+ priority: P2
1014
+ area: feat
1015
+ risk: standard
1016
+ effort: S
1017
+ source: user
1018
+ route: theme-ui
1019
+ summary: >-
1020
+ _includes/components/language-toggle.html: drop the EN text span and the
1021
+ Bootstrap caret (38-40px square icon button; title + aria-label carry the
1022
+ name). In the menu, mark the current language with a check icon + tint
1023
+ (not .active primary fill); keep untranslated languages clickable
1024
+ (navigate to source page, set zer0-lang); mark machine translations with a
1025
+ small "auto" chip; replace per-row "(Not yet translated)" with one
1026
+ footnote line under a divider; menu width <= 220px.
1027
+ acceptance:
1028
+ - "Menu shows no disabled rows; widest row <= 220px; current language visually distinct without primary fill."
1029
+ - "Trigger is icon-only at all widths with an accessible name."
1030
+ - "regression test added"
1031
+ - "before/after evidence committed"
1032
+ links: { issue: null, pr: null, roadmap: null }
1033
+ created: 2026-08-18
1034
+ updated: 2026-08-18
1035
+
1036
+ - id: T-039
1037
+ title: "Eliminate page-transition layout shift (fixed-nav offset, scrollbar gutter, server-rendered slots)"
1038
+ status: open
1039
+ priority: P1
1040
+ area: perf
1041
+ risk: standard
1042
+ effort: M
1043
+ source: user
1044
+ route: theme-ui
1045
+ summary: >-
1046
+ Navigation between pages visibly bounces. Define --zer0-nav-height in
1047
+ :root, derive body padding-top and every sticky offset (admin.html's
1048
+ top: 5rem, TOC, sidebars) and scroll-margin-top from it (no JS
1049
+ measurement). Add html { scrollbar-gutter: stable }. Render banner/nanobar
1050
+ slots server-side with fixed height (nanobar 3px strip always present;
1051
+ setup banner via Liquid when site_needs_setup, not JS insert). Verify
1052
+ explicit width/height on content images and font-display: swap + preload
1053
+ for bootstrap-icons woff2. Progressive enhancement: @view-transition
1054
+ { navigation: auto } with a 120ms root fade.
1055
+ acceptance:
1056
+ - "Lighthouse CLS < 0.02 on landing, docs, and admin pages."
1057
+ - "No visible reflow when navigating between short and long pages (scrollbar gutter stable)."
1058
+ - "All sticky offsets derive from --zer0-nav-height (no hardcoded top: 5rem)."
1059
+ - "regression test added"
1060
+ - "before/after evidence committed"
1061
+ links: { issue: null, pr: null, roadmap: null }
1062
+ created: 2026-08-18
1063
+ updated: 2026-08-18
1064
+
1065
+ - id: T-040
1066
+ title: "Setup wizard: live YAML preview, vertical stepper, draft persistence, stable panel height, inline validation"
1067
+ status: open
1068
+ priority: P2
1069
+ area: feat
1070
+ risk: standard
1071
+ effort: M
1072
+ source: user
1073
+ route: theme-ui
1074
+ summary: >-
1075
+ _includes/setup/wizard.html + assets/js/setup-wizard.js: replace the
1076
+ step-5-only <pre> with a persistent sticky preview panel regenerated on
1077
+ every input (copy/download always enabled); replace nav-pills tabs with a
1078
+ left vertical stepper showing done/active/upcoming state (steps unlock on
1079
+ validation, Back always allowed, remove the "N% complete" math); persist
1080
+ the draft to localStorage under zer0-setup-draft (debounced 300ms, restore
1081
+ on load, "Draft saved" chip, clear on download); give the panel container
1082
+ a min-height equal to the tallest step so Back/Next never move vertically;
1083
+ validate email/URL on blur with is-invalid + message and list unfilled
1084
+ recommended fields as warnings on the Review step.
1085
+ acceptance:
1086
+ - "YAML preview visible and live at lg+; Back/Next buttons never shift vertically between steps."
1087
+ - "Reload mid-wizard restores the draft; invalid email/URL flagged on blur."
1088
+ - "regression test added"
1089
+ - "before/after evidence committed"
1090
+ links: { issue: null, pr: null, roadmap: null }
1091
+ created: 2026-08-18
1092
+ updated: 2026-08-18
1093
+
1094
+ - id: T-041
1095
+ title: "Admin layout: actions reachable on mobile, grouped sidebar, shared sticky offset, single heading"
1096
+ status: open
1097
+ priority: P2
1098
+ area: feat
1099
+ risk: standard
1100
+ effort: M
1101
+ source: user
1102
+ route: theme-ui
1103
+ summary: >-
1104
+ _layouts/admin.html + _includes/navigation/admin-nav.html: the
1105
+ admin_actions wrapper is d-none d-md-flex so actions are unreachable on
1106
+ phones — render the first action as a visible button at all widths with
1107
+ the rest in a "..." dropdown. Group sidebar items under small-caps headers
1108
+ (Configure / System / Links) via a group: key in
1109
+ _data/navigation/admin.yml; counts as plain numerals, statuses as colored
1110
+ dot + word; active item = tint + semibold, not solid primary fill. Replace
1111
+ style="top: 5rem" with top: calc(var(--zer0-nav-height) + 1rem) (depends
1112
+ on T-039). Keep the "Administration" heading in the offcanvas header only.
1113
+ acceptance:
1114
+ - "Every admin action reachable at 375px width."
1115
+ - "Sidebar grouped; no solid-primary active fill; sticky offset uses the shared variable."
1116
+ - "regression test added"
1117
+ - "before/after evidence committed"
1118
+ links: { issue: null, pr: null, roadmap: null }
1119
+ created: 2026-08-18
1120
+ updated: 2026-08-18
1121
+ depends_on: [T-039]
1122
+
1123
+ - id: T-042
1124
+ title: "Skin contrast sweep: AA link params, tertiary meta text, per-skin badge/button audit"
1125
+ status: open
1126
+ priority: P1
1127
+ area: a11y
1128
+ risk: standard
1129
+ effort: M
1130
+ source: user
1131
+ route: a11y
1132
+ summary: >-
1133
+ _sass/theme/_skins.scss: several skins omit the zer0-skin-palette mixin's
1134
+ $link-light/$link-dark params and fall back to $brand/$accent, which fail
1135
+ 4.5:1 (yellow/orange/cyan brands on white, saturated brands on #212529) —
1136
+ pass explicit AA-checked values for every skin with a comment table of
1137
+ measured ratios. text-body-tertiary on 11-12px meta text (admin header
1138
+ "Updated…", wizard hints) falls below AA — use --bs-secondary-color at
1139
+ small sizes, tertiary only >= 16px. Audit .btn-primary text per skin
1140
+ (auto-switch to dark text when brand luminance > 0.55 via color-contrast()
1141
+ or a precomputed token).
1142
+ acceptance:
1143
+ - "axe/Lighthouse contrast passes on light + dark for all skins."
1144
+ - "Every skin passes explicit link colors; measured-ratio table committed as comments."
1145
+ - "regression test added"
1146
+ - "before/after evidence committed"
1147
+ links: { issue: null, pr: null, roadmap: null }
1148
+ created: 2026-08-18
1149
+ updated: 2026-08-18
1150
+
1151
+ - id: T-043
1152
+ title: "Mermaid diagrams: render-in-place (no CLS), token-driven theming, figure shell, flash-free theme switch"
1153
+ status: open
1154
+ priority: P2
1155
+ area: feat
1156
+ risk: standard
1157
+ effort: L
1158
+ source: user
1159
+ route: theme-ui
1160
+ summary: >-
1161
+ _includes/components/mermaid.html: the hidden pre > code is replaced by a
1162
+ min-height:100px div that grows to 300-500px on render, shifting the page —
1163
+ render in place with the placeholder inheriting the code block's height
1164
+ and animate the swap. Delete the ~20 !important SVG overrides that fight
1165
+ JS themeVariables (forced white text breaks colored nodes; light mode uses
1166
+ #007bff vs site primary #0d6efd; skins don't affect diagrams) — build
1167
+ themeVariables at init from live tokens (getComputedStyle on --bs-primary
1168
+ / --bs-body-bg / --bs-border-color / --bs-body-color), re-run on
1169
+ data-bs-theme AND data-theme-skin change. Wrap each diagram in a
1170
+ figure.mermaid-figure with type chip, optional title, copy-source /
1171
+ download-SVG / fullscreen toolbar (reuse the code-copy button pattern) and
1172
+ figcaption. On theme switch render the new SVG off-DOM and cross-fade
1173
+ 120ms (instant under prefers-reduced-motion). Builds on T-039.
1174
+ acceptance:
1175
+ - "CLS contribution from a page with 3 diagrams < 0.01."
1176
+ - "No !important SVG overrides remain; diagrams follow theme AND skin tokens live."
1177
+ - "Theme switch shows no blank flash."
1178
+ - "regression test added"
1179
+ - "before/after evidence committed"
1180
+ links: { issue: null, pr: null, roadmap: null }
1181
+ created: 2026-08-18
1182
+ updated: 2026-08-18
1183
+ depends_on: [T-039]
1184
+
1185
+ - id: T-044
1186
+ title: "Playwright suite aborts: duplicate openDialog declaration in accessibility.spec.js"
1187
+ status: done
1188
+ priority: P1
1189
+ area: tests
1190
+ risk: low
1191
+ effort: S
1192
+ source: issue
1193
+ route: test-author
1194
+ summary: >-
1195
+ test/visual/core/accessibility.spec.js declares `async function
1196
+ openDialog` twice — at line 172 (page, selector) and again at line 425
1197
+ (page, selector, kind), the latter added by 98fd6f9 (#387) without
1198
+ removing the former. Two function declarations of the same name in one
1199
+ module scope is a SyntaxError, so Playwright fails to parse the file and
1200
+ the WHOLE run aborts before any test executes: "Identifier 'openDialog'
1201
+ has already been declared. (425:15)". This takes down the critical and
1202
+ smoke tiers together, on every branch, and has been red on main since
1203
+ 2026-08-14. Discovered during #414, whose own new spec
1204
+ (test/visual/features/recipes.spec.js) therefore cannot run in CI. The
1205
+ three-argument version is a strict superset — with `kind` undefined it
1206
+ resolves to Ctor='Modal' and 'shown.bs.modal', exactly what the
1207
+ two-argument one does — so deleting the earlier declaration (lines
1208
+ 165-184, comment included) is behaviour-preserving.
1209
+ acceptance:
1210
+ - "test/visual/core/accessibility.spec.js parses; `npx playwright test --project=critical` runs to completion."
1211
+ - "Only one openDialog declaration remains, and its callers all still pass."
1212
+ - "No behavioural change to the modal/offcanvas a11y tests."
1213
+ links: { issue: 416, pr: null, roadmap: null }
1214
+ created: 2026-08-23
1215
+ updated: 2026-08-23
1216
+
1217
+ - id: T-045
1218
+ title: "Homepage snapshot baselines were regenerated without the 1967px drift being diagnosed"
1219
+ status: open
1220
+ priority: P2
1221
+ area: tests
1222
+ risk: low
1223
+ effort: S
1224
+ source: issue
1225
+ route: test-author
1226
+ summary: >-
1227
+ The Visual Snapshots tier fails all 9 `Theme skins > homepage visual
1228
+ snapshot` cases with the SAME diff — 1967 pixels (ratio 0.01) — against
1229
+ test/visual/snapshots/features/appearance-snapshot.spec.js-snapshots/homepage-*-snapshots-linux.png,
1230
+ well over the maxDiffPixels 150 budget. The count being identical across
1231
+ air/aqua/contrast/dark/dirt/neon/mint/plum/sunrise points at one shared,
1232
+ skin-independent region rather than a palette regression. Verified during
1233
+ #414 that it is NOT that PR's doing: building origin/main and the PR
1234
+ branch and screenshotting the homepage at the spec's own 1280x720
1235
+ viewport produced BYTE-IDENTICAL PNGs (same md5), so the branch renders
1236
+ the homepage exactly as main does and the drift is between the committed
1237
+ baselines and what main itself now renders. Note while investigating:
1238
+ _includes/components/theme-info.html renders a minute-resolution build
1239
+ stamp, site.time | date "%b %d, %H:%M" — it currently sits in a collapsed
1240
+ offcanvas (measured 0x0, not in the viewport) so it is not the cause
1241
+ today, but it is a live non-determinism hazard the moment that panel is
1242
+ shown by default, and worth masking regardless. UPDATE 2026-08-23: #413
1243
+ regenerated all 9 baselines when it merged, so the check is green again —
1244
+ resolved by re-blessing rather than by diagnosis, which is what the first
1245
+ acceptance criterion was meant to prevent. Diffing the old vs new CI
1246
+ baselines (homepage-air, 2596f866 vs e488c4c) puts the whole 5016px delta
1247
+ in two bands: y21-41 (a wide navbar strip, 4688px) and y626 (328px).
1248
+ page-views does render on the homepage so part of that is legitimately
1249
+ #413, but a navbar strip is not where a view-count badge belongs.
1250
+ Separating the two needs the diff artifact from a pre-#413 failing run,
1251
+ which has rolled off. What remains is confirming the y21-41 band is
1252
+ accounted for. UPDATE 2026-08-25: the y21-41 band is the navbar
1253
+ reflowing, and the reason it RECURS is structural — a path-filter gap.
1254
+ ci.yml's `styling` filter (which gates the snapshots job) lists only code
1255
+ paths; `_data/**` appears solely under `content`, which gates no visual
1256
+ job. So #419's nav item changed the chrome of every page with Visual
1257
+ Snapshots SKIPPED on both the PR and the main push, and the drift
1258
+ surfaced on #420, an unrelated PR. The y626 band is now UNRECOVERABLE:
1259
+ both artifact uploads use 14-day retention (ci.yml, nightly-extended.yml)
1260
+ and every pre-#413 failing run is past that window. Recording that rather
1261
+ than leaving it open — no evidence survives to diagnose it from.
1262
+ acceptance:
1263
+ - "_data/** (or at minimum _data/navigation/**) is covered by the `styling` filter in ci.yml, so a nav/chrome data change runs Visual Snapshots instead of skipping it."
1264
+ - "A CI guard pins that coverage, so the gap cannot silently reopen (lint-workflows.yml)."
1265
+ - "A PR touching only _data/navigation/main.yml runs Visual Snapshots — demonstrated on the checks list, not asserted."
1266
+ - "Visual Snapshots passes on main and on a fresh branch with no source changes."
1267
+ - "Any genuinely time- or build-varying content inside the snapshot region is masked or stubbed, so the tier cannot drift again."
1268
+ - "No baseline PNG is regenerated as part of this work; any diff is diagnosed in the PR before it is blessed."
1269
+ - "The y626 band is explained, or recorded as unrecoverable with the reason. (Recorded: artifacts past 14-day retention.)"
1270
+ links: { issue: 417, pr: null, roadmap: null }
1271
+ created: 2026-08-23
1272
+ updated: 2026-08-25
@@ -0,0 +1,252 @@
1
+ # ==============================================================================
2
+ # _data/consumers.yml — downstream consumers of this theme
3
+ # ==============================================================================
4
+ #
5
+ # The registry that makes theme releases propagate instead of sitting unnoticed.
6
+ # Every site that renders with zer0-mistakes is listed here together with HOW it
7
+ # consumes the theme and WHICH files hold its version pin.
8
+ #
9
+ # Read by:
10
+ # - scripts/bin/propagate report pin drift; dispatch release events
11
+ # - .github/workflows/propagate-theme.yml release fan-out
12
+ #
13
+ # Why this exists: nothing connected a release to the sites built on it. When
14
+ # 1.27.0 shipped, all five consumers were still on v1.26.0 (or floating on main)
15
+ # and none of them knew. Bumps were a manual, five-repo chore that nobody
16
+ # remembered, so pins drifted until a site broke.
17
+ #
18
+ # ------------------------------------------------------------------------------
19
+ # Consumption modes
20
+ # ------------------------------------------------------------------------------
21
+ # remote_theme_pinned `remote_theme: bamr87/zer0-mistakes@vX.Y.Z` — GitHub
22
+ # Pages fetches the tagged theme at build time. Gets a
23
+ # bump PR on release.
24
+ # remote_theme_floating `remote_theme: bamr87/zer0-mistakes` with no tag —
25
+ # tracks main, so every push to this repo reaches
26
+ # production immediately. Nothing to bump; what these
27
+ # need is a canary, not a PR. See `risk` below.
28
+ # gem `gem "jekyll-theme-zer0", "~> X.Y.Z"` — the theme is
29
+ # resolved from RubyGems, not GitHub. Bumping means
30
+ # editing a Gemfile and re-locking. This is the mode the
31
+ # scheduled updater matters most for: no remote_theme
32
+ # means no automatic pickup of anything, ever.
33
+ # path `gem "jekyll-theme-zer0", path: ...` — a local
34
+ # checkout for development. Never version-pinned and
35
+ # never bumped; listed so audits don't flag it.
36
+ #
37
+ # ------------------------------------------------------------------------------
38
+ # Pin kinds (scripts/bin/propagate knows how to read/rewrite each)
39
+ # ------------------------------------------------------------------------------
40
+ # remote_theme `remote_theme: "bamr87/zer0-mistakes@vX.Y.Z"` in a config
41
+ # hub_registry `theme_repo: bamr87/zer0-mistakes@vX.Y.Z` in _data/hub.yml
42
+ # gem_constraint `gem 'jekyll-theme-zer0', '~> X.Y.Z'` in a Gemfile
43
+ # theme_name `theme: "jekyll-theme-zer0"` — names the gem, carries no
44
+ # version; tracked so a bump doesn't miss the config that
45
+ # activates it
46
+ # path_gem local path gem, unversioned (development only)
47
+ # ==============================================================================
48
+
49
+ version: 1
50
+
51
+ defaults:
52
+ branch: main
53
+ # repository_dispatch event type consumers listen for. Their side of the
54
+ # contract lives in templates/consumer/theme-bump.yml.
55
+ dispatch_event: theme-release
56
+
57
+ consumers:
58
+ # ---------------------------------------------------------------------------
59
+ - repo: bamr87/it-journey
60
+ mode: remote_theme_floating
61
+ site: https://it-journey.dev
62
+ dispatch: false
63
+ risk: >-
64
+ Unpinned. Every push to this theme's main reaches production with no gate
65
+ and no rollback — that part is the real exposure. Recommend pinning and
66
+ joining the fan-out. NOTE: this entry previously claimed it "shadows ~55
67
+ theme files … ~75 lines of !important CSS working around #338", which
68
+ audit-consumer does not support. Only 2 files actually shadow a theme
69
+ file (_includes/content/giscus.html, assets/js/particles-source.js); the
70
+ other ~52 are it-journey's own quest-system layouts and includes that
71
+ share no filename with anything in this theme, so they carry no drift
72
+ risk at all. The !important CSS is real but is 31 lines in
73
+ assets/css/user-overrides.css — this theme's own documented extension
74
+ hook, enabled by `user_overrides: true`, not a shadow — and no fix
75
+ numbered #338 could be substantiated in that repo. Keep this field
76
+ derived from audit-consumer output, not hand-maintained estimates.
77
+ pins:
78
+ - file: _config.yml
79
+ kind: remote_theme
80
+ note: line 36, no tag — floats main
81
+
82
+ # ---------------------------------------------------------------------------
83
+ - repo: bamr87/lifehacker.dev
84
+ mode: remote_theme_floating
85
+ site: https://lifehacker.dev
86
+ dispatch: false
87
+ risk: >-
88
+ Unpinned, same exposure as it-journey. Small override surface (2 includes).
89
+ Recommend pinning and joining the fan-out.
90
+ pins:
91
+ - file: _config.yml
92
+ kind: remote_theme
93
+ note: line 40, no tag — floats main
94
+
95
+ # ---------------------------------------------------------------------------
96
+ - repo: bamr87/bashconsultants
97
+ mode: gem
98
+ site: https://bashconsultants.com
99
+ dispatch: true
100
+ note: >-
101
+ Three stacks, three theme surfaces: GitHub Pages uses the pinned
102
+ remote_theme, Azure Static Web Apps resolves the published gem via
103
+ Gemfile.azure + _config.azure.yml, and local dev mounts a path gem. A bump
104
+ must move the remote_theme tag and the gem constraint together or the two
105
+ production stacks render different theme versions.
106
+ pins:
107
+ - file: _config.yml
108
+ kind: remote_theme
109
+ note: line 31
110
+ - file: Gemfile.azure
111
+ kind: gem_constraint
112
+ note: line 9 — must match the remote_theme tag
113
+ - file: _config.azure.yml
114
+ kind: theme_name
115
+ note: line 3 — activates the gem; no version of its own
116
+ - file: Gemfile
117
+ kind: path_gem
118
+ note: line 40 — dev checkout via ZER0_MISTAKES_PATH; never bumped
119
+
120
+ # ---------------------------------------------------------------------------
121
+ - repo: year-of-ai/year-of-ai.github.io
122
+ mode: remote_theme_pinned
123
+ site: https://year-of-ai.github.io
124
+ dispatch: true
125
+ fans_out_to_members: true
126
+ note: >-
127
+ An org hub, not just a site. Its _data/hub.yml theme_repo is the value
128
+ stamped into every member repo's _config.yml by scripts/provision-org-sites.rb,
129
+ so one bump here re-rolls the whole org (12 sites). The hub's own
130
+ _config.yml and hub.yml must move together — they are read by different
131
+ code paths and drift silently otherwise.
132
+ pins:
133
+ - file: _config.yml
134
+ kind: remote_theme
135
+ note: line 40
136
+ - file: _data/hub.yml
137
+ kind: hub_registry
138
+ note: "line 42 (pages.theme_repo) — re-roll members after bumping"
139
+
140
+ # ---------------------------------------------------------------------------
141
+ - repo: ai-world-view/ai-world-view.github.io
142
+ mode: remote_theme_pinned
143
+ site: https://ai-world-view.github.io
144
+ dispatch: true
145
+ fans_out_to_members: true
146
+ note: >-
147
+ Same two-file hub shape as year-of-ai; members join by auto_discover, so
148
+ the member count grows without this file changing.
149
+ pins:
150
+ - file: _config.yml
151
+ kind: remote_theme
152
+ note: line 35
153
+ - file: _data/hub.yml
154
+ kind: hub_registry
155
+ note: "line 39 (pages.theme_repo) — re-roll members after bumping"
156
+
157
+ # ---------------------------------------------------------------------------
158
+ # The four entries below were missing entirely until the 2026-08 fleet audit.
159
+ # Unregistered consumers are invisible to propagate.rb in BOTH directions: no
160
+ # release dispatch reaches them, and they never appear in the weekly drift
161
+ # report — so their pins can rot indefinitely with nothing to notice. That is
162
+ # exactly what happened to zer0-pages, which sat three minors behind on a gem
163
+ # constraint that already permitted the current release.
164
+ # ---------------------------------------------------------------------------
165
+ - repo: bamr87/zer0-pages
166
+ mode: gem
167
+ site: https://bamr87.github.io/zer0-pages/
168
+ dispatch: true
169
+ note: >-
170
+ Dual Obsidian-vault / Jekyll repo. Two things make it unlike every other
171
+ consumer. (1) It sets `source: pages`, so the whole themable tree lives
172
+ under pages/ — audit-consumer follows `source:` for this reason; auditing
173
+ the repo root reports a falsely clean result. (2) It is gem mode with no
174
+ remote_theme anywhere, so NOTHING is picked up automatically: the
175
+ scheduled bump is its only path to a new version.
176
+ risk: >-
177
+ Gemfile.lock resolved 1.25.0 while the Gemfile constraint (~> 1.25)
178
+ already permitted 1.28.0 — stale purely because nothing was nudging it.
179
+ Keeps two deliberate include forks, declared in its .theme-overrides.yml.
180
+ pins:
181
+ - file: Gemfile
182
+ kind: gem_constraint
183
+ note: 'line 11 — `gem "jekyll-theme-zer0", "~> 1.25"`'
184
+ - file: Gemfile.lock
185
+ kind: resolved_lockfile
186
+ note: "line 86 — re-lock with `bundle lock --update jekyll-theme-zer0`"
187
+ - file: _config.yml
188
+ kind: theme_name
189
+ note: "line 29 — `theme: jekyll-theme-zer0`; activates the gem, no version of its own"
190
+
191
+ # ---------------------------------------------------------------------------
192
+ - repo: bamr87/irony-works
193
+ mode: remote_theme_floating
194
+ site: https://bamr87.github.io/irony-works/
195
+ dispatch: false
196
+ note: >-
197
+ Vault + Node engine; engine/scripts/transplant.mjs converts vault/ into
198
+ the _entries/ collection before Jekyll runs. Has NO Gemfile at all — it
199
+ builds via actions/jekyll-build-pages (the github-pages image), so it can
200
+ only ever be tracked as remote_theme_floating; there is no gem constraint
201
+ surface for a bump to rewrite.
202
+ risk: >-
203
+ Unpinned, and with no local preview path a contributor cannot see a theme
204
+ break before pushing. Keeps three deliberate overrides, declared in its
205
+ .theme-overrides.yml.
206
+ pins:
207
+ - file: _config.yml
208
+ kind: remote_theme
209
+ note: line 4, no tag — floats main
210
+
211
+ # ---------------------------------------------------------------------------
212
+ - repo: bamr87/wargames
213
+ mode: remote_theme_floating
214
+ site: https://bamr87.github.io/wargames/
215
+ dispatch: false
216
+ note: >-
217
+ Content-only vendored mirror of the OverTheWire challenges. Zero local
218
+ theme files — one of the cleanest consumers in the fleet. Its _config.yml
219
+ uses column-aligned YAML (`remote_theme : "..."`), the style
220
+ that used to defeat audit-consumer's mode detection.
221
+ risk: >-
222
+ Unpinned, and its only build runs on push to main (jekyll-gh-pages.yml),
223
+ so a break ships before anyone sees it.
224
+ pins:
225
+ - file: _config.yml
226
+ kind: remote_theme
227
+ note: line 44, no tag — floats main
228
+
229
+ # ---------------------------------------------------------------------------
230
+ - repo: bamr87/zer0-pages-remote
231
+ mode: remote_theme_floating
232
+ site: https://bamr87.github.io/zer0-pages-remote/
233
+ dispatch: false
234
+ note: >-
235
+ The minimal reference consumer: _config.yml + Gemfile + index.md, no
236
+ local theme files and no _data at all, still showing the onboarding
237
+ wizard by design. Useful as the canary for what a stock remote_theme
238
+ consumer gets with zero customisation — if the theme breaks a bare site,
239
+ it breaks here first.
240
+ private: true
241
+ risk: >-
242
+ Unpinned. Low stakes (a demo, not a content site), but it has already
243
+ caught one real integration break: PR #8 fixed a Liquid `include_cached`
244
+ failure caused by a Gemfile plugin missing from the safe-mode whitelist.
245
+ This repo is PRIVATE, so an unauthenticated `propagate.rb` run reports it
246
+ "unreachable" rather than reading its pin — drift reporting needs a token
247
+ here even though it is read-only. CI already supplies one; a bare local
248
+ run will not.
249
+ pins:
250
+ - file: _config.yml
251
+ kind: remote_theme
252
+ note: line 12, no tag — floats main