jekyll-theme-zer0 1.29.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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +715 -2
  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 +169 -18
  7. data/_data/consumers.yml +48 -5
  8. data/_data/features.yml +146 -20
  9. data/_data/feedback_types.yml +17 -12
  10. data/_data/landing.yml +5 -2
  11. data/_data/navigation/quickstart.yml +4 -0
  12. data/_data/site_builder.yml +874 -0
  13. data/_data/theme-manifest.yml +122 -114
  14. data/_data/ui-text.yml +18 -0
  15. data/_includes/README.md +11 -1
  16. data/_includes/analytics/posthog.html +2 -2
  17. data/_includes/components/admin-links.html +2 -2
  18. data/_includes/components/admin-tabs.html +2 -2
  19. data/_includes/components/ai-chat.html +14 -11
  20. data/_includes/components/analytics-dashboard.html +8 -8
  21. data/_includes/components/author-bio.html +1 -1
  22. data/_includes/components/author-card.html +10 -2
  23. data/_includes/components/author-eeat.html +4 -4
  24. data/_includes/components/background-customizer.html +8 -8
  25. data/_includes/components/background-image.html +114 -0
  26. data/_includes/components/background-settings.html +28 -15
  27. data/_includes/components/collection-manager.html +5 -5
  28. data/_includes/components/component-showcase.html +13 -13
  29. data/_includes/components/config-editor.html +12 -12
  30. data/_includes/components/config-viewer.html +8 -8
  31. data/_includes/components/cookie-consent.html +11 -11
  32. data/_includes/components/cta-button.html +7 -2
  33. data/_includes/components/dev-shortcuts.html +7 -7
  34. data/_includes/components/env-dashboard.html +8 -8
  35. data/_includes/components/env-switcher.html +9 -9
  36. data/_includes/components/feature-card.html +2 -2
  37. data/_includes/components/halfmoon.html +2 -2
  38. data/_includes/components/info-section.html +36 -36
  39. data/_includes/components/js-cdn.html +15 -15
  40. data/_includes/components/language-toggle.html +4 -4
  41. data/_includes/components/mermaid.html +72 -435
  42. data/_includes/components/nanobar.html +5 -5
  43. data/_includes/components/nav-editor.html +2 -2
  44. data/_includes/components/nav-export.html +2 -2
  45. data/_includes/components/nav-overview.html +2 -2
  46. data/_includes/components/page-feedback.html +45 -30
  47. data/_includes/components/page-views-init.html +1 -1
  48. data/_includes/components/post-card.html +22 -22
  49. data/_includes/components/post-type-badge.html +2 -2
  50. data/_includes/components/powered-by.html +2 -2
  51. data/_includes/components/preview-image.html +6 -0
  52. data/_includes/components/quick-index.html +2 -2
  53. data/_includes/components/search-modal.html +27 -2
  54. data/_includes/components/searchbar.html +2 -2
  55. data/_includes/components/svg-background.html +2 -2
  56. data/_includes/components/theme-customizer.html +2 -2
  57. data/_includes/components/theme-info.html +6 -6
  58. data/_includes/components/theme-preview-gallery.html +22 -22
  59. data/_includes/content/giscus.html +2 -2
  60. data/_includes/content/intro.html +8 -8
  61. data/_includes/content/jsonld-faq.html +2 -2
  62. data/_includes/content/jsonld-software.html +2 -2
  63. data/_includes/content/seo.html +4 -4
  64. data/_includes/content/sitemap.html +27 -27
  65. data/_includes/content/toc.html +183 -183
  66. data/_includes/core/branding.html +6 -6
  67. data/_includes/core/console-capture.html +32 -74
  68. data/_includes/core/favicon.html +49 -7
  69. data/_includes/core/footer-fabs.html +17 -3
  70. data/_includes/core/footer.html +31 -18
  71. data/_includes/core/head.html +102 -90
  72. data/_includes/core/header.html +71 -52
  73. data/_includes/docs/bootstrap-docs.html +8 -8
  74. data/_includes/landing/landing-install-cards.html +2 -2
  75. data/_includes/landing/landing-quick-links.html +1 -1
  76. data/_includes/navigation/admin-nav.html +2 -2
  77. data/_includes/navigation/nav-tree.html +8 -8
  78. data/_includes/navigation/navbar.html +12 -12
  79. data/_includes/navigation/section-sidebar.html +16 -16
  80. data/_includes/navigation/sidebar-config.html +36 -2
  81. data/_includes/navigation/sidebar-left.html +15 -15
  82. data/_includes/navigation/sidebar-right.html +6 -6
  83. data/_includes/obsidian/full-graph.html +2 -2
  84. data/_includes/setup/claude-session.html +72 -0
  85. data/_includes/setup/prereq-checklist.html +90 -0
  86. data/_includes/setup/wizard.html +906 -291
  87. data/_includes/stats/stats-categories.html +8 -8
  88. data/_includes/stats/stats-header.html +14 -14
  89. data/_includes/stats/stats-metrics.html +12 -12
  90. data/_includes/stats/stats-no-data.html +12 -12
  91. data/_includes/stats/stats-overview.html +6 -6
  92. data/_includes/stats/stats-tags.html +8 -8
  93. data/_layouts/404.html +38 -24
  94. data/_layouts/admin.html +22 -22
  95. data/_layouts/article.html +39 -34
  96. data/_layouts/author.html +20 -20
  97. data/_layouts/authors.html +2 -2
  98. data/_layouts/book-abc.html +12 -12
  99. data/_layouts/book-story.html +15 -15
  100. data/_layouts/book.html +12 -12
  101. data/_layouts/collection.html +33 -33
  102. data/_layouts/cookbook.html +12 -12
  103. data/_layouts/default.html +27 -24
  104. data/_layouts/home.html +23 -23
  105. data/_layouts/index.html +10 -10
  106. data/_layouts/landing.html +17 -17
  107. data/_layouts/news.html +44 -44
  108. data/_layouts/note.html +38 -38
  109. data/_layouts/notebook.html +34 -34
  110. data/_layouts/recipe.html +24 -24
  111. data/_layouts/root.html +73 -55
  112. data/_layouts/section.html +23 -23
  113. data/_layouts/setup.html +3 -3
  114. data/_layouts/sitemap-collection.html +49 -49
  115. data/_layouts/stats.html +40 -40
  116. data/_layouts/tag.html +12 -12
  117. data/_layouts/welcome.html +21 -21
  118. data/_sass/components/_mermaid.scss +375 -0
  119. data/_sass/components/_setup-wizard.scss +569 -40
  120. data/_sass/core/_navbar.scss +11 -31
  121. data/_sass/layouts/_navbar-extras.scss +14 -4
  122. data/assets/css/main.scss +1 -0
  123. data/assets/js/ai-chat.js +47 -5
  124. data/assets/js/fleet-feedback-capture.js +124 -0
  125. data/assets/js/fleet-feedback.js +853 -0
  126. data/assets/js/mermaid-diagrams.js +1267 -0
  127. data/assets/js/modules/navigation/config.js +9 -6
  128. data/assets/js/modules/navigation/scroll-spy.js +315 -80
  129. data/assets/js/modules/theme/appearance.js +8 -2
  130. data/assets/js/obsidian-wiki-links.js +8 -3
  131. data/assets/js/page-feedback.js +125 -192
  132. data/assets/js/search-modal.js +26 -0
  133. data/assets/js/setup-wizard.js +2112 -361
  134. data/assets/js/site-builder.js +1834 -0
  135. data/assets/js/ui-enhancements.js +11 -3
  136. data/scripts/README.md +15 -0
  137. data/scripts/ai/README.md +38 -0
  138. data/scripts/ai/api_call.rb +124 -0
  139. data/scripts/ai/usage.rb +314 -0
  140. data/scripts/ai/usage_report.rb +225 -0
  141. data/scripts/ci/test_visual_evidence_autogen.py +341 -0
  142. data/scripts/ci/visual_evidence_autogen.py +1060 -0
  143. data/scripts/content-review.rb +20 -1
  144. data/scripts/test/integration/mermaid +22 -8
  145. data/scripts/test/lib/run_tests.sh +1 -0
  146. data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
  147. data/scripts/translate.rb +23 -1
  148. metadata +19 -2
@@ -1,5 +1,5 @@
1
1
  {% comment %} Feature: ZER0-046 {% endcomment %}
2
- <!--
2
+ {% comment %}
3
3
  ===================================================================
4
4
  FOOTER - Comprehensive Site Footer with Branding & Navigation
5
5
  ===================================================================
@@ -33,12 +33,12 @@
33
33
  - Guard optional fields (address/phone/email) to avoid empty output
34
34
  - Use relative_url for internal links to respect baseurl
35
35
  ===================================================================
36
- -->
36
+ {% endcomment %}
37
37
 
38
38
  {%- include core/i18n.html lang=include.lang -%}
39
39
  <footer class="bd-footer border-top" role="contentinfo">
40
40
  {% assign footer_page_urls = site.html_pages | map: "url" | join: "|" | prepend: "|" | append: "|" %}
41
- <!-- Powered by Row -->
41
+ {% comment %} Powered by Row {% endcomment %}
42
42
  <div class="container-xl footer-powered-by">
43
43
  <p class="footer-powered-by-copyright text-center mb-0">
44
44
  &copy; {{ site.time | date: "%Y" }} {{ site.name | default: site.title }}
@@ -110,12 +110,12 @@
110
110
  </div>
111
111
  </div>
112
112
 
113
- <!-- Branding and Navigation Block — full-width dark background -->
113
+ {% comment %} Branding and Navigation Block — full-width dark background {% endcomment %}
114
114
  <div class="bg-dark text-light footer-dark-block">
115
115
  <div class="container-xl">
116
- <!-- Top: Site info + Quick Links + Social -->
116
+ {% comment %} Top: Site info + Quick Links + Social {% endcomment %}
117
117
  <div class="row mb-3 mb-lg-4 g-2 g-lg-3 footer-nav-columns justify-content-center">
118
- <!-- Site Info -->
118
+ {% comment %} Site Info {% endcomment %}
119
119
  <div class="col-12 col-md-3 col-lg-3 mb-2 mb-lg-3 order-0 order-md-1 order-lg-1">
120
120
  <h2 class="footer-section-title text-uppercase h6 mb-0">{{ site.title }}</h2>
121
121
  {% if site.address %}<p class="mb-1">{{ site.address }}</p>{% endif %}
@@ -123,12 +123,12 @@
123
123
  {% if site.email %}<p class="mb-0"><a href="mailto:{{ site.email }}" class="text-light text-decoration-none">{{ site.email }}</a></p>{% endif %}
124
124
  </div>
125
125
 
126
- <!-- Quick Links
126
+ {% comment %} Quick Links
127
127
  Resolution order:
128
128
  1. site.footer_quick_links (array of {label, url}) — explicit override
129
129
  2. Auto-detection: only render links whose target pages
130
130
  actually exist in the build. Prevents 404s on bare-minimum
131
- sites that haven't created /about/, /services/, etc. -->
131
+ sites that haven't created /about/, /services/, etc. {% endcomment %}
132
132
  <div class="col-6 col-md-3 col-lg-3 mb-2 mb-lg-3 order-2 order-md-2 order-lg-2">
133
133
  <h2 class="footer-section-title text-uppercase h6 mb-0">Quick Links</h2>
134
134
  <ul class="list-unstyled" aria-label="Footer quick links">
@@ -171,7 +171,7 @@
171
171
  </ul>
172
172
  </div>
173
173
 
174
- <!-- Latest Posts (dynamic) -->
174
+ {% comment %} Latest Posts (dynamic) {% endcomment %}
175
175
  {% assign posts_count = site.posts | size %}
176
176
  {% if posts_count and posts_count > 0 %}
177
177
  <div class="col-12 col-md-3 col-lg-3 mb-2 mb-lg-3 order-4 order-md-3 order-lg-3">
@@ -186,7 +186,7 @@
186
186
  </div>
187
187
  {% endif %}
188
188
 
189
- <!-- Social & RSS -->
189
+ {% comment %} Social & RSS {% endcomment %}
190
190
  <div class="col-6 col-md-3 col-lg-3 mb-2 mb-lg-3 order-3 order-md-4 order-lg-4">
191
191
  <h2 class="footer-section-title text-uppercase h6 mb-0">Follow Us</h2>
192
192
  <ul class="list-inline mb-0" aria-label="Social links">
@@ -194,7 +194,17 @@
194
194
  {% if link.url %}
195
195
  <li class="list-inline-item">
196
196
  {% capture link_icon_classes %}{{ site.default_icon }} {{ link.icon | default: "bi-link-45deg" }}{% endcapture %}
197
- <a href="{{ link.url }}" rel="nofollow" class="text-light text-decoration-none">
197
+ {%- comment -%}
198
+ aria-label is load-bearing below the md breakpoint (issue
199
+ #435): the icon is aria-hidden and the label span is
200
+ d-none there, so without it the link had NO accessible
201
+ name at all on mobile — a screen reader announced bare
202
+ "link". It matches the visible text exactly, so the
203
+ accessible name still contains the visible label at md+
204
+ (WCAG 2.5.3 Label in Name).
205
+ {%- endcomment -%}
206
+ <a href="{{ link.url }}" rel="nofollow" class="text-light text-decoration-none"
207
+ aria-label="{{ link.label }}">
198
208
  <i class="{{ link_icon_classes }}" aria-hidden="true"></i>
199
209
  <span class="d-none d-md-inline">{{ link.label }}</span>
200
210
  </a>
@@ -202,16 +212,19 @@
202
212
  {% endif %}
203
213
  {% endfor %}
204
214
  <li class="list-inline-item">
205
- <a class="text-light text-decoration-none" href="{{ '/feed.xml' | relative_url }}">
215
+ {%- comment -%} Same as the social links above (#435). {%- endcomment -%}
216
+ {%- assign _feed_label = ui.feed_label | default: "Feed" -%}
217
+ <a class="text-light text-decoration-none" href="{{ '/feed.xml' | relative_url }}"
218
+ aria-label="{{ _feed_label }}">
206
219
  <i class="{{ site.default_icon }} bi-rss" aria-hidden="true"></i>
207
- <span class="d-none d-md-inline">{{ ui.feed_label | default: "Feed" }}</span>
220
+ <span class="d-none d-md-inline">{{ _feed_label }}</span>
208
221
  </a>
209
222
  </li>
210
223
  </ul>
211
224
  </div>
212
225
  </div>
213
226
 
214
- <!-- Middle: Subscribe + Policies -->
227
+ {% comment %} Middle: Subscribe + Policies {% endcomment %}
215
228
  <div class="row mb-3 mb-lg-4 align-items-center justify-content-center g-2 footer-subscribe-row">
216
229
  <div class="col-12 col-md-8 col-lg-8">
217
230
  <form class="row g-2 footer-subscribe-form" aria-label="Newsletter signup" onsubmit="return false;">
@@ -249,7 +262,7 @@
249
262
  </div>
250
263
  </div>
251
264
 
252
- <!-- Bottom: Copyright -->
265
+ {% comment %} Bottom: Copyright {% endcomment %}
253
266
  <div class="text-center">
254
267
  <p class="mb-0">&copy; {{ site.time | date: "%Y" }} {{ site.title | default: site.name }}. All Rights Reserved.</p>
255
268
  </div>
@@ -265,9 +278,9 @@
265
278
  immediately after </footer> is visually identical to nesting them inside.
266
279
  {%- endcomment -%}
267
280
 
268
- <!-- Back to Top Button (controlled by assets/js/back-to-top.js) -->
269
- <!-- Positioning/stacking handled by _sass/components/_back-to-top.scss
270
- via the FAB tokens (bottom-up: back-to-top → chat toggle → TOC FAB) -->
281
+ {% comment %} Back to Top Button (controlled by assets/js/back-to-top.js) {% endcomment %}
282
+ {% comment %} Positioning/stacking handled by _sass/components/_back-to-top.scss
283
+ via the FAB tokens (bottom-up: back-to-top → chat toggle → TOC FAB) {% endcomment %}
271
284
  <a
272
285
  id="backToTopBtn"
273
286
  href="#"
@@ -1,5 +1,5 @@
1
1
  {% comment %} Feature: ZER0-001 {% endcomment %}
2
- <!--
2
+ {% comment %}
3
3
  ===================================================================
4
4
  HEAD - HTML Document Head Section
5
5
  ===================================================================
@@ -26,23 +26,23 @@
26
26
  - MathJax loaded asynchronously to prevent render blocking
27
27
  - Nanobar provides visual loading feedback (see components/nanobar.html)
28
28
  ===================================================================
29
- -->
29
+ {% endcomment %}
30
30
 
31
- <!-- ================================ -->
32
- <!-- ANALYTICS AND TRACKING -->
33
- <!-- ================================ -->
34
- <!-- Google Tag Manager - Must be in head section for proper tracking -->
35
- <!-- Only load in production builds (JEKYLL_ENV=production); the include also guards against dev hostnames -->
31
+ {% comment %} ================================ {% endcomment %}
32
+ {% comment %} ANALYTICS AND TRACKING {% endcomment %}
33
+ {% comment %} ================================ {% endcomment %}
34
+ {% comment %} Google Tag Manager - Must be in head section for proper tracking {% endcomment %}
35
+ {% comment %} Only load in production builds (JEKYLL_ENV=production); the include also guards against dev hostnames {% endcomment %}
36
36
  {% if jekyll.environment == "production" %}{% include analytics/google-tag-manager-head.html %}{% endif %}
37
37
 
38
- <!-- Page-feedback: early console/error capture — must run before the theme
39
- scripts below so a report can include the logs that led up to it. -->
38
+ {% comment %} Page-feedback: early console/error capture — must run before the theme
39
+ scripts below so a report can include the logs that led up to it. {% endcomment %}
40
40
  {% include core/console-capture.html %}
41
41
 
42
- <!-- ================================ -->
43
- <!-- JAVASCRIPT LIBRARIES -->
44
- <!-- ================================ -->
45
- <!-- Custom theme JavaScript - Deferred for performance -->
42
+ {% comment %} ================================ {% endcomment %}
43
+ {% comment %} JAVASCRIPT LIBRARIES {% endcomment %}
44
+ {% comment %} ================================ {% endcomment %}
45
+ {% comment %} Custom theme JavaScript - Deferred for performance {% endcomment %}
46
46
  <script defer src="{{ '/assets/js/auto-hide-nav.js' | relative_url }}"></script>
47
47
  <script defer src="{{ '/assets/js/back-to-top.js' | relative_url }}"></script>
48
48
  <script defer src="{{ '/assets/js/halfmoon.js' | relative_url }}"></script>
@@ -51,34 +51,34 @@
51
51
  <script defer src="{{ '/assets/js/table-copy.js' | relative_url }}"></script>
52
52
  <script defer src="{{ '/assets/js/ui-enhancements.js' | relative_url }}"></script>
53
53
 
54
- <!-- Page-view counter — injects its config and loads assets/js/page-views.js.
54
+ {% comment %} Page-view counter — injects its config and loads assets/js/page-views.js.
55
55
  Renders nothing on its own; components/page-views.html places the badge.
56
- Gated on site.page_views.enabled inside the include. -->
56
+ Gated on site.page_views.enabled inside the include. {% endcomment %}
57
57
  {% include components/page-views-init.html %}
58
58
 
59
- <!-- ================================ -->
60
- <!-- THIRD PARTY INTEGRATIONS -->
61
- <!-- ================================ -->
59
+ {% comment %} ================================ {% endcomment %}
60
+ {% comment %} THIRD PARTY INTEGRATIONS {% endcomment %}
61
+ {% comment %} ================================ {% endcomment %}
62
62
 
63
- <!-- Recipe scaling & unit conversion - Only on pages that render recipe
63
+ {% comment %} Recipe scaling & unit conversion - Only on pages that render recipe
64
64
  quantities: the `recipe` layout, or any page that opts in with
65
- `recipe_tools: true` (e.g. a post embedding components/recipe-*.html). -->
65
+ `recipe_tools: true` (e.g. a post embedding components/recipe-*.html). {% endcomment %}
66
66
  {% if page.layout == "recipe" or page.recipe_tools %}
67
67
  <script defer src="{{ '/assets/js/recipe-scaler.js' | relative_url }}"></script>
68
68
  {% endif %}
69
69
 
70
- <!-- Mermaid Diagrams - Conditional loading based on page front matter -->
70
+ {% comment %} Mermaid Diagrams - Conditional loading based on page front matter {% endcomment %}
71
71
  {% if page.mermaid %}
72
72
  {% include components/mermaid.html %}
73
73
  {% endif %}
74
74
 
75
- <!-- Nano Progress Bar - Visual loading indicator (config-driven) — page-invariant, cached. -->
75
+ {% comment %} Nano Progress Bar - Visual loading indicator (config-driven) — page-invariant, cached. {% endcomment %}
76
76
  {% include_cached components/nanobar.html %}
77
77
 
78
- <!-- MathJax - Conditional loading based on page front matter (like Mermaid) -->
78
+ {% comment %} MathJax - Conditional loading based on page front matter (like Mermaid) {% endcomment %}
79
79
  {% if page.mathjax %}
80
- <!-- MathJax 3 configuration — must appear before the script tag.
81
- Enables $...$ inline math (off by default in MathJax 3). -->
80
+ {% comment %} MathJax 3 configuration — must appear before the script tag.
81
+ Enables $...$ inline math (off by default in MathJax 3). {% endcomment %}
82
82
  <script>
83
83
  window.MathJax = {
84
84
  tex: {
@@ -91,33 +91,45 @@ window.MathJax = {
91
91
  <script id="MathJax-script" async src="{{ '/assets/vendor/mathjax/es5/tex-mml-chtml.js' | relative_url }}"></script>
92
92
  {% endif %}
93
93
 
94
- <!-- ================================ -->
95
- <!-- HTML DOCUMENT META DATA -->
96
- <!-- ================================ -->
97
- <!-- Essential meta tags for proper document rendering and mobile support -->
98
- <meta charset="utf-8">
94
+ {% comment %} ================================ {% endcomment %}
95
+ {% comment %} HTML DOCUMENT META DATA {% endcomment %}
96
+ {% comment %} ================================ {% endcomment %}
97
+ {%- comment -%} NOTE: the charset meta is NOT here — it lives at the very top
98
+ of _layouts/root.html so it lands inside the spec's 1024-byte window
99
+ (issue #372). Do not re-add it here. {%- endcomment -%}
99
100
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
100
101
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
101
102
 
102
- <!-- Favicon + browser identity (config-driven via `favicon:`; explicit
103
- /favicon.ico fallback so baseurl deployments resolve) — page-invariant, cached. -->
103
+ {% comment %} Favicon + browser identity (config-driven via `favicon:`; explicit
104
+ /favicon.ico fallback so baseurl deployments resolve) — page-invariant, cached. {% endcomment %}
104
105
  {% include_cached core/favicon.html %}
105
106
 
106
- <!-- ================================ -->
107
- <!-- SEO SUPPLEMENT -->
108
- <!-- ================================ -->
109
- <!-- Additional SEO tags not covered by jekyll-seo-tag plugin ( in root.html).
110
- Outputs custom og:image with assets_prefix normalisation and non-Google site verification. -->
107
+ {%- comment -%} Feed autodiscovery. jekyll-feed generates /feed.xml but emits
108
+ no link element unless feed_meta is called, so the feed was undiscoverable
109
+ to readers and crawlers even though the footer links it (issue #371).
110
+ Written out by hand rather than via feed_meta so the theme does not
111
+ hard-fail with "Unknown tag" on a consumer site that has not enabled the
112
+ plugin; the path matches the footer's own /feed.xml link. {%- endcomment -%}
113
+ {%- assign _feed_path = site.feed.path | default: 'feed.xml' -%}
114
+ <link rel="alternate" type="application/atom+xml"
115
+ title="{{ site.title | default: site.name | escape }}"
116
+ href="{{ _feed_path | relative_url }}">
117
+
118
+ {% comment %} ================================ {% endcomment %}
119
+ {% comment %} SEO SUPPLEMENT {% endcomment %}
120
+ {% comment %} ================================ {% endcomment %}
121
+ {% comment %} Additional SEO tags not covered by jekyll-seo-tag plugin ( in root.html).
122
+ Outputs custom og:image with assets_prefix normalisation and non-Google site verification. {% endcomment %}
111
123
  {% include content/seo.html %}
112
124
 
113
- <!-- hreflang alternates for machine-generated translations (no-op when the
114
- page has none) — see _data/i18n/manifest.yml + scripts/translate.rb -->
125
+ {% comment %} hreflang alternates for machine-generated translations (no-op when the
126
+ page has none) — see _data/i18n/manifest.yml + scripts/translate.rb {% endcomment %}
115
127
  {% include core/hreflang.html %}
116
128
 
117
- <!-- ========================== -->
118
- <!-- STRUCTURED DATA MARKUP -->
119
- <!-- ========================== -->
120
- <!-- Enhanced meta data for rich snippets and social media -->
129
+ {% comment %} ========================== {% endcomment %}
130
+ {% comment %} STRUCTURED DATA MARKUP {% endcomment %}
131
+ {% comment %} ========================== {% endcomment %}
132
+ {% comment %} Enhanced meta data for rich snippets and social media {% endcomment %}
121
133
  {% if page.title %}
122
134
  <meta itemprop="headline" content="{{ page.title | markdownify | strip_html | strip_newlines | escape_once }}">
123
135
  {% endif %}
@@ -135,84 +147,84 @@ window.MathJax = {
135
147
  <meta itemprop="dateModified" content="{{ page.lastmod | date_to_xmlschema }}">
136
148
  {% endif %}
137
149
 
138
- <!-- JSON-LD SoftwareApplication schema (homepage only) -->
150
+ {% comment %} JSON-LD SoftwareApplication schema (homepage only) {% endcomment %}
139
151
  {% include content/jsonld-software.html %}
140
152
 
141
- <!-- JSON-LD FAQ schema (pages with faq_items front matter) -->
153
+ {% comment %} JSON-LD FAQ schema (pages with faq_items front matter) {% endcomment %}
142
154
  {% include content/jsonld-faq.html %}
143
155
 
144
- <!-- ========================== -->
145
- <!-- ANALYTICS INTEGRATION -->
146
- <!-- ========================== -->
147
- <!-- Google Analytics tracking - Configured in _config.yml -->
148
- <!-- Only load in production AND when an ID is configured; the include also guards against dev hostnames -->
156
+ {% comment %} ========================== {% endcomment %}
157
+ {% comment %} ANALYTICS INTEGRATION {% endcomment %}
158
+ {% comment %} ========================== {% endcomment %}
159
+ {% comment %} Google Analytics tracking - Configured in _config.yml {% endcomment %}
160
+ {% comment %} Only load in production AND when an ID is configured; the include also guards against dev hostnames {% endcomment %}
149
161
  {% if jekyll.environment == "production" and site.google_analytics %}{% include analytics/google-analytics.html %}{% endif %}
150
162
 
151
- <!-- PostHog analytics - Configured via the `posthog:` block in _config.yml -->
152
- <!-- Only load in production AND when explicitly enabled; the include itself also respects Do Not Track -->
163
+ {% comment %} PostHog analytics - Configured via the `posthog:` block in _config.yml {% endcomment %}
164
+ {% comment %} Only load in production AND when explicitly enabled; the include itself also respects Do Not Track {% endcomment %}
153
165
  {% if jekyll.environment == "production" and site.posthog.enabled %}{% include analytics/posthog.html %}{% endif %}
154
166
 
155
167
 
156
- <!-- ================================ -->
157
- <!-- CSS FRAMEWORKS AND LIBRARIES -->
158
- <!-- ================================ -->
168
+ {% comment %} ================================ {% endcomment %}
169
+ {% comment %} CSS FRAMEWORKS AND LIBRARIES {% endcomment %}
170
+ {% comment %} ================================ {% endcomment %}
159
171
 
160
- <!-- ========================== -->
161
- <!-- COLOR MODE INIT (FOUC FIX) -->
162
- <!-- ========================== -->
163
- <!-- Must run BEFORE Bootstrap CSS so the correct data-bs-theme is applied
172
+ {% comment %} ========================== {% endcomment %}
173
+ {% comment %} COLOR MODE INIT (FOUC FIX) {% endcomment %}
174
+ {% comment %} ========================== {% endcomment %}
175
+ {% comment %} Must run BEFORE Bootstrap CSS so the correct data-bs-theme is applied
164
176
  when [data-bs-theme=dark/light] selectors are first evaluated.
165
177
  Reads site.color_mode_default (via data-color-mode-default attr on <html>)
166
- and falls back to localStorage["theme"] for the user's explicit preference. -->
178
+ and falls back to localStorage["theme"] for the user's explicit preference. {% endcomment %}
167
179
  {% include core/color-mode-init.html %}
168
180
 
169
- <!-- ========================== -->
170
- <!-- BOOTSTRAP 5 FRAMEWORK -->
171
- <!-- ========================== -->
172
- <!-- Bootstrap 5.3.3 - bundled locally (GitHub Pages–safe; refresh via scripts/vendor-install.sh) -->
181
+ {% comment %} ========================== {% endcomment %}
182
+ {% comment %} BOOTSTRAP 5 FRAMEWORK {% endcomment %}
183
+ {% comment %} ========================== {% endcomment %}
184
+ {% comment %} Bootstrap 5.3.3 - bundled locally (GitHub Pages–safe; refresh via scripts/vendor-install.sh) {% endcomment %}
173
185
  <link href="{{ '/assets/vendor/bootstrap/css/bootstrap.min.css' | relative_url }}" rel="stylesheet">
174
186
 
175
- <!-- ========================== -->
176
- <!-- ICON LIBRARIES -->
177
- <!-- ========================== -->
178
- <!-- Bootstrap Icons (bundled with theme; fonts use relative paths in CSS) -->
187
+ {% comment %} ========================== {% endcomment %}
188
+ {% comment %} ICON LIBRARIES {% endcomment %}
189
+ {% comment %} ========================== {% endcomment %}
190
+ {% comment %} Bootstrap Icons (bundled with theme; fonts use relative paths in CSS) {% endcomment %}
179
191
  <link rel="stylesheet" href="{{ '/assets/vendor/bootstrap-icons/font/bootstrap-icons.css' | relative_url }}">
180
192
 
181
- <!-- ========================== -->
182
- <!-- CUSTOM THEME STYLES -->
183
- <!-- ========================== -->
184
- <!-- Primary theme stylesheet - Core styling and layout (SCSS pipeline) -->
193
+ {% comment %} ========================== {% endcomment %}
194
+ {% comment %} CUSTOM THEME STYLES {% endcomment %}
195
+ {% comment %} ========================== {% endcomment %}
196
+ {% comment %} Primary theme stylesheet - Core styling and layout (SCSS pipeline) {% endcomment %}
185
197
  <link rel="stylesheet" href="{{ '/assets/css/main.css' | relative_url }}">
186
198
 
187
- <!-- ========================== -->
188
- <!-- DESIGN TOKEN OVERRIDES -->
189
- <!-- ========================== -->
190
- <!-- Inline <style> block emitted AFTER main.css so site-config `theme_color` keys
191
- and any per-user Appearance overrides win over compiled defaults. -->
199
+ {% comment %} ========================== {% endcomment %}
200
+ {% comment %} DESIGN TOKEN OVERRIDES {% endcomment %}
201
+ {% comment %} ========================== {% endcomment %}
202
+ {% comment %} Inline <style> block emitted AFTER main.css so site-config `theme_color` keys
203
+ and any per-user Appearance overrides win over compiled defaults. {% endcomment %}
192
204
  {% include core/tokens-inline.html %}
193
205
 
194
- <!-- Optional fork overrides: opt-in via `site.user_overrides: true` in _config.yml.
195
- Loaded LAST so consumer rules win over both main.css and tokens-inline. -->
206
+ {% comment %} Optional fork overrides: opt-in via `site.user_overrides: true` in _config.yml.
207
+ Loaded LAST so consumer rules win over both main.css and tokens-inline. {% endcomment %}
196
208
  {% if site.user_overrides %}
197
209
  <link rel="stylesheet" href="{{ '/assets/css/user-overrides.css' | relative_url }}">
198
210
  {% endif %}
199
211
 
200
- <!-- Statistics page specific styles -->
212
+ {% comment %} Statistics page specific styles {% endcomment %}
201
213
  {% if page.layout == 'stats' or page.permalink == '/about/stats/' %}
202
214
  <link rel="stylesheet" href="{{ '/assets/css/stats.css' | relative_url }}">
203
215
  {% endif %}
204
216
 
205
- <!-- Consumer hook: end of <head>. Inject JSON-LD, font preloads, verification
217
+ {% comment %} Consumer hook: end of <head>. Inject JSON-LD, font preloads, verification
206
218
  tags, or extra meta by shadowing _includes/custom/head.html in your site —
207
- no fork of this file needed. The theme ships the stub empty. -->
219
+ no fork of this file needed. The theme ships the stub empty. {% endcomment %}
208
220
  {% include custom/head.html %}
209
221
 
210
- <!-- ========================== -->
211
- <!-- EXPERIMENTAL FRAMEWORKS -->
212
- <!-- ========================== -->
213
- <!-- Tailwind CSS - Currently disabled, enable for utility-first styling -->
214
- <!-- <script src="https://cdn.tailwindcss.com"></script> -->
222
+ {% comment %} ========================== {% endcomment %}
223
+ {% comment %} EXPERIMENTAL FRAMEWORKS {% endcomment %}
224
+ {% comment %} ========================== {% endcomment %}
225
+ {% comment %} Tailwind CSS - Currently disabled, enable for utility-first styling {% endcomment %}
226
+ {% comment %} <script src="https://cdn.tailwindcss.com"></script> {% endcomment %}
215
227
 
216
- <!-- Docs CSS - Alternative stylesheet, currently disabled due to scroll spy issues -->
217
- <!-- TODO: Replace with compiled Sass when scroll spy functionality is fixed -->
218
- <!-- Optional docs.css link removed; use _sass/core/_docs-layout.scss → main.css -->
228
+ {% comment %} Docs CSS - Alternative stylesheet, currently disabled due to scroll spy issues {% endcomment %}
229
+ {% comment %} TODO: Replace with compiled Sass when scroll spy functionality is fixed {% endcomment %}
230
+ {% comment %} Optional docs.css link removed; use _sass/core/_docs-layout.scss → main.css {% endcomment %}
@@ -1,5 +1,5 @@
1
1
  {% comment %} Feature: ZER0-011 {% endcomment %}
2
- <!--
2
+ {% comment %}
3
3
  ===================================================================
4
4
  HEADER - Main Navigation and Site Header
5
5
  ===================================================================
@@ -36,48 +36,67 @@
36
36
  - When `position: navbar`, the bar mounts inside `#top-progress-target` below
37
37
  - Otherwise it floats fixed to the viewport (top or bottom)
38
38
  ===================================================================
39
- -->
39
+ {% endcomment %}
40
40
 
41
- <!-- ================================ -->
42
- <!-- SKIP TO CONTENT LINK -->
43
- <!-- ================================ -->
44
- <!-- UI strings resolve per page language (core/i18n.html sets `ui`) -->
41
+ {% comment %} ================================ {% endcomment %}
42
+ {% comment %} SKIP TO CONTENT LINK {% endcomment %}
43
+ {% comment %} ================================ {% endcomment %}
44
+ {% comment %} UI strings resolve per page language (core/i18n.html sets `ui`) {% endcomment %}
45
45
  {%- include core/i18n.html -%}
46
- <!-- Accessibility: allows keyboard users to skip navigation -->
46
+ {% comment %} Accessibility: allows keyboard users to skip navigation {% endcomment %}
47
47
  <a href="#main-content" class="visually-hidden-focusable position-absolute top-0 start-0 z-3 m-3 btn btn-primary">
48
48
  {{ ui.skip_main_content | default: "Skip to main content" }}
49
49
  </a>
50
50
 
51
51
  <header id="navbar" class="container-fluid text-center fixed-top" role="banner">
52
52
 
53
- <!-- ================================ -->
54
- <!-- TOP NAVIGATION BAR -->
55
- <!-- ================================ -->
53
+ {% comment %} ================================ {% endcomment %}
54
+ {% comment %} TOP NAVIGATION BAR {% endcomment %}
55
+ {% comment %} ================================ {% endcomment %}
56
56
  <div class="navbar navbar-expand-lg bg-body-tertiary flex-nowrap w-100 bottom-shadow">
57
57
 
58
- <!-- ========================== -->
59
- <!-- MAIN NAVIGATION CONTAINER -->
60
- <!-- ========================== -->
58
+ {% comment %} ========================== {% endcomment %}
59
+ {% comment %} MAIN NAVIGATION CONTAINER {% endcomment %}
60
+ {% comment %} ========================== {% endcomment %}
61
61
  <nav class="container-fluid order-2 order-lg-1 d-flex align-items-center gap-1 flex-nowrap navbar-main w-100" aria-label="Primary navigation">
62
62
 
63
- <!-- Leading cluster: sidebar toggle + brand + titles (grid column 1 on lg+) -->
63
+ {% comment %} Leading cluster: sidebar toggle + brand + titles (grid column 1 on lg+) {% endcomment %}
64
64
  <div class="navbar-main-start d-flex align-items-center gap-1 gap-lg-2 min-w-0 flex-shrink-1 flex-grow-1">
65
65
 
66
- <!-- ========================== -->
67
- <!-- MOBILE MENU CONTROLS -->
68
- <!-- ========================== -->
66
+ {% comment %} ========================== {% endcomment %}
67
+ {% comment %} MOBILE MENU CONTROLS {% endcomment %}
68
+ {% comment %} ========================== {% endcomment %}
69
69
 
70
- <!-- Left Sidebar Toggle - Mobile view only. Uses the shared
70
+ {% comment %} Left Sidebar Toggle - Mobile view only. Uses the shared
71
71
  resolver (navigation/sidebar-config.html) so this gate can
72
- never drift from the default layout's aside. -->
72
+ never drift from the default layout's aside. {% endcomment %}
73
73
  {% include navigation/sidebar-config.html %}
74
74
 
75
- {% if sidebar_has_content %}
76
- {%- comment -%}
77
- When site.navigation.unified_mobile_drawer is true, the sidebar
78
- toggle opens the consolidated drawer (Browse tab pre-selected)
79
- instead of the legacy #bdSidebar offcanvas.
80
- {%- endcomment -%}
75
+ {%- comment -%}
76
+ Two conditions, and BOTH are required (issue #373).
77
+
78
+ `sidebar_has_content` asks "is there nav content to show". It is
79
+ not enough on its own: this include is pulled in by
80
+ _layouts/root.html, so it renders on EVERY layout, while
81
+ `#bdSidebar` is emitted by _layouts/default.html alone. On a
82
+ layout that inherits root directly — admin, stats, 404, section,
83
+ home … — the toggle rendered pointing at an offcanvas that was
84
+ never in the document: a dead control, and aria-controls
85
+ dangling into nothing for assistive tech. Measured before the
86
+ fix: 37 of 415 built pages.
87
+
88
+ The unified drawer is exempt because #zer0UnifiedDrawer IS
89
+ emitted from root.html, so it exists on every layout — that
90
+ target is always real and the toggle is always live.
91
+ {%- endcomment -%}
92
+ {% if sidebar_has_content and site.navigation.unified_mobile_drawer %}
93
+ {%- assign _sidebar_toggle_ok = true -%}
94
+ {% elsif sidebar_has_content and sidebar_offcanvas_rendered %}
95
+ {%- assign _sidebar_toggle_ok = true -%}
96
+ {% else %}
97
+ {%- assign _sidebar_toggle_ok = false -%}
98
+ {% endif %}
99
+ {% if _sidebar_toggle_ok %}
81
100
  {%- if site.navigation.unified_mobile_drawer -%}
82
101
  {%- assign _sidebar_target = "#zer0UnifiedDrawer" -%}
83
102
  {%- assign _sidebar_controls = "zer0UnifiedDrawer" -%}
@@ -98,11 +117,11 @@
98
117
  </div>
99
118
  {% endif %}
100
119
 
101
- <!-- ========================== -->
102
- <!-- BRAND AND HOME NAVIGATION -->
103
- <!-- ========================== -->
120
+ {% comment %} ========================== {% endcomment %}
121
+ {% comment %} BRAND AND HOME NAVIGATION {% endcomment %}
122
+ {% comment %} ========================== {% endcomment %}
104
123
  <div class="navbar-brand-group d-inline-flex align-items-center gap-2">
105
- <!-- Home Navigation Button (Desktop only) -->
124
+ {% comment %} Home Navigation Button (Desktop only) {% endcomment %}
106
125
  <div class="navbar-home-links d-none" role="navigation" aria-label="Quick navigation">
107
126
  {%- assign nav_home = site.data.navigation.home -%}
108
127
  {%- if nav_home and nav_home.size > 0 -%}
@@ -118,18 +137,18 @@
118
137
  {%- endif -%}
119
138
  </div>
120
139
 
121
- <!-- Brand Logo Link to Root -->
122
- <!-- Logo path configured in _config.yml -->
140
+ {% comment %} Brand Logo Link to Root {% endcomment %}
141
+ {% comment %} Logo path configured in _config.yml {% endcomment %}
123
142
  {% capture logo_path %}{{ site.logo }}{% endcapture %}
124
143
  <a class="navbar-brand" href="{{ site.baseurl }}/" aria-label="Home - {{ site.title }}" {% if page.url == '/' %}aria-current="page"{% endif %}>
125
144
  <img src="{{ logo_path | relative_url }}" alt="{{ site.title }} logo" width="30" height="30">
126
145
  </a>
127
146
  </div>
128
147
 
129
- <!-- ========================== -->
130
- <!-- SITE BRANDING SECTION -->
131
- <!-- ========================== -->
132
- <!-- Site title and subtitle display -->
148
+ {% comment %} ========================== {% endcomment %}
149
+ {% comment %} SITE BRANDING SECTION {% endcomment %}
150
+ {% comment %} ========================== {% endcomment %}
151
+ {% comment %} Site title and subtitle display {% endcomment %}
133
152
  {% include core/branding.html %}
134
153
 
135
154
  </div>
@@ -141,21 +160,21 @@
141
160
  {%- endcomment -%}
142
161
  {% include navigation/navbar-mobile-quicklinks.html %}
143
162
 
144
- <!-- ========================== -->
145
- <!-- MAIN NAVIGATION MENU -->
146
- <!-- ========================== -->
147
- <!-- Primary navigation items from _data/navigation.yml -->
163
+ {% comment %} ========================== {% endcomment %}
164
+ {% comment %} MAIN NAVIGATION MENU {% endcomment %}
165
+ {% comment %} ========================== {% endcomment %}
166
+ {% comment %} Primary navigation items from _data/navigation.yml {% endcomment %}
148
167
  {% include navigation/navbar.html %}
149
168
 
150
- <!-- ========================== -->
151
- <!-- UTILITY CONTROLS -->
152
- <!-- ========================== -->
169
+ {% comment %} ========================== {% endcomment %}
170
+ {% comment %} UTILITY CONTROLS {% endcomment %}
171
+ {% comment %} ========================== {% endcomment %}
153
172
  <div class="navbar-utility-controls d-flex align-items-center flex-shrink-0" role="group" aria-label="Utility controls">
154
- <!-- Language selection lives in the Settings offcanvas
173
+ {% comment %} Language selection lives in the Settings offcanvas
155
174
  (components/info-section.html, Appearance tab) so the bar
156
- keeps its width for the main navigation items. -->
175
+ keeps its width for the main navigation items. {% endcomment %}
157
176
 
158
- <!-- Search Modal Toggle -->
177
+ {% comment %} Search Modal Toggle {% endcomment %}
159
178
  <button
160
179
  class="btn nav-search-button"
161
180
  type="button"
@@ -170,7 +189,7 @@
170
189
  <kbd class="nav-search-kbd d-none d-xl-inline-flex" aria-hidden="true">/</kbd>
171
190
  </button>
172
191
 
173
- <!-- Settings Offcanvas Toggle -->
192
+ {% comment %} Settings Offcanvas Toggle {% endcomment %}
174
193
  <button
175
194
  class="btn nav-settings-button d-none d-lg-inline-flex"
176
195
  type="button"
@@ -183,7 +202,7 @@
183
202
  <i class="{{site.default_icon}} bi-gear" aria-hidden="true"></i>
184
203
  </button>
185
204
 
186
- <!-- Navigation Menu Toggle - Mobile view -->
205
+ {% comment %} Navigation Menu Toggle - Mobile view {% endcomment %}
187
206
  {%- if site.navigation.unified_mobile_drawer -%}
188
207
  {%- assign _menu_target = "#zer0UnifiedDrawer" -%}
189
208
  {%- assign _menu_controls = "zer0UnifiedDrawer" -%}
@@ -206,12 +225,12 @@
206
225
  </nav>
207
226
  </div>
208
227
 
209
- <!-- ========================== -->
210
- <!-- PROGRESS BAR MOUNT POINT -->
211
- <!-- ========================== -->
212
- <!-- Nanobar mounts here when site.nanobar.position == "navbar" so the bar
228
+ {% comment %} ========================== {% endcomment %}
229
+ {% comment %} PROGRESS BAR MOUNT POINT {% endcomment %}
230
+ {% comment %} ========================== {% endcomment %}
231
+ {% comment %} Nanobar mounts here when site.nanobar.position == "navbar" so the bar
213
232
  renders as a thin strip directly under the header. For "top" / "bottom"
214
- the bar floats fixed to the viewport and no mount is needed. -->
233
+ the bar floats fixed to the viewport and no mount is needed. {% endcomment %}
215
234
  {%- assign _nb_pos = site.nanobar.position | default: "top" -%}
216
235
  {%- if site.nanobar.enabled != false and _nb_pos == "navbar" -%}
217
236
  <div id="top-progress-target" class="nanobar-mount" aria-hidden="true"></div>