jekyll-theme-zer0 1.27.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +812 -13
- data/_data/backlog.yml +356 -2
- data/_data/consumers.yml +252 -0
- data/_data/features.yml +234 -5
- data/_data/i18n/fr.yml +103 -0
- data/_data/i18n/manifest.yml +1529 -0
- data/_data/ingredient_densities.yml +122 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +22 -0
- data/_data/recipe_courses.yml +64 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +387 -277
- data/_data/ui-text.yml +8 -0
- data/_includes/README.md +36 -3
- data/_includes/analytics/google-tag-manager-body.html +11 -2
- data/_includes/analytics/google-tag-manager-head.html +16 -6
- data/_includes/analytics/posthog.html +33 -4
- data/_includes/components/abc-letter.html +43 -0
- data/_includes/components/background-customizer.html +2 -2
- data/_includes/components/book-card.html +42 -0
- data/_includes/components/book-nav.html +80 -0
- data/_includes/components/book-plate.html +31 -0
- data/_includes/components/book-toc.html +47 -0
- data/_includes/components/bookshelf.html +68 -0
- data/_includes/components/card-grid.html +60 -0
- data/_includes/components/cookie-consent.html +4 -4
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/info-section.html +8 -3
- data/_includes/components/language-toggle.html +168 -21
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/page-views-init.html +55 -0
- data/_includes/components/page-views.html +33 -0
- data/_includes/components/recipe-card.html +67 -0
- data/_includes/components/recipe-duration.html +50 -0
- data/_includes/components/recipe-grams.html +58 -0
- data/_includes/components/recipe-index.html +96 -0
- data/_includes/components/recipe-ingredients.html +90 -0
- data/_includes/components/recipe-meta.html +96 -0
- data/_includes/components/recipe-nutrition.html +57 -0
- data/_includes/components/recipe-qty.html +73 -0
- data/_includes/components/recipe-ratio.html +151 -0
- data/_includes/components/recipe-scaler.html +73 -0
- data/_includes/components/recipe-steps.html +86 -0
- data/_includes/components/recipe-temp.html +45 -0
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +3 -0
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/components/theme-info.html +8 -1
- data/_includes/content/jsonld-software.html +22 -3
- data/_includes/content/seo.html +9 -3
- data/_includes/core/color-mode-init.html +13 -4
- data/_includes/core/favicon.html +46 -0
- data/_includes/core/footer.html +18 -16
- data/_includes/core/head.html +21 -0
- data/_includes/core/header.html +7 -4
- data/_includes/custom/body-end.html +18 -0
- data/_includes/custom/body-start.html +17 -0
- data/_includes/custom/footer.html +18 -0
- data/_includes/custom/head.html +18 -0
- data/_includes/navigation/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/section-sidebar.html +93 -11
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-left.html +2 -1
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/sidebar-right.html +2 -1
- data/_includes/navigation/unified-drawer.html +8 -2
- data/_includes/obsidian/full-graph.html +165 -136
- data/_includes/setup/wizard.html +173 -86
- data/_includes/stats/stats-metrics.html +2 -2
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +4 -0
- data/_layouts/admin.html +2 -2
- data/_layouts/article.html +5 -0
- data/_layouts/book-abc.html +106 -0
- data/_layouts/book-story.html +91 -0
- data/_layouts/book.html +113 -0
- data/_layouts/collection.html +17 -5
- data/_layouts/cookbook.html +88 -0
- data/_layouts/default.html +4 -4
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +155 -31
- data/_layouts/recipe.html +274 -0
- data/_layouts/root.html +38 -5
- data/_layouts/section.html +96 -25
- data/_sass/components/_book.scss +423 -0
- data/_sass/components/_callout.scss +1 -1
- data/_sass/components/_footer.scss +37 -1
- data/_sass/components/_page-views.scss +36 -0
- data/_sass/components/_recipe.scss +506 -0
- data/_sass/components/_setup-wizard.scss +235 -0
- data/_sass/components/_ui-enhancements.scss +6 -6
- data/_sass/core/_navbar.scss +263 -15
- data/_sass/core/_obsidian.scss +286 -6
- data/_sass/layouts/_landing.scss +2 -2
- data/_sass/theme/_backgrounds.scss +21 -8
- data/_sass/tokens/_color.scss +6 -0
- data/_sass/tokens/_index.scss +2 -0
- data/_sass/tokens/_radius.scss +21 -0
- data/_sass/tokens/_typography.scss +4 -0
- data/_sass/utilities/_focus.scss +14 -0
- data/assets/css/main.scss +4 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/modules/navigation/navbar.js +55 -0
- data/assets/js/obsidian-graph.js +702 -264
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/page-views.js +372 -0
- data/assets/js/recipe-scaler.js +501 -0
- data/assets/js/search-modal.js +14 -1
- data/assets/js/setup-wizard.js +322 -20
- data/scripts/README.md +29 -0
- data/scripts/bin/audit-consumer +39 -7
- data/scripts/bin/giscus-discussions +213 -14
- data/scripts/bin/manifest +66 -16
- data/scripts/bin/validate +5 -1
- data/scripts/ci/agent_review_result.py +164 -0
- data/scripts/ci/test_agent_review_result.py +172 -0
- data/scripts/design-system-check.rb +170 -0
- data/scripts/install/README.md +47 -6
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- data/scripts/install/ai/wizard.sh +10 -5
- data/scripts/install/apply.sh +7 -3
- data/scripts/install/cli.sh +54 -4
- data/scripts/install/config.sh +167 -0
- data/scripts/install/doctor.sh +38 -0
- data/scripts/install/plan.sh +10 -0
- data/scripts/install/spec.sh +15 -7
- data/scripts/install/template.sh +4 -0
- data/scripts/lib/audit.sh +42 -2
- data/scripts/lint-liquid-raw.rb +137 -0
- data/scripts/propagate.rb +277 -0
- data/scripts/test/lib/run_tests.sh +2 -1
- data/scripts/test/lib/test_agent_review_result.sh +27 -0
- data/scripts/translate.rb +161 -15
- metadata +56 -2
data/_includes/core/footer.html
CHANGED
|
@@ -76,15 +76,16 @@
|
|
|
76
76
|
</span>
|
|
77
77
|
{% endif %}
|
|
78
78
|
{% if forloop.last and powered_by_count > 0 %}
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
<button type="button"
|
|
80
|
+
class="powered-by-link"
|
|
81
|
+
data-bs-toggle="offcanvas"
|
|
82
|
+
data-bs-target="#info-section"
|
|
83
|
+
aria-haspopup="dialog"
|
|
84
|
+
aria-label="View theme information and system details"
|
|
85
|
+
title="Theme Info">
|
|
85
86
|
<i class="{{ site.default_icon }} bi-info-circle" aria-hidden="true"></i>
|
|
86
87
|
<span class="powered-by-link-text">Info</span>
|
|
87
|
-
</
|
|
88
|
+
</button>
|
|
88
89
|
</span>
|
|
89
90
|
</li>
|
|
90
91
|
{% else %}
|
|
@@ -93,15 +94,16 @@
|
|
|
93
94
|
{% endfor %}
|
|
94
95
|
{% if powered_by_count == 0 %}
|
|
95
96
|
<li>
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
<button type="button"
|
|
98
|
+
class="powered-by-link"
|
|
99
|
+
data-bs-toggle="offcanvas"
|
|
100
|
+
data-bs-target="#info-section"
|
|
101
|
+
aria-haspopup="dialog"
|
|
102
|
+
aria-label="View theme information and system details"
|
|
103
|
+
title="Theme Info">
|
|
102
104
|
<i class="{{ site.default_icon }} bi-info-circle" aria-hidden="true"></i>
|
|
103
105
|
<span class="powered-by-link-text">Info</span>
|
|
104
|
-
</
|
|
106
|
+
</button>
|
|
105
107
|
</li>
|
|
106
108
|
{% endif %}
|
|
107
109
|
</ul>
|
|
@@ -239,9 +241,9 @@
|
|
|
239
241
|
<li class="list-inline-item"><a href="{{ site.terms_of_service_url | default: '/terms-of-service' | relative_url }}" class="text-light text-decoration-none">Terms of Service</a></li>
|
|
240
242
|
{% endif %}
|
|
241
243
|
<li class="list-inline-item">
|
|
242
|
-
<
|
|
244
|
+
<button type="button" class="footer-inline-button text-light text-decoration-none" data-bs-toggle="modal" data-bs-target="#cookieSettingsModal">
|
|
243
245
|
Cookie Preferences
|
|
244
|
-
</
|
|
246
|
+
</button>
|
|
245
247
|
</li>
|
|
246
248
|
</ul>
|
|
247
249
|
</div>
|
data/_includes/core/head.html
CHANGED
|
@@ -51,10 +51,22 @@
|
|
|
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.
|
|
55
|
+
Renders nothing on its own; components/page-views.html places the badge.
|
|
56
|
+
Gated on site.page_views.enabled inside the include. -->
|
|
57
|
+
{% include components/page-views-init.html %}
|
|
58
|
+
|
|
54
59
|
<!-- ================================ -->
|
|
55
60
|
<!-- THIRD PARTY INTEGRATIONS -->
|
|
56
61
|
<!-- ================================ -->
|
|
57
62
|
|
|
63
|
+
<!-- Recipe scaling & unit conversion - Only on pages that render recipe
|
|
64
|
+
quantities: the `recipe` layout, or any page that opts in with
|
|
65
|
+
`recipe_tools: true` (e.g. a post embedding components/recipe-*.html). -->
|
|
66
|
+
{% if page.layout == "recipe" or page.recipe_tools %}
|
|
67
|
+
<script defer src="{{ '/assets/js/recipe-scaler.js' | relative_url }}"></script>
|
|
68
|
+
{% endif %}
|
|
69
|
+
|
|
58
70
|
<!-- Mermaid Diagrams - Conditional loading based on page front matter -->
|
|
59
71
|
{% if page.mermaid %}
|
|
60
72
|
{% include components/mermaid.html %}
|
|
@@ -87,6 +99,10 @@ window.MathJax = {
|
|
|
87
99
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
88
100
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
89
101
|
|
|
102
|
+
<!-- Favicon + browser identity (config-driven via `favicon:`; explicit
|
|
103
|
+
/favicon.ico fallback so baseurl deployments resolve) — page-invariant, cached. -->
|
|
104
|
+
{% include_cached core/favicon.html %}
|
|
105
|
+
|
|
90
106
|
<!-- ================================ -->
|
|
91
107
|
<!-- SEO SUPPLEMENT -->
|
|
92
108
|
<!-- ================================ -->
|
|
@@ -186,6 +202,11 @@ window.MathJax = {
|
|
|
186
202
|
<link rel="stylesheet" href="{{ '/assets/css/stats.css' | relative_url }}">
|
|
187
203
|
{% endif %}
|
|
188
204
|
|
|
205
|
+
<!-- Consumer hook: end of <head>. Inject JSON-LD, font preloads, verification
|
|
206
|
+
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. -->
|
|
208
|
+
{% include custom/head.html %}
|
|
209
|
+
|
|
189
210
|
<!-- ========================== -->
|
|
190
211
|
<!-- EXPERIMENTAL FRAMEWORKS -->
|
|
191
212
|
<!-- ========================== -->
|
data/_includes/core/header.html
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<!-- ========================== -->
|
|
59
59
|
<!-- MAIN NAVIGATION CONTAINER -->
|
|
60
60
|
<!-- ========================== -->
|
|
61
|
-
<nav class="container-
|
|
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
63
|
<!-- Leading cluster: sidebar toggle + brand + titles (grid column 1 on lg+) -->
|
|
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">
|
|
@@ -151,8 +151,9 @@
|
|
|
151
151
|
<!-- UTILITY CONTROLS -->
|
|
152
152
|
<!-- ========================== -->
|
|
153
153
|
<div class="navbar-utility-controls d-flex align-items-center flex-shrink-0" role="group" aria-label="Utility controls">
|
|
154
|
-
<!-- Language
|
|
155
|
-
|
|
154
|
+
<!-- Language selection lives in the Settings offcanvas
|
|
155
|
+
(components/info-section.html, Appearance tab) so the bar
|
|
156
|
+
keeps its width for the main navigation items. -->
|
|
156
157
|
|
|
157
158
|
<!-- Search Modal Toggle -->
|
|
158
159
|
<button
|
|
@@ -161,10 +162,12 @@
|
|
|
161
162
|
data-search-toggle
|
|
162
163
|
aria-label="{{ ui.shortcuts_search | default: 'Open search' }}"
|
|
163
164
|
aria-haspopup="dialog"
|
|
164
|
-
|
|
165
|
+
aria-keyshortcuts="/ Meta+K Control+K"
|
|
166
|
+
title="{{ ui.search_button_title | default: 'Search — press / or Ctrl+K' }}"
|
|
165
167
|
>
|
|
166
168
|
<i class="{{site.default_icon}} bi-search" aria-hidden="true"></i>
|
|
167
169
|
<span class="nav-link-text d-none d-xl-inline ms-1">{{ ui.search_button_label | default: "Search" }}</span>
|
|
170
|
+
<kbd class="nav-search-kbd d-none d-xl-inline-flex" aria-hidden="true">/</kbd>
|
|
168
171
|
</button>
|
|
169
172
|
|
|
170
173
|
<!-- Settings Offcanvas Toggle -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
CUSTOM HOOK — body-end
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/custom/body-end.html
|
|
7
|
+
Purpose: Consumer extension point rendered as the LAST thing before
|
|
8
|
+
</body> — after the theme's JS bundle and analytics. Use it
|
|
9
|
+
for site-specific scripts, third-party widgets, or anything
|
|
10
|
+
that must load after the theme's JavaScript.
|
|
11
|
+
|
|
12
|
+
The theme ships this file EMPTY. Create _includes/custom/body-end.html
|
|
13
|
+
in YOUR site to shadow this stub. No theme layout needs forking.
|
|
14
|
+
|
|
15
|
+
Companion hooks: custom/head.html, custom/body-start.html,
|
|
16
|
+
custom/footer.html. See _includes/README.md § Custom hooks.
|
|
17
|
+
===================================================================
|
|
18
|
+
{%- endcomment -%}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
CUSTOM HOOK — body-start
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/custom/body-start.html
|
|
7
|
+
Purpose: Consumer extension point rendered immediately AFTER the
|
|
8
|
+
opening <body> tag — the required position for snippets like
|
|
9
|
+
Google Tag Manager's <noscript> iframe.
|
|
10
|
+
|
|
11
|
+
The theme ships this file EMPTY. Create _includes/custom/body-start.html
|
|
12
|
+
in YOUR site to shadow this stub. No theme layout needs forking.
|
|
13
|
+
|
|
14
|
+
Companion hooks: custom/head.html, custom/footer.html,
|
|
15
|
+
custom/body-end.html. See _includes/README.md § Custom hooks.
|
|
16
|
+
===================================================================
|
|
17
|
+
{%- endcomment -%}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
CUSTOM HOOK — footer
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/custom/footer.html
|
|
7
|
+
Purpose: Consumer extension point rendered directly AFTER the theme
|
|
8
|
+
footer (and its FAB tail), before the cookie-consent banner.
|
|
9
|
+
Use it for an extra footer strip, badges, or legal links the
|
|
10
|
+
theme footer doesn't model.
|
|
11
|
+
|
|
12
|
+
The theme ships this file EMPTY. Create _includes/custom/footer.html
|
|
13
|
+
in YOUR site to shadow this stub. No theme layout needs forking.
|
|
14
|
+
|
|
15
|
+
Companion hooks: custom/head.html, custom/body-start.html,
|
|
16
|
+
custom/body-end.html. See _includes/README.md § Custom hooks.
|
|
17
|
+
===================================================================
|
|
18
|
+
{%- endcomment -%}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
CUSTOM HOOK — head
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/custom/head.html
|
|
7
|
+
Purpose: Consumer extension point rendered at the END of the theme's
|
|
8
|
+
<head> (after main.css, tokens-inline, and user-overrides).
|
|
9
|
+
|
|
10
|
+
The theme ships this file EMPTY. To inject site-specific markup —
|
|
11
|
+
structured data (JSON-LD), font preloads, verification tags, extra
|
|
12
|
+
meta — create _includes/custom/head.html in YOUR site and it will
|
|
13
|
+
shadow this stub. No theme layout or core include needs forking.
|
|
14
|
+
|
|
15
|
+
Companion hooks: custom/body-start.html, custom/footer.html,
|
|
16
|
+
custom/body-end.html. See _includes/README.md § Custom hooks.
|
|
17
|
+
===================================================================
|
|
18
|
+
{%- endcomment -%}
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
Opt-out: set `local_graph: false` in page front matter.
|
|
24
24
|
|
|
25
25
|
Configurable depth: `local_graph_depth: 2` in page front matter (default 1).
|
|
26
|
+
The panel's depth selector (1–3) and incoming/outgoing switches override it
|
|
27
|
+
per-browser via localStorage (zer0.obsidianLocalGraph.v1).
|
|
26
28
|
===================================================================
|
|
27
29
|
{%- endcomment -%}
|
|
28
30
|
{%- comment -%} Shared visibility resolver — sets sidebar_enabled {%- endcomment -%}
|
|
@@ -63,8 +65,18 @@
|
|
|
63
65
|
{%- if _graph_page -%}{%- break -%}{%- endif -%}
|
|
64
66
|
{%- endfor -%}
|
|
65
67
|
{%- endunless -%}
|
|
66
|
-
<div class="obsidian-local-graph-meta d-flex align-items-center justify-content-between gap-2 mb-
|
|
67
|
-
<
|
|
68
|
+
<div class="obsidian-local-graph-meta d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
|
69
|
+
<div class="d-flex align-items-center gap-2">
|
|
70
|
+
<span class="small text-secondary">Depth</span>
|
|
71
|
+
<div class="btn-group btn-group-sm" role="group" aria-label="Graph depth">
|
|
72
|
+
{%- for d in (1..3) -%}
|
|
73
|
+
<input type="radio" class="btn-check" name="obsidianLgDepth"
|
|
74
|
+
id="obsidian-lg-depth-{{ d }}" autocomplete="off"
|
|
75
|
+
{% if d == lg_depth %}checked{% endif %} />
|
|
76
|
+
<label class="btn btn-outline-secondary" for="obsidian-lg-depth-{{ d }}">{{ d }}</label>
|
|
77
|
+
{%- endfor -%}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
68
80
|
{%- if _graph_page -%}
|
|
69
81
|
<a href="{{ _graph_url | relative_url }}"
|
|
70
82
|
class="btn btn-outline-primary btn-sm"
|
|
@@ -73,6 +85,20 @@
|
|
|
73
85
|
</a>
|
|
74
86
|
{%- endif -%}
|
|
75
87
|
</div>
|
|
88
|
+
<div class="d-flex flex-wrap column-gap-3 mb-2"
|
|
89
|
+
role="group"
|
|
90
|
+
aria-label="Link directions shown in the local graph">
|
|
91
|
+
<div class="form-check form-switch mb-0">
|
|
92
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
93
|
+
id="obsidian-lg-outgoing" checked />
|
|
94
|
+
<label class="form-check-label small" for="obsidian-lg-outgoing">Outgoing</label>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-check form-switch mb-0">
|
|
97
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
98
|
+
id="obsidian-lg-incoming" checked />
|
|
99
|
+
<label class="form-check-label small" for="obsidian-lg-incoming">Incoming</label>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
76
102
|
<div id="obsidian-local-graph"
|
|
77
103
|
data-depth="{{ lg_depth }}"
|
|
78
104
|
data-index-url="{{ '/assets/data/wiki-index.json' | relative_url }}"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<ul class="list-unstyled fw-normal pb-2 small nav-tree-root">
|
|
37
37
|
{% for item in navigation %}
|
|
38
38
|
{% assign item_id = item.title | slugify | prepend: "nav-" %}
|
|
39
|
-
{% assign is_active = page.url == item.url %}
|
|
39
|
+
{% assign is_active = false %}{% if page.url == item.url %}{% assign is_active = true %}{% endif %}
|
|
40
40
|
{% assign is_expanded = item.expanded | default: false %}
|
|
41
41
|
{% assign has_children = item.children and item.children.size > 0 %}
|
|
42
42
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<ul class="list-unstyled fw-normal ps-3 nav-tree-children">
|
|
79
79
|
{% for child in item.children %}
|
|
80
80
|
{% assign child_id = child.title | slugify | prepend: "nav-" %}
|
|
81
|
-
{% assign child_active = page.url == child.url %}
|
|
81
|
+
{% assign child_active = false %}{% if page.url == child.url %}{% assign child_active = true %}{% endif %}
|
|
82
82
|
{% assign child_expanded = child.expanded | default: false %}
|
|
83
83
|
{% assign child_has_children = child.children and child.children.size > 0 %}
|
|
84
84
|
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
<div class="collapse{% if child_expanded %} show{% endif %}" id="{{ child_id }}">
|
|
119
119
|
<ul class="list-unstyled fw-normal ps-3 nav-tree-children">
|
|
120
120
|
{% for grandchild in child.children %}
|
|
121
|
-
{% assign gc_active = page.url == grandchild.url %}
|
|
121
|
+
{% assign gc_active = false %}{% if page.url == grandchild.url %}{% assign gc_active = true %}{% endif %}
|
|
122
122
|
<li class="mb-1 nav-tree-item" data-depth="2">
|
|
123
123
|
{% if grandchild.url %}
|
|
124
124
|
<a href="{{ grandchild.url | relative_url }}"
|
|
@@ -12,13 +12,17 @@
|
|
|
12
12
|
- sub_categories: Array of unique tags/topics from section posts
|
|
13
13
|
- page_title: Title of the current section
|
|
14
14
|
- sidebar_id: Unique ID for the sidebar (default: "sectionSidebar")
|
|
15
|
-
|
|
15
|
+
- section_style: The parent layout's display style, one of grid | list |
|
|
16
|
+
magazine (default: "grid"). Decides whether topics render as scroll
|
|
17
|
+
anchors or as filter buttons — see the Topic controls note below.
|
|
18
|
+
|
|
16
19
|
Features:
|
|
17
20
|
- Desktop: Fixed sticky sidebar on the left
|
|
18
21
|
- Mobile: Offcanvas drawer
|
|
19
22
|
- Topic navigation with article counts
|
|
20
23
|
- Section statistics
|
|
21
|
-
- Smooth scroll to sections
|
|
24
|
+
- Smooth scroll to sections (magazine style, where scroll targets exist)
|
|
25
|
+
- Card filtering via [data-filter] (grid/list styles)
|
|
22
26
|
|
|
23
27
|
Dependencies:
|
|
24
28
|
- Bootstrap 5 offcanvas component
|
|
@@ -30,6 +34,42 @@
|
|
|
30
34
|
{% assign section_posts = include.section_posts %}
|
|
31
35
|
{% assign sub_categories = include.sub_categories %}
|
|
32
36
|
{% assign page_title = include.page_title | default: page.title %}
|
|
37
|
+
{% assign section_style = include.section_style | default: "grid" %}
|
|
38
|
+
|
|
39
|
+
{% comment %}
|
|
40
|
+
Topic controls: link vs filter.
|
|
41
|
+
|
|
42
|
+
Only the `magazine` branch of _layouts/section.html emits per-subcategory
|
|
43
|
+
`<section id="{{ subcat | slugify }}">` scroll targets, and only for the first
|
|
44
|
+
5 sub-categories that still have a post once the featured hero is excluded.
|
|
45
|
+
The `grid` and `list` branches emit no sub-topic ids at all — they filter
|
|
46
|
+
`[data-tags]` cards through `[data-filter]` buttons instead.
|
|
47
|
+
|
|
48
|
+
So an <a href="#slug"> here is only ever valid under magazine, and only for
|
|
49
|
+
that exact set. Emitting one anywhere else produced guaranteed-dead anchors:
|
|
50
|
+
invalid markup, a dead click for keyboard/AT users, and a scrollspy that could
|
|
51
|
+
never activate. `_linkable_subcats` below reproduces the layout's set exactly;
|
|
52
|
+
every other style renders filter buttons, which the layout's existing
|
|
53
|
+
`[data-filter]` handler already wires up with no extra JS.
|
|
54
|
+
{% endcomment %}
|
|
55
|
+
{% assign _anchors_resolve = false %}
|
|
56
|
+
{% if section_style == "magazine" %}{% assign _anchors_resolve = true %}{% endif %}
|
|
57
|
+
|
|
58
|
+
{% assign _linkable_subcats = "" | split: "" %}
|
|
59
|
+
{% if _anchors_resolve %}
|
|
60
|
+
{% assign _featured = section_posts | where: "featured", true | first %}
|
|
61
|
+
{% unless _featured %}{% assign _featured = section_posts | first %}{% endunless %}
|
|
62
|
+
{% for subcat in sub_categories limit: 5 %}
|
|
63
|
+
{% assign _subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
|
|
64
|
+
{% assign _remaining = 0 %}
|
|
65
|
+
{% for p in _subcat_posts %}
|
|
66
|
+
{% unless p.url == _featured.url %}{% assign _remaining = _remaining | plus: 1 %}{% endunless %}
|
|
67
|
+
{% endfor %}
|
|
68
|
+
{% if _remaining > 0 %}
|
|
69
|
+
{% assign _linkable_subcats = _linkable_subcats | push: subcat %}
|
|
70
|
+
{% endif %}
|
|
71
|
+
{% endfor %}
|
|
72
|
+
{% endif %}
|
|
33
73
|
|
|
34
74
|
<!-- ================================ -->
|
|
35
75
|
<!-- DESKTOP SIDEBAR (Fixed Sticky) -->
|
|
@@ -47,21 +87,37 @@
|
|
|
47
87
|
</div>
|
|
48
88
|
<div class="card-body p-0">
|
|
49
89
|
<nav class="nav flex-column sidebar-nav">
|
|
90
|
+
{% if _anchors_resolve %}
|
|
50
91
|
<a class="nav-link active" href="#all-posts" data-section="all-posts">
|
|
51
92
|
<i class="bi bi-grid me-2"></i>
|
|
52
93
|
<span class="nav-text">All Articles</span>
|
|
53
94
|
<span class="badge bg-secondary ms-auto">{{ section_posts.size }}</span>
|
|
54
95
|
</a>
|
|
55
|
-
{% for subcat in
|
|
96
|
+
{% for subcat in _linkable_subcats %}
|
|
56
97
|
{% assign subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
|
|
57
|
-
{% if subcat_posts.size > 0 %}
|
|
58
98
|
<a class="nav-link" href="#{{ subcat | slugify }}" data-section="{{ subcat | slugify }}">
|
|
59
99
|
<i class="bi bi-tag me-2"></i>
|
|
60
100
|
<span class="nav-text">{{ subcat | capitalize }}</span>
|
|
61
101
|
<span class="badge bg-light text-dark ms-auto">{{ subcat_posts.size }}</span>
|
|
62
102
|
</a>
|
|
103
|
+
{% endfor %}
|
|
104
|
+
{% else %}
|
|
105
|
+
<button type="button" class="nav-link text-start active" data-filter="all">
|
|
106
|
+
<i class="bi bi-grid me-2"></i>
|
|
107
|
+
<span class="nav-text">All Articles</span>
|
|
108
|
+
<span class="badge bg-secondary ms-auto">{{ section_posts.size }}</span>
|
|
109
|
+
</button>
|
|
110
|
+
{% for subcat in sub_categories limit: 15 %}
|
|
111
|
+
{% assign subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
|
|
112
|
+
{% if subcat_posts.size > 0 %}
|
|
113
|
+
<button type="button" class="nav-link text-start" data-filter="{{ subcat | slugify }}">
|
|
114
|
+
<i class="bi bi-tag me-2"></i>
|
|
115
|
+
<span class="nav-text">{{ subcat | capitalize }}</span>
|
|
116
|
+
<span class="badge bg-light text-dark ms-auto">{{ subcat_posts.size }}</span>
|
|
117
|
+
</button>
|
|
63
118
|
{% endif %}
|
|
64
119
|
{% endfor %}
|
|
120
|
+
{% endif %}
|
|
65
121
|
</nav>
|
|
66
122
|
</div>
|
|
67
123
|
{% comment %} Existence-gate the tags index: a remote-theme consumer without
|
|
@@ -107,26 +163,40 @@
|
|
|
107
163
|
<!-- ================================ -->
|
|
108
164
|
<div class="offcanvas offcanvas-start" tabindex="-1" id="{{ sidebar_id }}" aria-labelledby="{{ sidebar_id }}Label">
|
|
109
165
|
<div class="offcanvas-header bg-primary text-white">
|
|
110
|
-
<
|
|
166
|
+
<h2 class="offcanvas-title h5" id="{{ sidebar_id }}Label">
|
|
111
167
|
<i class="bi bi-bookmark me-2"></i>{{ page_title }} Topics
|
|
112
|
-
</
|
|
168
|
+
</h2>
|
|
113
169
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
|
114
170
|
</div>
|
|
115
171
|
<div class="offcanvas-body p-0">
|
|
116
172
|
<nav class="nav flex-column">
|
|
173
|
+
{% if _anchors_resolve %}
|
|
117
174
|
<a class="nav-link active bg-light py-3 border-bottom" href="#all-posts" data-bs-dismiss="offcanvas">
|
|
118
175
|
<i class="bi bi-grid me-2"></i>All Articles
|
|
119
176
|
<span class="badge bg-secondary float-end">{{ section_posts.size }}</span>
|
|
120
177
|
</a>
|
|
121
|
-
{% for subcat in
|
|
178
|
+
{% for subcat in _linkable_subcats %}
|
|
122
179
|
{% assign subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
|
|
123
|
-
{% if subcat_posts.size > 0 %}
|
|
124
180
|
<a class="nav-link text-body-secondary py-3 border-bottom" href="#{{ subcat | slugify }}" data-bs-dismiss="offcanvas">
|
|
125
181
|
<i class="bi bi-tag me-2"></i>{{ subcat | capitalize }}
|
|
126
182
|
<span class="badge bg-light text-dark float-end">{{ subcat_posts.size }}</span>
|
|
127
183
|
</a>
|
|
184
|
+
{% endfor %}
|
|
185
|
+
{% else %}
|
|
186
|
+
<button type="button" class="nav-link text-start active bg-light py-3 border-bottom w-100" data-filter="all" data-bs-dismiss="offcanvas">
|
|
187
|
+
<i class="bi bi-grid me-2"></i>All Articles
|
|
188
|
+
<span class="badge bg-secondary float-end">{{ section_posts.size }}</span>
|
|
189
|
+
</button>
|
|
190
|
+
{% for subcat in sub_categories %}
|
|
191
|
+
{% assign subcat_posts = section_posts | where_exp: "post", "post.tags contains subcat" %}
|
|
192
|
+
{% if subcat_posts.size > 0 %}
|
|
193
|
+
<button type="button" class="nav-link text-start text-body-secondary py-3 border-bottom w-100" data-filter="{{ subcat | slugify }}" data-bs-dismiss="offcanvas">
|
|
194
|
+
<i class="bi bi-tag me-2"></i>{{ subcat | capitalize }}
|
|
195
|
+
<span class="badge bg-light text-dark float-end">{{ subcat_posts.size }}</span>
|
|
196
|
+
</button>
|
|
128
197
|
{% endif %}
|
|
129
198
|
{% endfor %}
|
|
199
|
+
{% endif %}
|
|
130
200
|
</nav>
|
|
131
201
|
{% if _tags_page %}
|
|
132
202
|
<div class="p-3">
|
|
@@ -169,6 +239,16 @@
|
|
|
169
239
|
overflow: hidden;
|
|
170
240
|
text-overflow: ellipsis;
|
|
171
241
|
}
|
|
242
|
+
|
|
243
|
+
/* In grid/list styles the topic controls are <button data-filter> (there is
|
|
244
|
+
no per-topic scroll target to link to). Bootstrap's .nav-link already
|
|
245
|
+
clears button background/border; these are the two properties a <button>
|
|
246
|
+
still needs to render identically to the anchor form. */
|
|
247
|
+
.sidebar-nav button.nav-link,
|
|
248
|
+
.offcanvas button.nav-link {
|
|
249
|
+
width: 100%;
|
|
250
|
+
text-align: left;
|
|
251
|
+
}
|
|
172
252
|
|
|
173
253
|
/* Offcanvas mobile styling */
|
|
174
254
|
.offcanvas .nav-link {
|
|
@@ -194,8 +274,10 @@
|
|
|
194
274
|
if (target) {
|
|
195
275
|
target.scrollIntoView({ behavior: 'smooth' });
|
|
196
276
|
|
|
197
|
-
// Update active state in sidebar
|
|
198
|
-
|
|
277
|
+
// Update active state in sidebar. Scoped to anchors: in grid/list
|
|
278
|
+
// styles the topic controls are <button data-filter> elements whose
|
|
279
|
+
// active state belongs to the layout's filter handler, not here.
|
|
280
|
+
document.querySelectorAll('.sidebar-nav a.nav-link').forEach(link => {
|
|
199
281
|
link.classList.remove('active');
|
|
200
282
|
});
|
|
201
283
|
|
|
@@ -220,7 +302,7 @@
|
|
|
220
302
|
entries.forEach(entry => {
|
|
221
303
|
if (entry.isIntersecting) {
|
|
222
304
|
const id = entry.target.getAttribute('id');
|
|
223
|
-
document.querySelectorAll('.sidebar-nav .nav-link').forEach(link => {
|
|
305
|
+
document.querySelectorAll('.sidebar-nav a.nav-link').forEach(link => {
|
|
224
306
|
link.classList.remove('active');
|
|
225
307
|
if (link.getAttribute('href') === `#${id}`) {
|
|
226
308
|
link.classList.add('active');
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
Notes: `nav: auto` picks the best mode for the page's collection —
|
|
29
29
|
a curated _data/navigation/<collection>.yml wins, then the
|
|
30
30
|
live "collection" folder tree, then post "categories".
|
|
31
|
+
Legacy mode names ("tree", "dynamic", "searchCats") from
|
|
32
|
+
pre-1.x sidebars are aliased to "auto" for backward compat.
|
|
31
33
|
{%- endcomment -%}
|
|
32
34
|
|
|
33
35
|
{%- comment -%} 1. Visibility: page.sidebar wins; featured/breaking posts default off {%- endcomment -%}
|
|
@@ -61,6 +63,14 @@
|
|
|
61
63
|
{%- endif -%}
|
|
62
64
|
{%- endif -%}
|
|
63
65
|
|
|
66
|
+
{%- comment -%} 3b. Legacy mode names from pre-1.x sidebars ("tree", "dynamic",
|
|
67
|
+
"searchCats") no longer exist; consumer configs still carry them, which
|
|
68
|
+
used to silently disable the sidebar (no data file matches those names).
|
|
69
|
+
Treat them as "auto" so those sites get the best available mode. {%- endcomment -%}
|
|
70
|
+
{%- if sidebar_nav == "tree" or sidebar_nav == "dynamic" or sidebar_nav == "searchCats" -%}
|
|
71
|
+
{%- assign sidebar_nav = "auto" -%}
|
|
72
|
+
{%- endif -%}
|
|
73
|
+
|
|
64
74
|
{%- comment -%} 4. "auto" → concrete mode based on the page's collection {%- endcomment -%}
|
|
65
75
|
{%- if sidebar_nav == "auto" -%}
|
|
66
76
|
{%- assign sidebar_nav = nil -%}
|
|
@@ -91,6 +101,17 @@
|
|
|
91
101
|
{%- if site.tags.size > 0 -%}
|
|
92
102
|
{%- assign sidebar_has_content = true -%}
|
|
93
103
|
{%- endif -%}
|
|
104
|
+
{%- when "pages" -%}
|
|
105
|
+
{%- comment -%} URL-hierarchy page tree — content exists when the base
|
|
106
|
+
prefix matches at least one page (see sidebar-pagetree.html). {%- endcomment -%}
|
|
107
|
+
{%- assign _pt_base = page.sidebar.base -%}
|
|
108
|
+
{%- if _pt_base -%}
|
|
109
|
+
{%- assign _pt_pool = site.html_pages | concat: site.documents -%}
|
|
110
|
+
{%- assign _pt_hits = _pt_pool | where_exp: "p", "p.url contains _pt_base" -%}
|
|
111
|
+
{%- if _pt_hits.size > 0 -%}
|
|
112
|
+
{%- assign sidebar_has_content = true -%}
|
|
113
|
+
{%- endif -%}
|
|
114
|
+
{%- endif -%}
|
|
94
115
|
{%- else -%}
|
|
95
116
|
{%- if site.data.navigation[sidebar_nav] -%}
|
|
96
117
|
{%- assign sidebar_has_content = true -%}
|
|
@@ -79,7 +79,8 @@
|
|
|
79
79
|
aria-controls="bdSidebar"
|
|
80
80
|
aria-expanded="true"
|
|
81
81
|
aria-label="Hide documentation sidebar"
|
|
82
|
-
|
|
82
|
+
aria-keyshortcuts="b"
|
|
83
|
+
title="Hide documentation sidebar (b)">
|
|
83
84
|
<i class="bi bi-layout-sidebar-inset" aria-hidden="true"></i>
|
|
84
85
|
<span class="visually-hidden">Hide documentation sidebar</span>
|
|
85
86
|
</button>
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
<div class="nav-tree nav-tree--categories"{% if include.id %} id="{{ include.id }}"{% endif %} data-nav-tree>
|
|
24
24
|
{% include navigation/sidebar-categories.html taxonomy=sidebar_nav %}
|
|
25
25
|
</div>
|
|
26
|
+
{%- elsif sidebar_nav == "pages" -%}
|
|
27
|
+
<nav class="w-100 nav-tree"{% if include.id %} id="{{ include.id }}"{% endif %} data-nav-tree aria-label="Section navigation">
|
|
28
|
+
{% include navigation/sidebar-pagetree.html %}
|
|
29
|
+
</nav>
|
|
26
30
|
{%- elsif sidebar_nav -%}
|
|
27
31
|
<nav class="w-100 nav-tree"{% if include.id %} id="{{ include.id }}"{% endif %} data-nav-tree aria-label="Section navigation">
|
|
28
32
|
{% include navigation/nav-tree.html nav=sidebar_nav %}
|