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/_layouts/root.html CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  # Features: ZER0-001, ZER0-019, ZER0-031, ZER0-060
3
3
  ---
4
- <!--
4
+ {% comment %}
5
5
  ===================================================================
6
6
  ROOT LAYOUT - Base template for the Zer0 Mistakes Jekyll Theme
7
7
  ===================================================================
@@ -29,7 +29,7 @@
29
29
  Author: Amr Abdel-Motaleb
30
30
  Copyright: 2024 - it-journey.com | @bamr87
31
31
  ===================================================================
32
- -->
32
+ {% endcomment %}
33
33
 
34
34
  <!doctype html>
35
35
  {%- comment -%}
@@ -48,119 +48,156 @@
48
48
  {%- endif -%}
49
49
  <html lang="{{ page.lang | default: site.locale | slice: 0, 2 | default: 'en' }}" class="no-js" data-bs-theme="{{ _bs_theme }}" data-color-mode-default="{{ _cm }}" data-color-mode-lock="{{ site.color_mode_lock | default: false }}" data-theme-skin="{{ site.theme_skin | default: 'air' }}" data-zer0-bg="{{ site.theme_background.enabled | default: true }}">
50
50
  <head>
51
- <!-- =============================================== -->
52
- <!-- HEAD SECTION: Meta tags, styles, and SEO setup -->
53
- <!-- =============================================== -->
54
-
55
- <!-- Core site metadata, stylesheets, and external dependencies -->
51
+ {%- comment -%} Character encoding MUST be the first thing in <head>: the HTML spec
52
+ only looks for it in the first 1024 bytes of the document, and
53
+ everything below (the no-js note, core/head.html's own banner,
54
+ Google Tag Manager, console-capture, page-views, mermaid, nanobar)
55
+ pushed it to byte ~4415 (issue #372). Past the limit the browser
56
+ stops looking and falls back to its locale default, mis-decoding
57
+ every non-ASCII byte until it re-parses — and this theme ships
58
+ UTF-8 throughout (fr/** translations, em-dashes, curly quotes).
59
+ It sits here rather than in core/head.html because that include is
60
+ not, and cannot be, the first thing in <head>.
61
+ Keep it first. Nothing may be emitted above it. {%- endcomment -%}
62
+ <meta charset="utf-8">
63
+
64
+ {% comment %} Progressive-enhancement hook: upgrade <html class="no-js"> to "js".
65
+ MUST be the first child of <head>, inline, and neither `defer` nor
66
+ `async` — it has to run before the first stylesheet is fetched or
67
+ `html.js` rules would flash. It cannot live in core/head.html:
68
+ Google Tag Manager is emitted above everything else there, so
69
+ "first in that include" is not "first in <head>".
70
+ classList.replace() is a no-op when `no-js` is absent, so this is
71
+ safe unconditionally. The served HTML keeps `no-js` on <html> so
72
+ the JS-disabled branch stays correct. {% endcomment %}
73
+ <script>document.documentElement.classList.replace("no-js", "js");</script>
74
+
75
+ {% comment %} =============================================== {% endcomment %}
76
+ {% comment %} HEAD SECTION: Meta tags, styles, and SEO setup {% endcomment %}
77
+ {% comment %} =============================================== {% endcomment %}
78
+
79
+ {% comment %} Core site metadata, stylesheets, and external dependencies {% endcomment %}
56
80
  {% include core/head.html %}
57
81
 
58
- <!-- Jekyll SEO plugin for enhanced search engine optimization -->
59
- <!-- Documentation: https://github.com/jekyll/jekyll-seo-tag -->
82
+ {% comment %} Jekyll SEO plugin for enhanced search engine optimization {% endcomment %}
83
+ {% comment %} Documentation: https://github.com/jekyll/jekyll-seo-tag {% endcomment %}
60
84
  {% seo %}
61
85
 
62
- <!-- fffuel-style SVG background config overrides -->
86
+ {% comment %} fffuel-style SVG background config overrides {% endcomment %}
63
87
  {% include components/svg-background.html zone="body" %}
64
88
  </head>
65
89
 
66
- <body class="zer0-bg-body" data-bs-spy="scroll" data-bs-target="#TableOfContents">
67
- <!-- ============================================== -->
68
- <!-- BODY SECTION: Site structure and content area -->
69
- <!-- ============================================== -->
70
-
71
- <!-- Consumer hook: first thing inside <body> (e.g. GTM noscript).
72
- Empty stub shipped by the theme; shadow _includes/custom/body-start.html to use. -->
90
+ {% comment %} No `data-bs-spy` here: the theme's own scroll spy
91
+ (assets/js/modules/navigation/scroll-spy.js) owns TOC highlighting.
92
+ Bootstrap's native ScrollSpy competed with it for the `.active` class
93
+ on `#TableOfContents a` (making the bolding jump), and also stripped
94
+ server-rendered `.active` classes off sidebar nav links. {% endcomment %}
95
+ <body class="zer0-bg-body">
96
+ {% comment %} ============================================== {% endcomment %}
97
+ {% comment %} BODY SECTION: Site structure and content area {% endcomment %}
98
+ {% comment %} ============================================== {% endcomment %}
99
+
100
+ {% comment %} Consumer hook: first thing inside <body> (e.g. GTM noscript).
101
+ Empty stub shipped by the theme; shadow _includes/custom/body-start.html to use. {% endcomment %}
73
102
  {% include custom/body-start.html %}
74
103
 
75
- <!-- Inline SVG symbols for consistent iconography throughout the site -->
76
- <!-- Page-invariant: cached once per build via jekyll-include-cache. -->
104
+ {% comment %} Inline SVG symbols for consistent iconography throughout the site {% endcomment %}
105
+ {% comment %} Page-invariant: cached once per build via jekyll-include-cache. {% endcomment %}
77
106
  {% include_cached components/svg.html %}
78
107
 
79
- <!-- Google Tag Manager body code for analytics (if configured) -->
80
- <!-- Note: Currently commented out - uncomment when GTM is needed -->
108
+ {% comment %} Google Tag Manager body code for analytics (if configured) {% endcomment %}
109
+ {% comment %} Note: Currently commented out - uncomment when GTM is needed {% endcomment %}
81
110
 
82
- <!-- Main site header with navigation, branding, and search functionality -->
111
+ {% comment %} Main site header with navigation, branding, and search functionality {% endcomment %}
83
112
  {% include core/header.html %}
84
113
 
85
- <!-- Optional unified mobile drawer (Browse / Menu / Settings tabs).
86
- Enable with `navigation.unified_mobile_drawer: true` in _config.yml. -->
114
+ {% comment %} Optional unified mobile drawer (Browse / Menu / Settings tabs).
115
+ Enable with `navigation.unified_mobile_drawer: true` in _config.yml. {% endcomment %}
87
116
  {% include navigation/unified-drawer.html %}
88
117
 
89
- <!-- Search modal (site-wide) — cached once per build PER LANGUAGE
118
+ {% comment %} Search modal (site-wide) — cached once per build PER LANGUAGE
90
119
  (the lang param keys the include-cache entry; page.lang is nil
91
- on English pages, so they all still share a single render). -->
120
+ on English pages, so they all still share a single render). {% endcomment %}
92
121
  {% include_cached components/search-modal.html lang=page.lang %}
93
122
 
94
- <!-- Keyboard shortcuts help modal (triggered by `?`) — cached per language. -->
123
+ {% comment %} Keyboard shortcuts help modal (triggered by `?`) — cached per language. {% endcomment %}
95
124
  {% include_cached components/shortcuts-modal.html lang=page.lang %}
96
125
 
97
- <!-- Settings offcanvas (Appearance / Site / Developer tabs) -->
126
+ {% comment %} Settings offcanvas (Appearance / Site / Developer tabs) {% endcomment %}
98
127
  {% include components/info-section.html %}
99
128
 
100
- <!-- Setup banner for unconfigured sites — page-invariant. -->
129
+ {% comment %} Setup banner for unconfigured sites — page-invariant. {% endcomment %}
101
130
  {% include_cached components/setup-banner.html %}
102
131
 
103
- <!-- ======================== -->
104
- <!-- MAIN CONTENT AREA -->
105
- <!-- ======================== -->
106
- <!-- Skip-link target: keep a single, consistent #main-content anchor site-wide.
132
+ {% comment %} ======================== {% endcomment %}
133
+ {% comment %} MAIN CONTENT AREA {% endcomment %}
134
+ {% comment %} ======================== {% endcomment %}
135
+ {% comment %} Skip-link target: keep a single, consistent #main-content anchor site-wide.
107
136
  This is the ONE <main> landmark per page — child layouts (default/section/
108
137
  news) must use non-landmark wrappers (<div>) inside it (issue #299) — so
109
138
  assistive tech, search engines, and AI content extractors can isolate the
110
- primary content from the surrounding header/nav/footer chrome. -->
111
- <main id="main-content">
112
- <!-- Machine-translation disclosure — renders only on generated
113
- translations (page.machine_translated); no-op elsewhere. -->
139
+ primary content from the surrounding header/nav/footer chrome.
140
+
141
+ tabindex="-1" is REQUIRED, not decorative: <main> is not natively
142
+ focusable, and Safari/WebKit (historically Firefox too) only scroll on a
143
+ fragment jump to a non-focusable container — keyboard focus stays put, so
144
+ the next Tab returns the user to the header nav they just skipped
145
+ (WCAG 2.4.1 Bypass Blocks). -1 keeps it out of the sequential Tab order
146
+ while allowing programmatic focus. The focus ring it would otherwise
147
+ paint is suppressed in _sass/utilities/_focus.scss. {% endcomment %}
148
+ <main id="main-content" tabindex="-1">
149
+ {% comment %} Machine-translation disclosure — renders only on generated
150
+ translations (page.machine_translated); no-op elsewhere. {% endcomment %}
114
151
  {%- include components/translation-notice.html -%}
115
152
  {{ content }}
116
153
  </main>
117
154
 
118
- <!-- ======================== -->
119
- <!-- FOOTER AND SCRIPTS -->
120
- <!-- ======================== -->
155
+ {% comment %} ======================== {% endcomment %}
156
+ {% comment %} FOOTER AND SCRIPTS {% endcomment %}
157
+ {% comment %} ======================== {% endcomment %}
121
158
 
122
- <!-- Site footer with links, copyright, and additional navigation.
159
+ {% comment %} Site footer with links, copyright, and additional navigation.
123
160
  Page-invariant chrome is cached once per build; the page-dependent
124
161
  FAB/local-graph tail renders fresh per page (see core/footer-fabs.html).
125
162
  Build-time values inside (e.g. the copyright year from site.time) stay
126
163
  fresh: jekyll-include-cache stores in memory only (Jekyll 3.x has no
127
164
  Jekyll::Cache) and clears on every build's :pre_render hook — it never
128
- persists to .jekyll-cache. -->
165
+ persists to .jekyll-cache. {% endcomment %}
129
166
  {%- include_cached core/footer.html lang=page.lang -%}
130
167
  {%- include core/footer-fabs.html -%}
131
168
 
132
- <!-- Consumer hook: after the theme footer, before the consent banner.
169
+ {% comment %} Consumer hook: after the theme footer, before the consent banner.
133
170
  Empty stub shipped by the theme; shadow _includes/custom/footer.html to use.
134
171
  Deliberately OUTSIDE the cached footer include so page-dependent
135
- consumer markup renders per page. -->
172
+ consumer markup renders per page. {% endcomment %}
136
173
  {%- include custom/footer.html -%}
137
174
 
138
- <!-- Privacy-compliant cookie consent banner — page-invariant. -->
175
+ {% comment %} Privacy-compliant cookie consent banner — page-invariant. {% endcomment %}
139
176
  {%- include_cached components/cookie-consent.html -%}
140
177
 
141
- <!-- AI Chat Assistant -->
178
+ {% comment %} AI Chat Assistant {% endcomment %}
142
179
  {%- include components/ai-chat.html -%}
143
180
 
144
- <!-- Page-feedback capture widget ("Improve this page" → GitHub issue) -->
181
+ {% comment %} Page-feedback capture widget ("Improve this page" → GitHub issue) {% endcomment %}
145
182
  {%- include components/page-feedback.html -%}
146
183
 
147
- <!-- JavaScript dependencies and custom scripts -->
184
+ {% comment %} JavaScript dependencies and custom scripts {% endcomment %}
148
185
  <div>
149
- <!-- Search functionality -->
150
- <!-- Note: Currently commented out - uncomment when search is implemented -->
186
+ {% comment %} Search functionality {% endcomment %}
187
+ {% comment %} Note: Currently commented out - uncomment when search is implemented {% endcomment %}
151
188
 
152
- <!-- External JavaScript libraries and custom scripts — page-invariant.
189
+ {% comment %} External JavaScript libraries and custom scripts — page-invariant.
153
190
  The "?v=" cache-buster (site.time) in js-cdn.html stays correct:
154
191
  the include cache is in-memory and reset every build (see footer
155
- note above), so v= advances on each build, never frozen across deploys. -->
192
+ note above), so v= advances on each build, never frozen across deploys. {% endcomment %}
156
193
  {% include_cached components/js-cdn.html %}
157
194
  </div>
158
195
 
159
- <!-- Analytics Integration -->
196
+ {% comment %} Analytics Integration {% endcomment %}
160
197
  {%- include analytics/posthog.html -%}
161
198
 
162
- <!-- Consumer hook: last thing before </body> (site scripts, widgets).
163
- Empty stub shipped by the theme; shadow _includes/custom/body-end.html to use. -->
199
+ {% comment %} Consumer hook: last thing before </body> (site scripts, widgets).
200
+ Empty stub shipped by the theme; shadow _includes/custom/body-end.html to use. {% endcomment %}
164
201
  {%- include custom/body-end.html -%}
165
202
  </body>
166
203
  </html>
@@ -2,7 +2,7 @@
2
2
  # Features: ZER0-019, ZER0-063
3
3
  layout: root
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  SECTION LAYOUT - News Section with Collapsible Sub-Category Sidebar
8
8
  ===================================================================
@@ -40,7 +40,7 @@ layout: root
40
40
  - Bootstrap 5 grid, card, and offcanvas components
41
41
  - Bootstrap Icons
42
42
  ===================================================================
43
- -->
43
+ {% endcomment %}
44
44
 
45
45
  {% comment %} ================================ {% endcomment %}
46
46
  {% comment %} SECTION DISCOVERY LOGIC {% endcomment %}
@@ -112,9 +112,9 @@ layout: root
112
112
  {% endfor %}
113
113
  {% assign sub_categories = sub_categories | sort %}
114
114
 
115
- <!-- ================================ -->
116
- <!-- SECTION HEADER -->
117
- <!-- ================================ -->
115
+ {% comment %} ================================ {% endcomment %}
116
+ {% comment %} SECTION HEADER {% endcomment %}
117
+ {% comment %} ================================ {% endcomment %}
118
118
  <div class="container-fluid bg-dark text-white py-3 mb-0">
119
119
  <div class="container-fluid px-4">
120
120
  <div class="row align-items-center">
@@ -147,7 +147,7 @@ layout: root
147
147
  </div>
148
148
  </div>
149
149
 
150
- <!-- Sub-navigation for other sections -->
150
+ {% comment %} Sub-navigation for other sections {% endcomment %}
151
151
  <div class="container-fluid bg-body-tertiary border-bottom py-2 mb-4">
152
152
  <div class="container-fluid px-4">
153
153
  <nav class="nav nav-pills flex-nowrap overflow-auto" style="scrollbar-width: none;">
@@ -175,26 +175,27 @@ layout: root
175
175
  <div class="container-fluid px-4">
176
176
  <div class="row g-lg-4 section-layout">
177
177
 
178
- <!-- ================================ -->
179
- <!-- FIXED SIDEBAR COMPONENT -->
180
- <!-- ================================ -->
178
+ {% comment %} ================================ {% endcomment %}
179
+ {% comment %} FIXED SIDEBAR COMPONENT {% endcomment %}
180
+ {% comment %} ================================ {% endcomment %}
181
181
  <div class="col-lg-3 col-xl-2 section-layout-sidebar">
182
182
  {% include navigation/section-sidebar.html
183
183
  section_posts=section_posts
184
184
  sub_categories=sub_categories
185
- page_title=page.title
186
- sidebar_id="sectionSidebar"
185
+ page_title=page.title
186
+ sidebar_id="sectionSidebar"
187
+ section_style=section_style
187
188
  %}
188
189
  </div>
189
190
 
190
- <!-- ================================ -->
191
- <!-- MAIN CONTENT AREA -->
192
- <!-- ================================ -->
193
- <!-- Not a <main>: root.html already provides the single <main id="main-content">
194
- landmark; a nested <main> here would duplicate it (issue #299). -->
191
+ {% comment %} ================================ {% endcomment %}
192
+ {% comment %} MAIN CONTENT AREA {% endcomment %}
193
+ {% comment %} ================================ {% endcomment %}
194
+ {% comment %} Not a <main>: root.html already provides the single <main id="main-content">
195
+ landmark; a nested <main> here would duplicate it (issue #299). {% endcomment %}
195
196
  <div class="col-lg-9 col-xl-10 section-layout-main">
196
197
 
197
- <!-- Section Header -->
198
+ {% comment %} Section Header {% endcomment %}
198
199
  <header class="section-header mb-4">
199
200
  <div class="d-flex align-items-center mb-3">
200
201
  {% if page.icon %}
@@ -222,7 +223,7 @@ layout: root
222
223
  {% comment %} ================================ {% endcomment %}
223
224
  {% if section_style == "magazine" %}
224
225
 
225
- <!-- Hero Section -->
226
+ {% comment %} Hero Section {% endcomment %}
226
227
  {% assign featured_in_section = section_posts | where: "featured", true | first %}
227
228
  {% unless featured_in_section %}
228
229
  {% assign featured_in_section = section_posts | first %}
@@ -273,7 +274,7 @@ layout: root
273
274
  </div>
274
275
  </section>
275
276
 
276
- <!-- Sub-Category Sections -->
277
+ {% comment %} Sub-Category Sections {% endcomment %}
277
278
  {% for subcat in sub_categories limit: 5 %}
278
279
  {% assign subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
279
280
  {% assign subcat_posts_filtered = "" | split: "" %}
@@ -339,7 +340,7 @@ layout: root
339
340
  {% endif %}
340
341
  {% endfor %}
341
342
 
342
- <!-- All Posts Section -->
343
+ {% comment %} All Posts Section {% endcomment %}
343
344
  <section id="all-posts" class="all-posts-section mb-5 scroll-mt-5">
344
345
  <div class="d-flex justify-content-between align-items-center mb-4">
345
346
  <h3 class="h4 mb-0">
@@ -403,7 +404,7 @@ layout: root
403
404
  {% comment %} ================================ {% endcomment %}
404
405
  {% elsif section_style == "grid" %}
405
406
 
406
- <!-- Topic Filter Pills -->
407
+ {% comment %} Topic Filter Pills {% endcomment %}
407
408
  <div class="topic-filters mb-4">
408
409
  <div class="d-flex flex-wrap gap-2 align-items-center">
409
410
  <span class="text-muted me-2">Filter:</span>
@@ -419,7 +420,17 @@ layout: root
419
420
  <section id="all-posts" class="grid-section mb-5 scroll-mt-5">
420
421
  <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
421
422
  {% for gpost in section_posts %}
422
- <div class="col" data-tags="{{ gpost.tags | join: ' ' | slugify }}">
423
+ {%- comment -%}
424
+ Slugify each tag separately and separate with a space. Joining
425
+ first and slugifying the result turned the separators into
426
+ hyphens, making a tag boundary indistinguishable from a hyphen
427
+ inside a slug ("edge-ai"), which broke filtering.
428
+ The trailing space is deliberate: guarding the separator with a
429
+ last-iteration conditional gives that conditional a
430
+ whitespace-only body, which Liquid treats as a blank block and
431
+ renders as nothing — silently gluing the tags together.
432
+ {%- endcomment -%}
433
+ <div class="col" data-tags="{% for t in gpost.tags %}{{ t | slugify }} {% endfor %}">
423
434
  <div class="card h-100 border-0 shadow-sm hover-lift">
424
435
  <div class="position-relative">
425
436
  {% if gpost.breaking %}
@@ -482,7 +493,8 @@ layout: root
482
493
 
483
494
  <section id="all-posts" class="list-section mb-5 scroll-mt-5">
484
495
  {% for lpost in section_posts %}
485
- <article class="card border-0 shadow-sm mb-3 hover-lift">
496
+ {%- comment -%} Per-tag slugify + space separator — see the grid branch above. {%- endcomment -%}
497
+ <article class="card border-0 shadow-sm mb-3 hover-lift" data-tags="{% for t in lpost.tags %}{{ t | slugify }} {% endfor %}">
486
498
  <div class="row g-0">
487
499
  <div class="col-md-3">
488
500
  <a href="{{ lpost.url | relative_url }}">
@@ -528,7 +540,7 @@ layout: root
528
540
  {% endif %}
529
541
 
530
542
  {% else %}
531
- <!-- Empty state -->
543
+ {% comment %} Empty state {% endcomment %}
532
544
  <div class="text-center py-5">
533
545
  <i class="bi bi-inbox fs-1 text-muted"></i>
534
546
  <p class="text-muted mt-3">No articles in this section yet.</p>
@@ -538,9 +550,9 @@ layout: root
538
550
  </div>
539
551
  {% endif %}
540
552
 
541
- <!-- ========================== -->
542
- <!-- RELATED SECTIONS -->
543
- <!-- ========================== -->
553
+ {% comment %} ========================== {% endcomment %}
554
+ {% comment %} RELATED SECTIONS {% endcomment %}
555
+ {% comment %} ========================== {% endcomment %}
544
556
  <aside class="related-sections mt-5 pt-4 border-top">
545
557
  <h3 class="h5 mb-3">
546
558
  <i class="bi bi-compass me-2"></i>Explore Other Sections
@@ -597,17 +609,34 @@ layout: root
597
609
  btn.addEventListener('click', function() {
598
610
  const filter = this.dataset.filter;
599
611
 
600
- // Update active button
612
+ // Update active state across every filter control on the page — the
613
+ // topic pills here and the section sidebar's topic buttons both carry
614
+ // [data-filter], so they stay in sync. Only pills get the btn-* colour
615
+ // swap; the sidebar's controls are .nav-link and style themselves off
616
+ // .active alone.
601
617
  document.querySelectorAll('[data-filter]').forEach(b => {
602
- b.classList.remove('btn-primary', 'active');
603
- b.classList.add('btn-outline-secondary');
618
+ b.classList.remove('active');
619
+ if (b.classList.contains('btn')) {
620
+ b.classList.remove('btn-primary');
621
+ b.classList.add('btn-outline-secondary');
622
+ }
604
623
  });
605
- this.classList.remove('btn-outline-secondary');
606
- this.classList.add('btn-primary', 'active');
624
+ this.classList.add('active');
625
+ if (this.classList.contains('btn')) {
626
+ this.classList.remove('btn-outline-secondary');
627
+ this.classList.add('btn-primary');
628
+ }
607
629
 
608
630
  // Filter cards
631
+ // Match whole tags, not substrings. `data-tags` is a space-separated list
632
+ // of individually-slugified tags, so a slug never spans a separator and
633
+ // filtering by "ai" no longer also matches a post tagged "edge-ai".
634
+ // (This used to disagree with the sidebar's own count: the badge is built
635
+ // in Liquid with `post.tags contains subcat` — exact membership — so a
636
+ // topic showing 5 posts could reveal 6 cards when clicked.)
609
637
  document.querySelectorAll('[data-tags]').forEach(card => {
610
- if (filter === 'all' || card.dataset.tags.includes(filter)) {
638
+ const cardTags = (card.dataset.tags || '').split(' ').filter(Boolean);
639
+ if (filter === 'all' || cardTags.indexOf(filter) !== -1) {
611
640
  card.style.display = '';
612
641
  } else {
613
642
  card.style.display = 'none';
data/_layouts/setup.html CHANGED
@@ -2,7 +2,7 @@
2
2
  # Features: ZER0-019, ZER0-067
3
3
  layout: root
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  SETUP LAYOUT - Onboarding and configuration pages
8
8
  ===================================================================
@@ -24,7 +24,7 @@ layout: root
24
24
  setup_step: 2 # optional, highlights step in progress bar
25
25
  setup_total: 4 # optional, total number of steps
26
26
  ===================================================================
27
- -->
27
+ {% endcomment %}
28
28
 
29
29
  {% include components/setup-check.html %}
30
30
 
@@ -45,7 +45,7 @@ layout: root
45
45
  {% endif %}
46
46
 
47
47
  {% if page.setup_step and page.setup_total %}
48
- <!-- Progress indicator -->
48
+ {% comment %} Progress indicator {% endcomment %}
49
49
  <div class="mb-4">
50
50
  <div class="d-flex justify-content-between small text-body-secondary mb-1">
51
51
  <span>Step {{ page.setup_step }} of {{ page.setup_total }}</span>