jekyll-theme-zer0 1.28.0 → 1.30.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1143 -13
  3. data/_data/README.md +2 -0
  4. data/_data/ai.yml +5 -3
  5. data/_data/ai_pricing.yml +36 -0
  6. data/_data/backlog.yml +507 -2
  7. data/_data/consumers.yml +157 -9
  8. data/_data/features.yml +303 -20
  9. data/_data/feedback_types.yml +17 -12
  10. data/_data/i18n/fr.yml +12 -7
  11. data/_data/i18n/manifest.yml +39 -12
  12. data/_data/ingredient_densities.yml +122 -0
  13. data/_data/landing.yml +5 -2
  14. data/_data/navigation/main.yml +16 -0
  15. data/_data/navigation/quickstart.yml +4 -0
  16. data/_data/recipe_courses.yml +64 -0
  17. data/_data/site_builder.yml +874 -0
  18. data/_data/theme-manifest.yml +160 -124
  19. data/_data/ui-text.yml +26 -0
  20. data/_includes/README.md +26 -2
  21. data/_includes/analytics/posthog.html +2 -2
  22. data/_includes/components/admin-links.html +2 -2
  23. data/_includes/components/admin-tabs.html +2 -2
  24. data/_includes/components/ai-chat.html +14 -11
  25. data/_includes/components/analytics-dashboard.html +8 -8
  26. data/_includes/components/author-bio.html +1 -1
  27. data/_includes/components/author-card.html +10 -2
  28. data/_includes/components/author-eeat.html +4 -4
  29. data/_includes/components/background-customizer.html +10 -10
  30. data/_includes/components/background-image.html +114 -0
  31. data/_includes/components/background-settings.html +28 -15
  32. data/_includes/components/collection-manager.html +5 -5
  33. data/_includes/components/component-showcase.html +13 -13
  34. data/_includes/components/config-editor.html +12 -12
  35. data/_includes/components/config-viewer.html +8 -8
  36. data/_includes/components/cookie-consent.html +15 -15
  37. data/_includes/components/cta-button.html +7 -2
  38. data/_includes/components/dev-shortcuts.html +7 -7
  39. data/_includes/components/env-dashboard.html +8 -8
  40. data/_includes/components/env-switcher.html +9 -9
  41. data/_includes/components/feature-card.html +2 -2
  42. data/_includes/components/halfmoon.html +2 -2
  43. data/_includes/components/info-section.html +42 -37
  44. data/_includes/components/js-cdn.html +15 -15
  45. data/_includes/components/language-toggle.html +168 -21
  46. data/_includes/components/mermaid.html +72 -435
  47. data/_includes/components/nanobar.html +5 -5
  48. data/_includes/components/nav-editor.html +2 -2
  49. data/_includes/components/nav-export.html +2 -2
  50. data/_includes/components/nav-overview.html +2 -2
  51. data/_includes/components/page-feedback.html +45 -30
  52. data/_includes/components/page-views-init.html +55 -0
  53. data/_includes/components/page-views.html +33 -0
  54. data/_includes/components/post-card.html +22 -22
  55. data/_includes/components/post-type-badge.html +2 -2
  56. data/_includes/components/powered-by.html +2 -2
  57. data/_includes/components/preview-image.html +6 -0
  58. data/_includes/components/quick-index.html +2 -2
  59. data/_includes/components/recipe-card.html +67 -0
  60. data/_includes/components/recipe-duration.html +50 -0
  61. data/_includes/components/recipe-grams.html +58 -0
  62. data/_includes/components/recipe-index.html +96 -0
  63. data/_includes/components/recipe-ingredients.html +90 -0
  64. data/_includes/components/recipe-meta.html +96 -0
  65. data/_includes/components/recipe-nutrition.html +57 -0
  66. data/_includes/components/recipe-qty.html +73 -0
  67. data/_includes/components/recipe-ratio.html +151 -0
  68. data/_includes/components/recipe-scaler.html +73 -0
  69. data/_includes/components/recipe-steps.html +86 -0
  70. data/_includes/components/recipe-temp.html +45 -0
  71. data/_includes/components/search-modal.html +29 -4
  72. data/_includes/components/searchbar.html +2 -2
  73. data/_includes/components/shortcuts-modal.html +3 -0
  74. data/_includes/components/svg-background.html +2 -2
  75. data/_includes/components/theme-customizer.html +2 -2
  76. data/_includes/components/theme-info.html +14 -7
  77. data/_includes/components/theme-preview-gallery.html +22 -22
  78. data/_includes/content/giscus.html +2 -2
  79. data/_includes/content/intro.html +8 -8
  80. data/_includes/content/jsonld-faq.html +2 -2
  81. data/_includes/content/jsonld-software.html +24 -5
  82. data/_includes/content/seo.html +4 -4
  83. data/_includes/content/sitemap.html +27 -27
  84. data/_includes/content/toc.html +183 -183
  85. data/_includes/core/branding.html +6 -6
  86. data/_includes/core/console-capture.html +32 -74
  87. data/_includes/core/favicon.html +49 -7
  88. data/_includes/core/footer-fabs.html +17 -3
  89. data/_includes/core/footer.html +49 -34
  90. data/_includes/core/head.html +110 -86
  91. data/_includes/core/header.html +76 -54
  92. data/_includes/docs/bootstrap-docs.html +8 -8
  93. data/_includes/landing/landing-install-cards.html +2 -2
  94. data/_includes/landing/landing-quick-links.html +1 -1
  95. data/_includes/navigation/admin-nav.html +2 -2
  96. data/_includes/navigation/nav-tree.html +8 -8
  97. data/_includes/navigation/navbar.html +12 -12
  98. data/_includes/navigation/section-sidebar.html +109 -27
  99. data/_includes/navigation/sidebar-config.html +36 -2
  100. data/_includes/navigation/sidebar-left.html +17 -16
  101. data/_includes/navigation/sidebar-right.html +8 -7
  102. data/_includes/obsidian/full-graph.html +2 -2
  103. data/_includes/setup/claude-session.html +72 -0
  104. data/_includes/setup/prereq-checklist.html +90 -0
  105. data/_includes/setup/wizard.html +924 -222
  106. data/_includes/stats/stats-categories.html +8 -8
  107. data/_includes/stats/stats-header.html +14 -14
  108. data/_includes/stats/stats-metrics.html +14 -14
  109. data/_includes/stats/stats-no-data.html +12 -12
  110. data/_includes/stats/stats-overview.html +6 -6
  111. data/_includes/stats/stats-tags.html +8 -8
  112. data/_layouts/404.html +38 -24
  113. data/_layouts/README.md +2 -0
  114. data/_layouts/admin.html +24 -24
  115. data/_layouts/article.html +43 -33
  116. data/_layouts/author.html +20 -20
  117. data/_layouts/authors.html +2 -2
  118. data/_layouts/book-abc.html +12 -12
  119. data/_layouts/book-story.html +15 -15
  120. data/_layouts/book.html +12 -12
  121. data/_layouts/collection.html +33 -33
  122. data/_layouts/cookbook.html +88 -0
  123. data/_layouts/default.html +31 -28
  124. data/_layouts/home.html +23 -23
  125. data/_layouts/index.html +10 -10
  126. data/_layouts/landing.html +17 -17
  127. data/_layouts/news.html +44 -44
  128. data/_layouts/note.html +38 -38
  129. data/_layouts/notebook.html +34 -34
  130. data/_layouts/recipe.html +274 -0
  131. data/_layouts/root.html +92 -55
  132. data/_layouts/section.html +62 -33
  133. data/_layouts/setup.html +3 -3
  134. data/_layouts/sitemap-collection.html +49 -49
  135. data/_layouts/stats.html +40 -40
  136. data/_layouts/tag.html +12 -12
  137. data/_layouts/welcome.html +21 -21
  138. data/_sass/components/_callout.scss +1 -1
  139. data/_sass/components/_footer.scss +37 -1
  140. data/_sass/components/_mermaid.scss +375 -0
  141. data/_sass/components/_page-views.scss +36 -0
  142. data/_sass/components/_recipe.scss +506 -0
  143. data/_sass/components/_setup-wizard.scss +764 -0
  144. data/_sass/components/_ui-enhancements.scss +6 -6
  145. data/_sass/core/_navbar.scss +261 -46
  146. data/_sass/layouts/_landing.scss +2 -2
  147. data/_sass/layouts/_navbar-extras.scss +14 -4
  148. data/_sass/tokens/_color.scss +6 -0
  149. data/_sass/tokens/_index.scss +2 -0
  150. data/_sass/tokens/_radius.scss +21 -0
  151. data/_sass/tokens/_typography.scss +4 -0
  152. data/_sass/utilities/_focus.scss +14 -0
  153. data/assets/css/main.scss +4 -0
  154. data/assets/js/ai-chat.js +47 -5
  155. data/assets/js/fleet-feedback-capture.js +124 -0
  156. data/assets/js/fleet-feedback.js +853 -0
  157. data/assets/js/mermaid-diagrams.js +1267 -0
  158. data/assets/js/modules/navigation/config.js +9 -6
  159. data/assets/js/modules/navigation/navbar.js +55 -0
  160. data/assets/js/modules/navigation/scroll-spy.js +315 -80
  161. data/assets/js/modules/theme/appearance.js +8 -2
  162. data/assets/js/obsidian-wiki-links.js +8 -3
  163. data/assets/js/page-feedback.js +125 -192
  164. data/assets/js/page-views.js +372 -0
  165. data/assets/js/recipe-scaler.js +501 -0
  166. data/assets/js/search-modal.js +36 -0
  167. data/assets/js/setup-wizard.js +2279 -226
  168. data/assets/js/site-builder.js +1834 -0
  169. data/assets/js/ui-enhancements.js +11 -3
  170. data/scripts/README.md +44 -0
  171. data/scripts/ai/README.md +38 -0
  172. data/scripts/ai/api_call.rb +124 -0
  173. data/scripts/ai/usage.rb +314 -0
  174. data/scripts/ai/usage_report.rb +225 -0
  175. data/scripts/bin/audit-consumer +39 -7
  176. data/scripts/bin/giscus-discussions +213 -14
  177. data/scripts/bin/manifest +35 -12
  178. data/scripts/ci/agent_review_result.py +164 -0
  179. data/scripts/ci/test_agent_review_result.py +172 -0
  180. data/scripts/ci/test_visual_evidence_autogen.py +341 -0
  181. data/scripts/ci/visual_evidence_autogen.py +1060 -0
  182. data/scripts/content-review.rb +20 -1
  183. data/scripts/design-system-check.rb +170 -0
  184. data/scripts/lib/audit.sh +42 -2
  185. data/scripts/lint-liquid-raw.rb +137 -0
  186. data/scripts/test/integration/mermaid +22 -8
  187. data/scripts/test/lib/run_tests.sh +3 -1
  188. data/scripts/test/lib/test_agent_review_result.sh +27 -0
  189. data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
  190. data/scripts/translate.rb +94 -16
  191. metadata +48 -2
data/_data/README.md CHANGED
@@ -7,6 +7,8 @@ This directory contains YAML data files that power the Zer0-Mistakes Jekyll them
7
7
  | File | Purpose | Auto-Generated |
8
8
  |------|---------|----------------|
9
9
  | `navigation/` | Site navigation menus | No |
10
+ | `ai.yml` | The one place the AI model is configured (read by the hub's `bamr87/bamr87/.github/actions/claude-run@main` runner, consumed by reference) | No |
11
+ | `ai_pricing.yml` | API list prices used to *estimate* AI spend for calls that report tokens but no cost (part of the shared `ai-runner` kit) | No |
10
12
  | `authors.yml` | Author profile information | No |
11
13
  | `content_statistics.yml` | Site content analytics | **Yes** |
12
14
  | `roadmap.yml` | Single source of truth for the project roadmap (drives the README mermaid diagram and the `/roadmap/` Jekyll page) | No |
data/_data/ai.yml CHANGED
@@ -1,9 +1,11 @@
1
1
  # =============================================================================
2
2
  # _data/ai.yml — one place the AI model is configured for zer0-mistakes agents
3
3
  # -----------------------------------------------------------------------------
4
- # Read by .github/actions/claude-run (the issue-autopilot's AI step). Change the
5
- # model here and every autopilot agent call uses it. Auth is NOT here — it comes
6
- # from the CLAUDE_CODE_OAUTH_TOKEN (preferred) or ANTHROPIC_API_KEY secret.
4
+ # Read by the fleet's ai-runner kit bamr87/bamr87/.github/actions/claude-run
5
+ # (consumed by reference; the issue-autopilot's AI step). Change the model
6
+ # here and every agent call uses it; AI_MODEL (or the action's `model` input)
7
+ # overrides it for one run. Auth is NOT here — it comes from the
8
+ # CLAUDE_CODE_OAUTH_TOKEN (preferred) or ANTHROPIC_API_KEY secret.
7
9
  # =============================================================================
8
10
  provider: anthropic
9
11
  model: claude-opus-4-8
@@ -0,0 +1,36 @@
1
+ # =============================================================================
2
+ # ai_pricing.yml — API list prices used to ESTIMATE AI spend (per million tokens)
3
+ # -----------------------------------------------------------------------------
4
+ # The telemetry layer (scripts/ai/usage.rb) prefers the cost the Claude Code CLI
5
+ # itself reports (`total_cost_usd`) — that number is authoritative and this file
6
+ # is never consulted for it. This table exists for the paths that report tokens
7
+ # but no dollars: the raw Claude API fallback (scripts/ai/api_call.rb) and any
8
+ # execution log that predates cost reporting. Those records get cost_source:
9
+ # "estimated", computed as
10
+ #
11
+ # input×in + output×out + cache_read×(0.1×in)
12
+ # + cache_write_5m×(1.25×in) + cache_write_1h×(2×in)
13
+ #
14
+ # which reproduces the CLI's own arithmetic to the cent (validated 2026-07-14
15
+ # against a live run). Prices are USD per MTok at standard API list rates —
16
+ # subscription (OAuth) runs have no marginal dollar cost, so every published
17
+ # figure is labeled "API-equivalent". Source: platform.claude.com/docs/en/pricing
18
+ # (cached 2026-07-14). Update this file when Anthropic changes list prices;
19
+ # estimates are marked as such everywhere they surface.
20
+ # =============================================================================
21
+ multipliers:
22
+ cache_read: 0.1 # × input price
23
+ cache_write_5m: 1.25 # × input price (5-minute TTL — the CLI default)
24
+ cache_write_1h: 2.0 # × input price (1-hour TTL)
25
+
26
+ # Matched by longest model-id prefix, so dated snapshots (claude-haiku-4-5-20251001)
27
+ # resolve to their family row. `default` catches unknown models (opus-tier, so an
28
+ # unknown model over- rather than under-estimates).
29
+ models:
30
+ claude-opus-4-8: { input: 5.00, output: 25.00 }
31
+ claude-opus-4-7: { input: 5.00, output: 25.00 }
32
+ claude-opus-4-6: { input: 5.00, output: 25.00 }
33
+ claude-sonnet-5: { input: 3.00, output: 15.00 }
34
+ claude-sonnet-4-6: { input: 3.00, output: 15.00 }
35
+ claude-haiku-4-5: { input: 1.00, output: 5.00 }
36
+ default: { input: 5.00, output: 25.00 }
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-09-05
66
+ next_id: 52
67
67
 
68
68
  tasks:
69
69
  # --- Housekeeping (seeded so the loop has work on day one) ------------------
@@ -916,3 +916,508 @@ 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: done
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
+ # Scoped to the navbar per the issue's Option A. Criterion 1 was
972
+ # originally "no transform in any :hover rule under _sass/" — that is 42
973
+ # rules across ~15 files (card lifts, book tilts, recipe hovers) and a
974
+ # visual redesign of a published theme, not this defect. It also flagged
975
+ # `transform: none`, which is the CORRECTIVE pattern this repo already
976
+ # uses in _post-navigation.scss and the prefers-reduced-motion blocks.
977
+ # A theme-wide motion policy belongs in its own task.
978
+ # Stated as the COMPUTED value, not "no matching rule": these controls
979
+ # are `.btn`s and _ui-enhancements.scss's theme-wide
980
+ # `.btn:hover { translateY(-2px) }` still matches them and always will.
981
+ # It is overridden by `transform: none`, not deleted, so a rule-existence
982
+ # check would be permanently red. That inherited lift is why deleting the
983
+ # navbar's own translateY was not sufficient on its own.
984
+ - "getComputedStyle(control).transform is `none` while hovered, for every navbar control."
985
+ # Direction corrected: translateY(-1px) moves the element UP, so the row
986
+ # it stops covering is the BOTTOM one. A test parked on the top edge
987
+ # passes without exercising the bug.
988
+ - "Pointer parked on the bottom edge of any nav item or utility button does not flicker."
989
+ # Note: `#bdNavbar .nav-link:hover`, named in the summary as the primary
990
+ # cause, was dead code — its lift sits inside a container query that never
991
+ # matches at desktop widths (measured 1040/1140/1200/1280px on the built
992
+ # site, `none` at every one, on main too). The observable flicker was on
993
+ # the utility buttons and the FABs.
994
+ - "regression test added"
995
+ - "before/after evidence committed"
996
+ links: { issue: 404, pr: null, roadmap: null }
997
+ created: 2026-08-18
998
+ updated: 2026-09-03
999
+
1000
+ - id: T-037
1001
+ title: "Navbar width recovery: two clean label tiers, merged chevron, centered grid, no subtitle"
1002
+ status: open
1003
+ priority: P2
1004
+ area: feat
1005
+ risk: standard
1006
+ effort: M
1007
+ source: user
1008
+ route: theme-ui
1009
+ summary: >-
1010
+ Top nav is cramped and truncates labels ("Quicksta…"). Keep per-item icons
1011
+ at every tier but only two states — icon + full label (wide container) and
1012
+ the existing icon-only tier (narrow) — removing the intermediate ellipsized
1013
+ state (_includes/navigation/navbar.html + _sass/core/_navbar.scss container
1014
+ queries). Merge each dropdown's separate 22-24px chevron button into the
1015
+ parent link's padding box (position:absolute overlay, shared hover
1016
+ background on the li, button kept for a11y). Use grid-template-columns:
1017
+ 1fr auto 1fr on .navbar-main so the menu stays optically centered. Retire
1018
+ the navbar subtitle (_includes/core/branding.html — home hero only).
1019
+ Clarify the three toggles: labeled "Menu" text button below lg, gear moves
1020
+ into the unified drawer on mobile so only two toggles coexist.
1021
+ acceptance:
1022
+ - "At 1024px and 1280px with 5 nav items every label is fully visible or the whole row is icon-only — no ellipses anywhere."
1023
+ - "Dropdown chevron shares the parent row's hover zone; no dead zone between link and chevron."
1024
+ - "Navbar carries logo + title only; subtitle appears on the home hero."
1025
+ - "regression test added"
1026
+ - "before/after evidence committed"
1027
+ links: { issue: null, pr: null, roadmap: null }
1028
+ created: 2026-08-18
1029
+ updated: 2026-08-18
1030
+
1031
+ - id: T-038
1032
+ title: "Language switcher: icon-only trigger and a compact, positive menu"
1033
+ status: done
1034
+ priority: P2
1035
+ area: feat
1036
+ risk: standard
1037
+ effort: S
1038
+ source: user
1039
+ route: theme-ui
1040
+ summary: >-
1041
+ _includes/components/language-toggle.html: drop the EN text span and the
1042
+ Bootstrap caret (38-40px square icon button; title + aria-label carry the
1043
+ name). In the menu, mark the current language with a check icon + tint
1044
+ (not .active primary fill); keep untranslated languages clickable
1045
+ (navigate to source page, set zer0-lang); mark machine translations with a
1046
+ small "auto" chip; replace per-row "(Not yet translated)" with one
1047
+ footnote line under a divider; menu width <= 220px.
1048
+ acceptance:
1049
+ - "Menu shows no disabled rows; widest row <= 220px; current language visually distinct without primary fill."
1050
+ - "Trigger is icon-only at all widths with an accessible name."
1051
+ - "regression test added"
1052
+ - "before/after evidence committed"
1053
+ links: { issue: 406, pr: 431, roadmap: null }
1054
+ created: 2026-08-18
1055
+ updated: 2026-09-01
1056
+ - id: T-039
1057
+ title: "Eliminate page-transition layout shift (fixed-nav offset, scrollbar gutter, server-rendered slots)"
1058
+ status: open
1059
+ priority: P1
1060
+ area: perf
1061
+ risk: standard
1062
+ effort: M
1063
+ source: user
1064
+ route: theme-ui
1065
+ summary: >-
1066
+ Navigation between pages visibly bounces. Define --zer0-nav-height in
1067
+ :root, derive body padding-top and every sticky offset (admin.html's
1068
+ top: 5rem, TOC, sidebars) and scroll-margin-top from it (no JS
1069
+ measurement). Add html { scrollbar-gutter: stable }. Render banner/nanobar
1070
+ slots server-side with fixed height (nanobar 3px strip always present;
1071
+ setup banner via Liquid when site_needs_setup, not JS insert). Verify
1072
+ explicit width/height on content images and font-display: swap + preload
1073
+ for bootstrap-icons woff2. Progressive enhancement: @view-transition
1074
+ { navigation: auto } with a 120ms root fade.
1075
+ acceptance:
1076
+ - "Lighthouse CLS < 0.02 on landing, docs, and admin pages."
1077
+ - "No visible reflow when navigating between short and long pages (scrollbar gutter stable)."
1078
+ - "All sticky offsets derive from --zer0-nav-height (no hardcoded top: 5rem)."
1079
+ - "regression test added"
1080
+ - "before/after evidence committed"
1081
+ links: { issue: null, pr: null, roadmap: null }
1082
+ created: 2026-08-18
1083
+ updated: 2026-08-18
1084
+
1085
+ - id: T-040
1086
+ title: "Setup wizard: live YAML preview, vertical stepper, draft persistence, stable panel height, inline validation"
1087
+ status: done
1088
+ priority: P2
1089
+ area: feat
1090
+ risk: standard
1091
+ effort: M
1092
+ source: user
1093
+ route: theme-ui
1094
+ summary: >-
1095
+ _includes/setup/wizard.html + assets/js/setup-wizard.js: replace the
1096
+ step-5-only <pre> with a persistent sticky preview panel regenerated on
1097
+ every input (copy/download always enabled); replace nav-pills tabs with a
1098
+ left vertical stepper showing done/active/upcoming state (steps unlock on
1099
+ validation, Back always allowed, remove the "N% complete" math); persist
1100
+ the draft to localStorage under zer0-setup-draft (debounced 300ms, restore
1101
+ on load, "Draft saved" chip, clear on download); give the panel container
1102
+ a min-height equal to the tallest step so Back/Next never move vertically;
1103
+ validate email/URL on blur with is-invalid + message and list unfilled
1104
+ recommended fields as warnings on the Review step.
1105
+ acceptance:
1106
+ - "YAML preview visible and live at lg+; Back/Next buttons never shift vertically between steps."
1107
+ - "Reload mid-wizard restores the draft; invalid email/URL flagged on blur."
1108
+ - "regression test added"
1109
+ - "before/after evidence committed"
1110
+ links: { issue: 408, pr: 432, roadmap: null }
1111
+ created: 2026-08-18
1112
+ updated: 2026-09-01
1113
+ - id: T-041
1114
+ title: "Admin layout: actions reachable on mobile, grouped sidebar, shared sticky offset, single heading"
1115
+ status: open
1116
+ priority: P2
1117
+ area: feat
1118
+ risk: standard
1119
+ effort: M
1120
+ source: user
1121
+ route: theme-ui
1122
+ summary: >-
1123
+ _layouts/admin.html + _includes/navigation/admin-nav.html: the
1124
+ admin_actions wrapper is d-none d-md-flex so actions are unreachable on
1125
+ phones — render the first action as a visible button at all widths with
1126
+ the rest in a "..." dropdown. Group sidebar items under small-caps headers
1127
+ (Configure / System / Links) via a group: key in
1128
+ _data/navigation/admin.yml; counts as plain numerals, statuses as colored
1129
+ dot + word; active item = tint + semibold, not solid primary fill. Replace
1130
+ style="top: 5rem" with top: calc(var(--zer0-nav-height) + 1rem) (depends
1131
+ on T-039). Keep the "Administration" heading in the offcanvas header only.
1132
+ acceptance:
1133
+ - "Every admin action reachable at 375px width."
1134
+ - "Sidebar grouped; no solid-primary active fill; sticky offset uses the shared variable."
1135
+ - "regression test added"
1136
+ - "before/after evidence committed"
1137
+ links: { issue: null, pr: null, roadmap: null }
1138
+ created: 2026-08-18
1139
+ updated: 2026-08-18
1140
+ depends_on: [T-039]
1141
+
1142
+ - id: T-042
1143
+ title: "Skin contrast sweep: AA link params, tertiary meta text, per-skin badge/button audit"
1144
+ status: open
1145
+ priority: P1
1146
+ area: a11y
1147
+ risk: standard
1148
+ effort: M
1149
+ source: user
1150
+ route: a11y
1151
+ summary: >-
1152
+ _sass/theme/_skins.scss: several skins omit the zer0-skin-palette mixin's
1153
+ $link-light/$link-dark params and fall back to $brand/$accent, which fail
1154
+ 4.5:1 (yellow/orange/cyan brands on white, saturated brands on #212529) —
1155
+ pass explicit AA-checked values for every skin with a comment table of
1156
+ measured ratios. text-body-tertiary on 11-12px meta text (admin header
1157
+ "Updated…", wizard hints) falls below AA — use --bs-secondary-color at
1158
+ small sizes, tertiary only >= 16px. Audit .btn-primary text per skin
1159
+ (auto-switch to dark text when brand luminance > 0.55 via color-contrast()
1160
+ or a precomputed token).
1161
+ acceptance:
1162
+ - "axe/Lighthouse contrast passes on light + dark for all skins."
1163
+ - "Every skin passes explicit link colors; measured-ratio table committed as comments."
1164
+ - "regression test added"
1165
+ - "before/after evidence committed"
1166
+ links: { issue: null, pr: null, roadmap: null }
1167
+ created: 2026-08-18
1168
+ updated: 2026-08-18
1169
+
1170
+ - id: T-043
1171
+ title: "Mermaid diagrams: render-in-place (no CLS), token-driven theming, figure shell, flash-free theme switch"
1172
+ status: open
1173
+ priority: P2
1174
+ area: feat
1175
+ risk: standard
1176
+ effort: L
1177
+ source: user
1178
+ route: theme-ui
1179
+ summary: >-
1180
+ _includes/components/mermaid.html: the hidden pre > code is replaced by a
1181
+ min-height:100px div that grows to 300-500px on render, shifting the page —
1182
+ render in place with the placeholder inheriting the code block's height
1183
+ and animate the swap. Delete the ~20 !important SVG overrides that fight
1184
+ JS themeVariables (forced white text breaks colored nodes; light mode uses
1185
+ #007bff vs site primary #0d6efd; skins don't affect diagrams) — build
1186
+ themeVariables at init from live tokens (getComputedStyle on --bs-primary
1187
+ / --bs-body-bg / --bs-border-color / --bs-body-color), re-run on
1188
+ data-bs-theme AND data-theme-skin change. Wrap each diagram in a
1189
+ figure.mermaid-figure with type chip, optional title, copy-source /
1190
+ download-SVG / fullscreen toolbar (reuse the code-copy button pattern) and
1191
+ figcaption. On theme switch render the new SVG off-DOM and cross-fade
1192
+ 120ms (instant under prefers-reduced-motion). Builds on T-039.
1193
+ acceptance:
1194
+ - "CLS contribution from a page with 3 diagrams < 0.01."
1195
+ - "No !important SVG overrides remain; diagrams follow theme AND skin tokens live."
1196
+ - "Theme switch shows no blank flash."
1197
+ - "regression test added"
1198
+ - "before/after evidence committed"
1199
+ links: { issue: null, pr: null, roadmap: null }
1200
+ created: 2026-08-18
1201
+ updated: 2026-08-18
1202
+ depends_on: [T-039]
1203
+
1204
+ - id: T-044
1205
+ title: "Playwright suite aborts: duplicate openDialog declaration in accessibility.spec.js"
1206
+ status: done
1207
+ priority: P1
1208
+ area: tests
1209
+ risk: low
1210
+ effort: S
1211
+ source: issue
1212
+ route: test-author
1213
+ summary: >-
1214
+ test/visual/core/accessibility.spec.js declares `async function
1215
+ openDialog` twice — at line 172 (page, selector) and again at line 425
1216
+ (page, selector, kind), the latter added by 98fd6f9 (#387) without
1217
+ removing the former. Two function declarations of the same name in one
1218
+ module scope is a SyntaxError, so Playwright fails to parse the file and
1219
+ the WHOLE run aborts before any test executes: "Identifier 'openDialog'
1220
+ has already been declared. (425:15)". This takes down the critical and
1221
+ smoke tiers together, on every branch, and has been red on main since
1222
+ 2026-08-14. Discovered during #414, whose own new spec
1223
+ (test/visual/features/recipes.spec.js) therefore cannot run in CI. The
1224
+ three-argument version is a strict superset — with `kind` undefined it
1225
+ resolves to Ctor='Modal' and 'shown.bs.modal', exactly what the
1226
+ two-argument one does — so deleting the earlier declaration (lines
1227
+ 165-184, comment included) is behaviour-preserving.
1228
+ acceptance:
1229
+ - "test/visual/core/accessibility.spec.js parses; `npx playwright test --project=critical` runs to completion."
1230
+ - "Only one openDialog declaration remains, and its callers all still pass."
1231
+ - "No behavioural change to the modal/offcanvas a11y tests."
1232
+ links: { issue: 416, pr: null, roadmap: null }
1233
+ created: 2026-08-23
1234
+ updated: 2026-08-23
1235
+
1236
+ - id: T-045
1237
+ title: "Homepage snapshot baselines were regenerated without the 1967px drift being diagnosed"
1238
+ status: done
1239
+ priority: P2
1240
+ area: tests
1241
+ risk: low
1242
+ effort: S
1243
+ source: issue
1244
+ route: test-author
1245
+ summary: >-
1246
+ The Visual Snapshots tier fails all 9 `Theme skins > homepage visual
1247
+ snapshot` cases with the SAME diff — 1967 pixels (ratio 0.01) — against
1248
+ test/visual/snapshots/features/appearance-snapshot.spec.js-snapshots/homepage-*-snapshots-linux.png,
1249
+ well over the maxDiffPixels 150 budget. The count being identical across
1250
+ air/aqua/contrast/dark/dirt/neon/mint/plum/sunrise points at one shared,
1251
+ skin-independent region rather than a palette regression. Verified during
1252
+ #414 that it is NOT that PR's doing: building origin/main and the PR
1253
+ branch and screenshotting the homepage at the spec's own 1280x720
1254
+ viewport produced BYTE-IDENTICAL PNGs (same md5), so the branch renders
1255
+ the homepage exactly as main does and the drift is between the committed
1256
+ baselines and what main itself now renders. Note while investigating:
1257
+ _includes/components/theme-info.html renders a minute-resolution build
1258
+ stamp, site.time | date "%b %d, %H:%M" — it currently sits in a collapsed
1259
+ offcanvas (measured 0x0, not in the viewport) so it is not the cause
1260
+ today, but it is a live non-determinism hazard the moment that panel is
1261
+ shown by default, and worth masking regardless. UPDATE 2026-08-23: #413
1262
+ regenerated all 9 baselines when it merged, so the check is green again —
1263
+ resolved by re-blessing rather than by diagnosis, which is what the first
1264
+ acceptance criterion was meant to prevent. Diffing the old vs new CI
1265
+ baselines (homepage-air, 2596f866 vs e488c4c) puts the whole 5016px delta
1266
+ in two bands: y21-41 (a wide navbar strip, 4688px) and y626 (328px).
1267
+ page-views does render on the homepage so part of that is legitimately
1268
+ #413, but a navbar strip is not where a view-count badge belongs.
1269
+ Separating the two needs the diff artifact from a pre-#413 failing run,
1270
+ which has rolled off. What remains is confirming the y21-41 band is
1271
+ accounted for. UPDATE 2026-08-25: the y21-41 band is the navbar
1272
+ reflowing, and the reason it RECURS is structural — a path-filter gap.
1273
+ ci.yml's `styling` filter (which gates the snapshots job) lists only code
1274
+ paths; `_data/**` appears solely under `content`, which gates no visual
1275
+ job. So #419's nav item changed the chrome of every page with Visual
1276
+ Snapshots SKIPPED on both the PR and the main push, and the drift
1277
+ surfaced on #420, an unrelated PR. The y626 band is now UNRECOVERABLE:
1278
+ both artifact uploads use 14-day retention (ci.yml, nightly-extended.yml)
1279
+ and every pre-#413 failing run is past that window. Recording that rather
1280
+ than leaving it open — no evidence survives to diagnose it from.
1281
+ acceptance:
1282
+ - "_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."
1283
+ - "A CI guard pins that coverage, so the gap cannot silently reopen (lint-workflows.yml)."
1284
+ - "A PR touching only _data/navigation/main.yml runs Visual Snapshots — demonstrated on the checks list, not asserted."
1285
+ - "Visual Snapshots passes on main and on a fresh branch with no source changes."
1286
+ - "Any genuinely time- or build-varying content inside the snapshot region is masked or stubbed, so the tier cannot drift again."
1287
+ - "No baseline PNG is regenerated as part of this work; any diff is diagnosed in the PR before it is blessed."
1288
+ - "The y626 band is explained, or recorded as unrecoverable with the reason. (Recorded: artifacts past 14-day retention.)"
1289
+ links: { issue: 417, pr: 425, roadmap: null }
1290
+ created: 2026-08-23
1291
+ updated: 2026-09-01
1292
+
1293
+ - id: T-046
1294
+ title: "Retire the now-dead gotoBeforeScrollSpy fixture and cover the mobile offcanvas TOC spy"
1295
+ status: open
1296
+ priority: P3
1297
+ area: tests
1298
+ risk: low
1299
+ effort: S
1300
+ source: issue
1301
+ route: test-author
1302
+ summary: >-
1303
+ Discovered while fixing the TOC scroll spy: test/visual/fixtures.js
1304
+ exports gotoBeforeScrollSpy(), which strips data-bs-spy/data-bs-target
1305
+ from <body> before Bootstrap auto-initializes so its native ScrollSpy
1306
+ cannot clear server-rendered .active classes off admin sidebar links.
1307
+ That attribute no longer exists — _layouts/root.html dropped it (and
1308
+ _layouts/default.html dropped the second copy on .bd-main) so the theme's
1309
+ own scroll-spy.js is the single owner of .active on TOC links — which
1310
+ makes the fixture a no-op that still reads as load-bearing. Replace its
1311
+ call sites in test/visual/features/admin.spec.js with the plain
1312
+ waitForJekyll/gotoOrSkip helpers and delete it, keeping the admin
1313
+ active-link assertions that motivated it. While there, add the coverage
1314
+ test/visual/features/scroll-spy.spec.js deliberately left out: the mobile
1315
+ offcanvas TOC, where .offcanvas-body rather than .bd-toc is the scroll
1316
+ container the spy keeps the active link visible in.
1317
+ acceptance:
1318
+ - "gotoBeforeScrollSpy is removed from test/visual/fixtures.js and from its call sites; no spec references it."
1319
+ - "The admin specs that used it still pass, asserting the same server-rendered active nav links."
1320
+ - "A spec exercises the TOC scroll spy below the lg breakpoint with the offcanvas TOC open, asserting the active entry tracks scrolling and stays visible inside .offcanvas-body."
1321
+ - "npm run test:smoke is green."
1322
+ links: { issue: null, pr: null, roadmap: null }
1323
+ created: 2026-09-05
1324
+ updated: 2026-09-05
1325
+
1326
+ - id: T-047
1327
+ title: "pages/_docs/features/toc.md documents a TOC API the theme does not implement"
1328
+ status: open
1329
+ priority: P3
1330
+ area: docs
1331
+ risk: low
1332
+ effort: S
1333
+ source: issue
1334
+ route: content-reviewer
1335
+ summary: >-
1336
+ Discovered while correcting the scroll-spy sections of
1337
+ pages/_docs/features/toc.md: the page's Implementation and Configuration
1338
+ halves describe a table of contents this theme does not have. "TOC
1339
+ Generation" shows a Kramdown `{{ content | toc_only }}` snippet and a
1340
+ hand-rolled `page.content | split: '<h'` extractor, neither of which the
1341
+ theme uses — the real TOC comes from _includes/content/toc.html (the
1342
+ allejo/jekyll-toc parser) invoked by _includes/navigation/sidebar-right.html.
1343
+ "Enable TOC" documents front-matter keys that nothing reads; the
1344
+ `toc.min_level` / `toc.max_level` pair in the same section was already
1345
+ corrected (grep confirms no include, layout, plugin or _config.yml
1346
+ references either key). A reader following this page cannot reproduce the
1347
+ theme's behaviour. Rewrite the two sections against the actual include,
1348
+ its parameters, and the front-matter keys that really gate the panel.
1349
+ acceptance:
1350
+ - "Every Liquid snippet in toc.md matches an include the theme actually ships, verified by path."
1351
+ - "Front-matter keys documented on the page are read somewhere in _includes, _layouts, _plugins or _config.yml."
1352
+ - "scripts/content-review.rb still scores the page in the green band."
1353
+ - "Jekyll build with the layered dev configs is green and the page's anchors keep resolving."
1354
+
1355
+ - id: T-048
1356
+ title: "Site-level theme_color re-pins --zer0-color-primary over the active skin"
1357
+ status: open
1358
+ priority: P2
1359
+ area: feat
1360
+ risk: low
1361
+ effort: S
1362
+ 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."
1364
+ acceptance:
1365
+ - "With theme_color.main set, switching skin changes --zer0-color-primary as well as --bs-primary (probe getComputedStyle on <html>)."
1366
+ - "Decide and document the precedence: skin over site theme_color (recommended) or the reverse — one rule, applied to every --zer0-color-* the mixin sets."
1367
+ - "A smoke test asserts --zer0-color-primary === --bs-primary after setSkin('neon')."
1368
+ - "before/after evidence committed"
1369
+ links: { issue: null, pr: null, roadmap: null }
1370
+ created: 2026-09-05
1371
+ updated: 2026-09-05
1372
+
1373
+ - id: T-049
1374
+ title: "test_core.sh: Liquid nested-tag check and gem-content check can never fail"
1375
+ status: open
1376
+ priority: P3
1377
+ area: tests
1378
+ risk: low
1379
+ effort: S
1380
+ source: audit
1381
+ summary: "Found while reviewing CI logs for PR #455: test/test_core.sh prints '[ERROR] Nested Liquid output tags found in _includes/navigation/sidebar-pagetree.html' on every run and still reports the Liquid validation as passed. Two defects: (1) the check is `grep -q \"{{.*{{\"`, which flags any line with two output tags — sidebar-pagetree.html:65 is `{{ _base }}{{ _section }}` inside a capture, valid Liquid — and the `return 1` sits inside a `find | while read` pipeline subshell, so it can never fail the function; (2) the gem-content check runs `tar -tzf` on the .gem, but a gem is an uncompressed tar (metadata.gz, data.tar.gz, checksums.yaml.gz), so gzip prints 'stdin: not in gzip format' four times and both branches always fall through to the 'Gem may not contain…' warnings without inspecting anything."
1382
+ acceptance:
1383
+ - "The nested-tag check only flags a genuinely nested output tag (e.g. `{{ a {{ b }} }}`), not two sibling tags on one line, and a positive match fails the test (no subshell swallowing the return)."
1384
+ - "The gem-content check lists data.tar.gz inside the .gem (`tar -xOf … data.tar.gz | tar -tzf -`) and asserts _layouts/ and assets/ are present; a gem missing them fails the test."
1385
+ - "./test/test_core.sh runs clean: no '[ERROR]' line on a passing run and no gzip/tar noise."
1386
+ links: { issue: null, pr: null, roadmap: null }
1387
+ created: 2026-09-05
1388
+ updated: 2026-09-05
1389
+
1390
+ - id: T-050
1391
+ title: "Visual Snapshots tier: LiveReload reloads the page mid-test because npm ci lands after Jekyll starts watching"
1392
+ status: open
1393
+ priority: P2
1394
+ area: tests
1395
+ risk: low
1396
+ effort: S
1397
+ source: audit
1398
+ summary: "The snapshots project shows a retried test on most CI runs — '[snapshots] › appearance-snapshot.spec.js › skin: <random> › homepage visual snapshot' fails with 'page.evaluate: Execution context was destroyed, most likely because of a navigation' and passes on retry (aqua on PR #455, mint on main 5ff38bf). Nothing on the homepage navigates; the likely trigger is Jekyll's LiveReload. test/update-snapshots.sh runs `docker compose up -d` (jekyll serve --watch --livereload, repo bind-mounted at /site) and only then starts the Playwright container, which runs `npm ci` into the same bind mount. jekyll-watch 2.2.1 registers an ignore pattern only for exclude paths that EXIST when serve starts (`next unless absolute_path.exist?` in listen_ignore_paths), and a fresh CI checkout has no node_modules/, so the thousands of files npm ci writes fire the watcher, the site regenerates, and LiveReload pushes a reload to every open page over localhost:35729 (reachable: the Playwright container uses --network host). Whichever skin is mid-step when the regeneration finishes fails; Playwright's CI retry (retries: 1) masks it, at the cost of one retry per run and a false 'flaky' signal that would hide a real regression."
1399
+ acceptance:
1400
+ - "Jekyll in the snapshot stack cannot reload the browser during a run: either node_modules/ exists before `docker compose up` (run `npm ci` first, or `mkdir -p node_modules`) or the test stack serves without --livereload (compose override or a JEKYLL_LIVERELOAD toggle)."
1401
+ - "Confirmed with the Playwright trace or `docker compose logs jekyll` showing no 'Regenerating' during a run."
1402
+ - "Ten consecutive CI runs of the snapshots project report 0 flaky tests."
1403
+ - "Root cause noted in the header of test/update-snapshots.sh so the ordering is not undone."
1404
+ links: { issue: null, pr: null, roadmap: null }
1405
+ created: 2026-09-05
1406
+ updated: 2026-09-05
1407
+
1408
+ - id: T-051
1409
+ title: "Preview-image engine: the xAI default model grok-2-image is retired — default to grok-imagine-image-2.0"
1410
+ status: open
1411
+ priority: P2
1412
+ area: infra
1413
+ risk: low
1414
+ effort: S
1415
+ source: issue
1416
+ summary: "Discovered while adding the xAI provider to the chat proxy and Site Builder (ZER0-087): scripts/lib/preview_generator.py's XAIProvider.default_model() still returns 'grok-2-image', which xAI has retired in favour of the Grok Imagine family (grok-imagine-image-2.0, per docs.x.ai release notes). A site with preview_images.provider: xai and no explicit model gets an upstream error. The Site Builder now writes preview_images.model explicitly for generated sites, so only the engine default (and the .env.example comment for IMAGE_MODEL) is stale. scripts/lib/ is CODEOWNERS-owned, so this is a human-review change."
1417
+ acceptance:
1418
+ - "XAIProvider.default_model() returns grok-imagine-image-2.0 and the provider table comment in the engine header matches."
1419
+ - "The image request keeps working with the current xAI response shape (data[0].url or b64_json)."
1420
+ - "scripts/lib/README.md / .env.example mention the new default."
1421
+ links: { issue: null, pr: null, roadmap: null }
1422
+ created: 2026-09-07
1423
+ updated: 2026-09-07