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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1143 -13
- data/_data/README.md +2 -0
- data/_data/ai.yml +5 -3
- data/_data/ai_pricing.yml +36 -0
- data/_data/backlog.yml +507 -2
- data/_data/consumers.yml +157 -9
- data/_data/features.yml +303 -20
- data/_data/feedback_types.yml +17 -12
- data/_data/i18n/fr.yml +12 -7
- data/_data/i18n/manifest.yml +39 -12
- data/_data/ingredient_densities.yml +122 -0
- data/_data/landing.yml +5 -2
- data/_data/navigation/main.yml +16 -0
- data/_data/navigation/quickstart.yml +4 -0
- data/_data/recipe_courses.yml +64 -0
- data/_data/site_builder.yml +874 -0
- data/_data/theme-manifest.yml +160 -124
- data/_data/ui-text.yml +26 -0
- data/_includes/README.md +26 -2
- data/_includes/analytics/posthog.html +2 -2
- data/_includes/components/admin-links.html +2 -2
- data/_includes/components/admin-tabs.html +2 -2
- data/_includes/components/ai-chat.html +14 -11
- data/_includes/components/analytics-dashboard.html +8 -8
- data/_includes/components/author-bio.html +1 -1
- data/_includes/components/author-card.html +10 -2
- data/_includes/components/author-eeat.html +4 -4
- data/_includes/components/background-customizer.html +10 -10
- data/_includes/components/background-image.html +114 -0
- data/_includes/components/background-settings.html +28 -15
- data/_includes/components/collection-manager.html +5 -5
- data/_includes/components/component-showcase.html +13 -13
- data/_includes/components/config-editor.html +12 -12
- data/_includes/components/config-viewer.html +8 -8
- data/_includes/components/cookie-consent.html +15 -15
- data/_includes/components/cta-button.html +7 -2
- data/_includes/components/dev-shortcuts.html +7 -7
- data/_includes/components/env-dashboard.html +8 -8
- data/_includes/components/env-switcher.html +9 -9
- data/_includes/components/feature-card.html +2 -2
- data/_includes/components/halfmoon.html +2 -2
- data/_includes/components/info-section.html +42 -37
- data/_includes/components/js-cdn.html +15 -15
- data/_includes/components/language-toggle.html +168 -21
- data/_includes/components/mermaid.html +72 -435
- data/_includes/components/nanobar.html +5 -5
- data/_includes/components/nav-editor.html +2 -2
- data/_includes/components/nav-export.html +2 -2
- data/_includes/components/nav-overview.html +2 -2
- data/_includes/components/page-feedback.html +45 -30
- data/_includes/components/page-views-init.html +55 -0
- data/_includes/components/page-views.html +33 -0
- data/_includes/components/post-card.html +22 -22
- data/_includes/components/post-type-badge.html +2 -2
- data/_includes/components/powered-by.html +2 -2
- data/_includes/components/preview-image.html +6 -0
- data/_includes/components/quick-index.html +2 -2
- 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 +29 -4
- data/_includes/components/searchbar.html +2 -2
- data/_includes/components/shortcuts-modal.html +3 -0
- data/_includes/components/svg-background.html +2 -2
- data/_includes/components/theme-customizer.html +2 -2
- data/_includes/components/theme-info.html +14 -7
- data/_includes/components/theme-preview-gallery.html +22 -22
- data/_includes/content/giscus.html +2 -2
- data/_includes/content/intro.html +8 -8
- data/_includes/content/jsonld-faq.html +2 -2
- data/_includes/content/jsonld-software.html +24 -5
- data/_includes/content/seo.html +4 -4
- data/_includes/content/sitemap.html +27 -27
- data/_includes/content/toc.html +183 -183
- data/_includes/core/branding.html +6 -6
- data/_includes/core/console-capture.html +32 -74
- data/_includes/core/favicon.html +49 -7
- data/_includes/core/footer-fabs.html +17 -3
- data/_includes/core/footer.html +49 -34
- data/_includes/core/head.html +110 -86
- data/_includes/core/header.html +76 -54
- data/_includes/docs/bootstrap-docs.html +8 -8
- data/_includes/landing/landing-install-cards.html +2 -2
- data/_includes/landing/landing-quick-links.html +1 -1
- data/_includes/navigation/admin-nav.html +2 -2
- data/_includes/navigation/nav-tree.html +8 -8
- data/_includes/navigation/navbar.html +12 -12
- data/_includes/navigation/section-sidebar.html +109 -27
- data/_includes/navigation/sidebar-config.html +36 -2
- data/_includes/navigation/sidebar-left.html +17 -16
- data/_includes/navigation/sidebar-right.html +8 -7
- data/_includes/obsidian/full-graph.html +2 -2
- data/_includes/setup/claude-session.html +72 -0
- data/_includes/setup/prereq-checklist.html +90 -0
- data/_includes/setup/wizard.html +924 -222
- data/_includes/stats/stats-categories.html +8 -8
- data/_includes/stats/stats-header.html +14 -14
- data/_includes/stats/stats-metrics.html +14 -14
- data/_includes/stats/stats-no-data.html +12 -12
- data/_includes/stats/stats-overview.html +6 -6
- data/_includes/stats/stats-tags.html +8 -8
- data/_layouts/404.html +38 -24
- data/_layouts/README.md +2 -0
- data/_layouts/admin.html +24 -24
- data/_layouts/article.html +43 -33
- data/_layouts/author.html +20 -20
- data/_layouts/authors.html +2 -2
- data/_layouts/book-abc.html +12 -12
- data/_layouts/book-story.html +15 -15
- data/_layouts/book.html +12 -12
- data/_layouts/collection.html +33 -33
- data/_layouts/cookbook.html +88 -0
- data/_layouts/default.html +31 -28
- data/_layouts/home.html +23 -23
- data/_layouts/index.html +10 -10
- data/_layouts/landing.html +17 -17
- data/_layouts/news.html +44 -44
- data/_layouts/note.html +38 -38
- data/_layouts/notebook.html +34 -34
- data/_layouts/recipe.html +274 -0
- data/_layouts/root.html +92 -55
- data/_layouts/section.html +62 -33
- data/_layouts/setup.html +3 -3
- data/_layouts/sitemap-collection.html +49 -49
- data/_layouts/stats.html +40 -40
- data/_layouts/tag.html +12 -12
- data/_layouts/welcome.html +21 -21
- data/_sass/components/_callout.scss +1 -1
- data/_sass/components/_footer.scss +37 -1
- data/_sass/components/_mermaid.scss +375 -0
- data/_sass/components/_page-views.scss +36 -0
- data/_sass/components/_recipe.scss +506 -0
- data/_sass/components/_setup-wizard.scss +764 -0
- data/_sass/components/_ui-enhancements.scss +6 -6
- data/_sass/core/_navbar.scss +261 -46
- data/_sass/layouts/_landing.scss +2 -2
- data/_sass/layouts/_navbar-extras.scss +14 -4
- 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/ai-chat.js +47 -5
- data/assets/js/fleet-feedback-capture.js +124 -0
- data/assets/js/fleet-feedback.js +853 -0
- data/assets/js/mermaid-diagrams.js +1267 -0
- data/assets/js/modules/navigation/config.js +9 -6
- data/assets/js/modules/navigation/navbar.js +55 -0
- data/assets/js/modules/navigation/scroll-spy.js +315 -80
- data/assets/js/modules/theme/appearance.js +8 -2
- data/assets/js/obsidian-wiki-links.js +8 -3
- data/assets/js/page-feedback.js +125 -192
- data/assets/js/page-views.js +372 -0
- data/assets/js/recipe-scaler.js +501 -0
- data/assets/js/search-modal.js +36 -0
- data/assets/js/setup-wizard.js +2279 -226
- data/assets/js/site-builder.js +1834 -0
- data/assets/js/ui-enhancements.js +11 -3
- data/scripts/README.md +44 -0
- data/scripts/ai/README.md +38 -0
- data/scripts/ai/api_call.rb +124 -0
- data/scripts/ai/usage.rb +314 -0
- data/scripts/ai/usage_report.rb +225 -0
- data/scripts/bin/audit-consumer +39 -7
- data/scripts/bin/giscus-discussions +213 -14
- data/scripts/bin/manifest +35 -12
- data/scripts/ci/agent_review_result.py +164 -0
- data/scripts/ci/test_agent_review_result.py +172 -0
- data/scripts/ci/test_visual_evidence_autogen.py +341 -0
- data/scripts/ci/visual_evidence_autogen.py +1060 -0
- data/scripts/content-review.rb +20 -1
- data/scripts/design-system-check.rb +170 -0
- data/scripts/lib/audit.sh +42 -2
- data/scripts/lint-liquid-raw.rb +137 -0
- data/scripts/test/integration/mermaid +22 -8
- data/scripts/test/lib/run_tests.sh +3 -1
- data/scripts/test/lib/test_agent_review_result.sh +27 -0
- data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
- data/scripts/translate.rb +94 -16
- metadata +48 -2
|
@@ -1,89 +1,47 @@
|
|
|
1
1
|
{%- comment -%}
|
|
2
2
|
===================================================================
|
|
3
|
-
CONSOLE CAPTURE — early
|
|
3
|
+
CONSOLE CAPTURE — early console/error ring buffer
|
|
4
4
|
===================================================================
|
|
5
5
|
|
|
6
6
|
File: console-capture.html
|
|
7
7
|
Path: _includes/core/console-capture.html
|
|
8
|
-
Purpose:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
(assets/js/page-feedback.js) can attach the logs and JS
|
|
12
|
-
errors that led up to a report.
|
|
8
|
+
Purpose: Load the fleet feedback kit's capture buffer HIGH in <head> —
|
|
9
|
+
before the theme's deferred scripts run — so the page-feedback
|
|
10
|
+
widget can attach the logs and JS errors that led up to a report.
|
|
13
11
|
|
|
14
12
|
Renders: only when page_feedback.enabled AND page_feedback.capture_logs.
|
|
15
13
|
|
|
14
|
+
This used to be ~60 lines of inline ES5 maintained here. It is now the
|
|
15
|
+
fleet kit's capture.js, VENDORED to assets/js/fleet-feedback-capture.js
|
|
16
|
+
(bamr87/bamr87 templates/feedback/capture.js — drift check (i) holds the
|
|
17
|
+
copy to the hub's). Two things came with the move: the buffer shape is
|
|
18
|
+
now the one FleetFeedbackCore reads, so the theme's widget and the fleet's
|
|
19
|
+
web component file byte-identical issues; and every captured line is
|
|
20
|
+
REDACTED before it enters the ring (bearer/API keys, JWTs, GitHub tokens,
|
|
21
|
+
email addresses), so a secret cannot be previewed, copied, or filed.
|
|
22
|
+
|
|
23
|
+
Loaded blocking rather than deferred, and that is the point: a buffer
|
|
24
|
+
installed after parsing misses exactly the load-time failures worth
|
|
25
|
+
reporting. It is ~4 KB, and it REPLACED ~3.7 KB of inline script, so the
|
|
26
|
+
head got smaller — which also buys back room for the charset meta that
|
|
27
|
+
has to land in the first 1024 bytes (issue #372).
|
|
28
|
+
|
|
16
29
|
Privacy: in-memory only, never persisted, never sent anywhere until the
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
reader opens the modal, reviews the captured lines, and submits.
|
|
31
|
+
Any single entry is truncated; the buffer is a fixed-size ring
|
|
32
|
+
(page_feedback.log_limit, default 40).
|
|
20
33
|
|
|
21
|
-
Depends on:
|
|
34
|
+
Depends on: assets/js/fleet-feedback-capture.js (vendored, no CDN).
|
|
22
35
|
===================================================================
|
|
23
36
|
{%- endcomment -%}
|
|
24
37
|
{%- if site.page_feedback.enabled and site.page_feedback.capture_logs -%}
|
|
25
|
-
<script>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var buffer = [];
|
|
36
|
-
function now() { return start === null ? Math.round(performance.now()) : (Date.now() - start); }
|
|
37
|
-
|
|
38
|
-
function stringify(value) {
|
|
39
|
-
if (typeof value === "string") { return value; }
|
|
40
|
-
if (value instanceof Error) {
|
|
41
|
-
return (value.name || "Error") + ": " + (value.message || "") + (value.stack ? "\n" + value.stack : "");
|
|
42
|
-
}
|
|
43
|
-
try { return JSON.stringify(value); } catch (e) { return String(value); }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function push(level, args) {
|
|
47
|
-
try {
|
|
48
|
-
var parts = [];
|
|
49
|
-
for (var i = 0; i < args.length; i++) { parts.push(stringify(args[i])); }
|
|
50
|
-
var text = parts.join(" ");
|
|
51
|
-
if (text.length > MAX_ENTRY) { text = text.slice(0, MAX_ENTRY) + "…"; }
|
|
52
|
-
buffer.push({ t: now(), level: level, text: text });
|
|
53
|
-
while (buffer.length > LIMIT) { buffer.shift(); }
|
|
54
|
-
} catch (e) { /* logging must never break the page */ }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Wrap console methods, preserving passthrough to the real console.
|
|
58
|
-
if (window.console) {
|
|
59
|
-
["log", "info", "warn", "error", "debug"].forEach(function (level) {
|
|
60
|
-
var original = console[level] ? console[level].bind(console) : null;
|
|
61
|
-
console[level] = function () {
|
|
62
|
-
push(level, arguments);
|
|
63
|
-
if (original) { original.apply(null, arguments); }
|
|
64
|
-
};
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Uncaught errors and unhandled promise rejections.
|
|
69
|
-
window.addEventListener("error", function (e) {
|
|
70
|
-
if (!e) { return; }
|
|
71
|
-
var where = e.filename ? " (" + e.filename + ":" + (e.lineno || 0) + ":" + (e.colno || 0) + ")" : "";
|
|
72
|
-
push("error", [(e.message || "Script error") + where]);
|
|
73
|
-
});
|
|
74
|
-
window.addEventListener("unhandledrejection", function (e) {
|
|
75
|
-
var reason = e && e.reason;
|
|
76
|
-
var msg = reason && reason.message ? reason.message : stringify(reason);
|
|
77
|
-
push("error", ["Unhandled promise rejection: " + msg]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
window.__zer0Feedback = {
|
|
81
|
-
installed: true,
|
|
82
|
-
logs: buffer,
|
|
83
|
-
/** Snapshot the buffer (newest last) for the widget to read. */
|
|
84
|
-
snapshot: function () { return buffer.slice(); },
|
|
85
|
-
clear: function () { buffer.length = 0; }
|
|
86
|
-
};
|
|
87
|
-
})();
|
|
88
|
-
</script>
|
|
38
|
+
<script src="{{ '/assets/js/fleet-feedback-capture.js' | relative_url }}"></script>
|
|
39
|
+
{%- comment -%}
|
|
40
|
+
The ring size is site configuration, so it is applied after the buffer
|
|
41
|
+
installs rather than baked into the vendored file (which must stay
|
|
42
|
+
byte-identical to the hub's).
|
|
43
|
+
{%- endcomment -%}
|
|
44
|
+
{%- if site.page_feedback.log_limit -%}
|
|
45
|
+
<script>if(window.__fleetFeedback){window.__fleetFeedback.limit={{ site.page_feedback.log_limit | jsonify }};}</script>
|
|
46
|
+
{%- endif -%}
|
|
89
47
|
{%- endif -%}
|
data/_includes/core/favicon.html
CHANGED
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
png_size : 32x32 # sizes attr for png (optional)
|
|
17
17
|
apple_touch : /assets/images/apple-touch.png # iOS home-screen icon (optional)
|
|
18
18
|
manifest : /site.webmanifest # PWA manifest (optional)
|
|
19
|
-
theme_color
|
|
19
|
+
theme_color : "#0d1117" # pin ONE chrome color (optional)
|
|
20
|
+
theme_color_light : "#ffffff" # light-scheme chrome (optional)
|
|
21
|
+
theme_color_dark : "#212529" # dark-scheme chrome (optional)
|
|
20
22
|
|
|
21
23
|
With NO config at all this still emits the explicit
|
|
22
|
-
/favicon.ico link (every consumer site carries one at root)
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
/favicon.ico link (every consumer site carries one at root)
|
|
25
|
+
AND both theme-color tags. That last part is the point of the
|
|
26
|
+
fallbacks below: a remote_theme consumer does not inherit this
|
|
27
|
+
repo's _config.yml (see remote-theme-checklist.md), so a
|
|
28
|
+
config-gated tag emitted nothing at all on exactly the sites
|
|
29
|
+
this include exists to serve (issue #281).
|
|
25
30
|
Notes: Page-invariant — safe to render via include_cached. All paths
|
|
26
31
|
run through relative_url so baseurl deployments resolve.
|
|
27
32
|
{%- endcomment -%}
|
|
@@ -40,7 +45,44 @@
|
|
|
40
45
|
{%- if _fav.manifest %}
|
|
41
46
|
<link rel="manifest" href="{{ _fav.manifest | relative_url }}">
|
|
42
47
|
{%- endif %}
|
|
43
|
-
{%-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
{%- comment -%}
|
|
49
|
+
theme-color drives mobile browser chrome (iOS Safari address/status bar,
|
|
50
|
+
the Chrome/Android task-switcher card). It must track the page SURFACE.
|
|
51
|
+
|
|
52
|
+
It used to fall back to site.theme_color.main, which is the brand ACCENT --
|
|
53
|
+
#007bff on this site -- so a dark-first theme advertised a bright blue
|
|
54
|
+
address bar. The defaults below are Bootstrap 5.3.3's own --bs-body-bg for
|
|
55
|
+
each scheme (:root #fff, [data-bs-theme=dark] #212529); the theme adds no
|
|
56
|
+
dark override of that variable, so they are the real computed surface.
|
|
57
|
+
|
|
58
|
+
WHICH SHAPE TO EMIT depends on how the mode is decided, because
|
|
59
|
+
`media="(prefers-color-scheme: ...)"` keys off the OS and nothing else:
|
|
60
|
+
|
|
61
|
+
- mode is pinned (color_mode_lock, or a non-auto color_mode_default)
|
|
62
|
+
-> ONE unconditional tag. A media pair would hand light chrome to an
|
|
63
|
+
OS-light visitor looking at a page this site renders dark.
|
|
64
|
+
- mode is `auto` -> the media pair, which is exactly what `auto` means.
|
|
65
|
+
|
|
66
|
+
A locked `auto` resolves to dark here, matching core/color-mode-init.html.
|
|
67
|
+
|
|
68
|
+
Known limitation, deliberately not solved here: an unlocked site lets a
|
|
69
|
+
visitor override the mode from the Appearance panel via localStorage, which
|
|
70
|
+
no static meta can follow. Keeping chrome in sync with the panel needs a
|
|
71
|
+
runtime hook in the toggle path -- tracked separately.
|
|
72
|
+
{%- endcomment -%}
|
|
73
|
+
{%- assign _tc_light = _fav.theme_color_light | default: '#ffffff' -%}
|
|
74
|
+
{%- assign _tc_dark = _fav.theme_color_dark | default: '#212529' -%}
|
|
75
|
+
{%- assign _tc_mode = site.color_mode_default | default: 'auto' -%}
|
|
76
|
+
{%- if _fav.theme_color %}
|
|
77
|
+
{%- comment -%} Back-compat: a consumer that pinned one colour keeps it. {%- endcomment %}
|
|
78
|
+
<meta name="theme-color" content="{{ _fav.theme_color }}">
|
|
79
|
+
{%- elsif site.color_mode_lock and _tc_mode == 'light' %}
|
|
80
|
+
<meta name="theme-color" content="{{ _tc_light }}">
|
|
81
|
+
{%- elsif site.color_mode_lock or _tc_mode == 'dark' %}
|
|
82
|
+
<meta name="theme-color" content="{{ _tc_dark }}">
|
|
83
|
+
{%- elsif _tc_mode == 'light' %}
|
|
84
|
+
<meta name="theme-color" content="{{ _tc_light }}">
|
|
85
|
+
{%- else %}
|
|
86
|
+
<meta name="theme-color" media="(prefers-color-scheme: light)" content="{{ _tc_light }}">
|
|
87
|
+
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="{{ _tc_dark }}">
|
|
46
88
|
{%- endif %}
|
|
@@ -20,12 +20,26 @@
|
|
|
20
20
|
after </footer> is visually identical to the previous nesting.
|
|
21
21
|
===================================================================
|
|
22
22
|
{%- endcomment -%}
|
|
23
|
-
{%-
|
|
24
|
-
|
|
23
|
+
{%- comment -%}
|
|
24
|
+
The TOC FAB controls `#tocContents`, which navigation/sidebar-right.html
|
|
25
|
+
emits — and only _layouts/default.html includes that. This used to be a
|
|
26
|
+
DENYLIST of layouts to skip, which fails open: any layout not on the list got
|
|
27
|
+
the FAB whether or not it rendered the panel, and the list had already fallen
|
|
28
|
+
behind (404 and the book-* layouts were missing, so those pages shipped a FAB
|
|
29
|
+
pointing at nothing — issue #373).
|
|
30
|
+
|
|
31
|
+
Replaced with the positive gate from navigation/sidebar-config.html, which
|
|
32
|
+
fails safe: a new layout gets no FAB until it is declared as one that renders
|
|
33
|
+
the offcanvas. sidebar-config is included here rather than relied upon from
|
|
34
|
+
header.html because Liquid assigns propagate to the INCLUDING template, and
|
|
35
|
+
these are two sibling includes of root.html.
|
|
36
|
+
{%- endcomment -%}
|
|
37
|
+
{% include navigation/sidebar-config.html %}
|
|
38
|
+
{%- if sidebar_offcanvas_rendered -%}
|
|
25
39
|
{% include navigation/toc-fab.html %}
|
|
26
40
|
{%- comment -%} Local-graph FAB/panel are part of the Obsidian feature. {%- endcomment -%}
|
|
27
41
|
{%- if site.obsidian.enabled != false -%}
|
|
28
42
|
{% include navigation/local-graph-fab.html %}
|
|
29
43
|
{% include navigation/local-graph.html %}
|
|
30
44
|
{%- endif -%}
|
|
31
|
-
{%-
|
|
45
|
+
{%- endif -%}
|
data/_includes/core/footer.html
CHANGED
|
@@ -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
|
-
|
|
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
|
© {{ site.time | date: "%Y" }} {{ site.name | default: site.title }}
|
|
@@ -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,27 +94,28 @@
|
|
|
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>
|
|
108
110
|
</div>
|
|
109
111
|
</div>
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
{% comment %} Branding and Navigation Block — full-width dark background {% endcomment %}
|
|
112
114
|
<div class="bg-dark text-light footer-dark-block">
|
|
113
115
|
<div class="container-xl">
|
|
114
|
-
|
|
116
|
+
{% comment %} Top: Site info + Quick Links + Social {% endcomment %}
|
|
115
117
|
<div class="row mb-3 mb-lg-4 g-2 g-lg-3 footer-nav-columns justify-content-center">
|
|
116
|
-
|
|
118
|
+
{% comment %} Site Info {% endcomment %}
|
|
117
119
|
<div class="col-12 col-md-3 col-lg-3 mb-2 mb-lg-3 order-0 order-md-1 order-lg-1">
|
|
118
120
|
<h2 class="footer-section-title text-uppercase h6 mb-0">{{ site.title }}</h2>
|
|
119
121
|
{% if site.address %}<p class="mb-1">{{ site.address }}</p>{% endif %}
|
|
@@ -121,12 +123,12 @@
|
|
|
121
123
|
{% if site.email %}<p class="mb-0"><a href="mailto:{{ site.email }}" class="text-light text-decoration-none">{{ site.email }}</a></p>{% endif %}
|
|
122
124
|
</div>
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
{% comment %} Quick Links
|
|
125
127
|
Resolution order:
|
|
126
128
|
1. site.footer_quick_links (array of {label, url}) — explicit override
|
|
127
129
|
2. Auto-detection: only render links whose target pages
|
|
128
130
|
actually exist in the build. Prevents 404s on bare-minimum
|
|
129
|
-
sites that haven't created /about/, /services/, etc.
|
|
131
|
+
sites that haven't created /about/, /services/, etc. {% endcomment %}
|
|
130
132
|
<div class="col-6 col-md-3 col-lg-3 mb-2 mb-lg-3 order-2 order-md-2 order-lg-2">
|
|
131
133
|
<h2 class="footer-section-title text-uppercase h6 mb-0">Quick Links</h2>
|
|
132
134
|
<ul class="list-unstyled" aria-label="Footer quick links">
|
|
@@ -169,7 +171,7 @@
|
|
|
169
171
|
</ul>
|
|
170
172
|
</div>
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
{% comment %} Latest Posts (dynamic) {% endcomment %}
|
|
173
175
|
{% assign posts_count = site.posts | size %}
|
|
174
176
|
{% if posts_count and posts_count > 0 %}
|
|
175
177
|
<div class="col-12 col-md-3 col-lg-3 mb-2 mb-lg-3 order-4 order-md-3 order-lg-3">
|
|
@@ -184,7 +186,7 @@
|
|
|
184
186
|
</div>
|
|
185
187
|
{% endif %}
|
|
186
188
|
|
|
187
|
-
|
|
189
|
+
{% comment %} Social & RSS {% endcomment %}
|
|
188
190
|
<div class="col-6 col-md-3 col-lg-3 mb-2 mb-lg-3 order-3 order-md-4 order-lg-4">
|
|
189
191
|
<h2 class="footer-section-title text-uppercase h6 mb-0">Follow Us</h2>
|
|
190
192
|
<ul class="list-inline mb-0" aria-label="Social links">
|
|
@@ -192,7 +194,17 @@
|
|
|
192
194
|
{% if link.url %}
|
|
193
195
|
<li class="list-inline-item">
|
|
194
196
|
{% capture link_icon_classes %}{{ site.default_icon }} {{ link.icon | default: "bi-link-45deg" }}{% endcapture %}
|
|
195
|
-
|
|
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 }}">
|
|
196
208
|
<i class="{{ link_icon_classes }}" aria-hidden="true"></i>
|
|
197
209
|
<span class="d-none d-md-inline">{{ link.label }}</span>
|
|
198
210
|
</a>
|
|
@@ -200,16 +212,19 @@
|
|
|
200
212
|
{% endif %}
|
|
201
213
|
{% endfor %}
|
|
202
214
|
<li class="list-inline-item">
|
|
203
|
-
|
|
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 }}">
|
|
204
219
|
<i class="{{ site.default_icon }} bi-rss" aria-hidden="true"></i>
|
|
205
|
-
<span class="d-none d-md-inline">{{
|
|
220
|
+
<span class="d-none d-md-inline">{{ _feed_label }}</span>
|
|
206
221
|
</a>
|
|
207
222
|
</li>
|
|
208
223
|
</ul>
|
|
209
224
|
</div>
|
|
210
225
|
</div>
|
|
211
226
|
|
|
212
|
-
|
|
227
|
+
{% comment %} Middle: Subscribe + Policies {% endcomment %}
|
|
213
228
|
<div class="row mb-3 mb-lg-4 align-items-center justify-content-center g-2 footer-subscribe-row">
|
|
214
229
|
<div class="col-12 col-md-8 col-lg-8">
|
|
215
230
|
<form class="row g-2 footer-subscribe-form" aria-label="Newsletter signup" onsubmit="return false;">
|
|
@@ -239,15 +254,15 @@
|
|
|
239
254
|
<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
255
|
{% endif %}
|
|
241
256
|
<li class="list-inline-item">
|
|
242
|
-
<
|
|
257
|
+
<button type="button" class="footer-inline-button text-light text-decoration-none" data-bs-toggle="modal" data-bs-target="#cookieSettingsModal">
|
|
243
258
|
Cookie Preferences
|
|
244
|
-
</
|
|
259
|
+
</button>
|
|
245
260
|
</li>
|
|
246
261
|
</ul>
|
|
247
262
|
</div>
|
|
248
263
|
</div>
|
|
249
264
|
|
|
250
|
-
|
|
265
|
+
{% comment %} Bottom: Copyright {% endcomment %}
|
|
251
266
|
<div class="text-center">
|
|
252
267
|
<p class="mb-0">© {{ site.time | date: "%Y" }} {{ site.title | default: site.name }}. All Rights Reserved.</p>
|
|
253
268
|
</div>
|
|
@@ -263,9 +278,9 @@
|
|
|
263
278
|
immediately after </footer> is visually identical to nesting them inside.
|
|
264
279
|
{%- endcomment -%}
|
|
265
280
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
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 %}
|
|
269
284
|
<a
|
|
270
285
|
id="backToTopBtn"
|
|
271
286
|
href="#"
|