jekyll-theme-zer0 1.22.0 → 1.23.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bb7f0d10a0dcb7cbe5297c6a412db670fc837d5acc43c85b6752baab5acf5a9
4
- data.tar.gz: '0095e82963f7316a63fecdf355f6fb3e8a126c7528666ab540d274175dda515c'
3
+ metadata.gz: 73435219bd711a7e8485d895f8e07156804569313c0213be215495e994a72c6b
4
+ data.tar.gz: ff65b97cd663f7cacd18757aad849725c18c9e036563e72bff8185e894dd8c47
5
5
  SHA512:
6
- metadata.gz: 0240ae6516cbc159ae33925ad991e74b958ddbbe19c3806fdf6cde01ba7e625ae6a530184cde8a3e51d5e7748944eba105fec439cde7332910bce2e027f2b5d7
7
- data.tar.gz: 80283b4cb1950832efac7b7713f660bab1d106adcc0dbeb320c385570a95cc5fd8e6f88614fd09fabdbc44081369300bc6e4e88ade905125edf5caad4ac14c74
6
+ metadata.gz: 94a2e3468724372bbf8478f3615b3195a6182a0775dd4ab45be19d9b0a57cdb0af7e85d7d990faa2456cbdc66b23e58b71af7a3fa487fd9e021ffb6175f4edd3
7
+ data.tar.gz: 1bbacd96a15484f01b193394addcc354afb9b35364d9eedf388b0dfce1dd24dafefdc9b7fee46fb1862e5212a50bddb325e3fbe0c8c0808bc254527b2a2e86b7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.23.0](https://github.com/bamr87/zer0-mistakes/compare/v1.22.0...v1.23.0) (2026-06-30)
9
+
10
+
11
+ ### Features
12
+
13
+ * **automation:** issue-triage→PR autopilot (off by default) ([#243](https://github.com/bamr87/zer0-mistakes/issues/243)) ([229e076](https://github.com/bamr87/zer0-mistakes/commit/229e07654915a9a0e401b30796c4f6c078eda926))
14
+ * **automation:** verify-and-close lane — autopilot closes human issues fixed on main when CI is green ([#247](https://github.com/bamr87/zer0-mistakes/issues/247)) ([7cc7cb0](https://github.com/bamr87/zer0-mistakes/commit/7cc7cb0cdada7f64d59f46c2f4c3a7e88afbde6b))
15
+ * **layouts:** add color_mode_default config knob with FOUC prevention ([#253](https://github.com/bamr87/zer0-mistakes/issues/253)) ([55bee97](https://github.com/bamr87/zer0-mistakes/commit/55bee970686c10a08aeed15bff43c3b66b5293e7))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **ci:** make claude-run truly OAuth-first — drop API key whenever an OAuth token is set ([#248](https://github.com/bamr87/zer0-mistakes/issues/248)) ([d2b44d2](https://github.com/bamr87/zer0-mistakes/commit/d2b44d2766f5c8543f4c5d8ddb79015e726f88fd))
21
+ * **ci:** pass -R to gh pr view in the self-repair opt-in gate ([#260](https://github.com/bamr87/zer0-mistakes/issues/260)) ([67d67d7](https://github.com/bamr87/zer0-mistakes/commit/67d67d74146366afe6d495f529ba2c86a4513e18))
22
+ * **includes:** existence-guard component-showcase demo links ([#219](https://github.com/bamr87/zer0-mistakes/issues/219)) ([#256](https://github.com/bamr87/zer0-mistakes/issues/256)) ([ef15e79](https://github.com/bamr87/zer0-mistakes/commit/ef15e79dc64db0dc7e721ca10bb7c1e826612473))
23
+ * **layouts:** existence-gate /authors/ breadcrumb in author.html for remote-theme consumers ([#252](https://github.com/bamr87/zer0-mistakes/issues/252)) ([cae21a7](https://github.com/bamr87/zer0-mistakes/commit/cae21a7107b1875ac876f9e30cf71590ac7b0cc5))
24
+ * **search:** existence-gate search modal form action for remote-theme consumers ([#257](https://github.com/bamr87/zer0-mistakes/issues/257)) ([b915c4d](https://github.com/bamr87/zer0-mistakes/commit/b915c4dead5eff3d6a2986720ebacdcf2bed5e33))
25
+ * **skins:** map dark + contrast skins to their background assets ([#245](https://github.com/bamr87/zer0-mistakes/issues/245)) ([fb8808e](https://github.com/bamr87/zer0-mistakes/commit/fb8808e5ec754785b8239b01bccfab99569b70fa)), closes [#240](https://github.com/bamr87/zer0-mistakes/issues/240)
26
+
27
+ ## [Unreleased]
28
+
29
+ ### Bug Fixes
30
+
31
+ * **includes:** existence-guard component-showcase demo links — breadcrumb and list-group items now render as real links when the target page exists in the build (`/docs/`, `/docs/customization/`, `/categories/`, `/tags/`) and as plain text when absent; removes inert `href="#"` / `onclick="return false;"` workaround ([#219](https://github.com/bamr87/zer0-mistakes/issues/219)) (evidence: [`test/visual/evidence/component-showcase/`](test/visual/evidence/component-showcase/README.md))
32
+
33
+ ### Tests
34
+
35
+ * **navigation:** regression spec for section-sidebar /tags/ existence gate — asserts "Browse All Tags" and "View All Tags" links only render when /tags/ page is present in the build (issue #218, `test/visual/section-sidebar-tags-gate.spec.js`)
36
+ ### Features
37
+
38
+ - **color_mode_default config knob** — new `color_mode_default` setting (`dark` | `light` | `auto`, default `auto`) in `_config.yml` controls Bootstrap's `data-bs-theme` both server-side (in `_layouts/root.html`) and client-side. An early inline script (`_includes/core/color-mode-init.html`, loaded before Bootstrap CSS) applies the correct theme before any `[data-bs-theme]` selector is evaluated, preventing FOUC. `localStorage["theme"]` (the Appearance panel override) always wins over the config default. `auto` follows `prefers-color-scheme` — backward-compatible with the previous behaviour. (evidence: [`test/visual/evidence/color-mode-default/`](test/visual/evidence/color-mode-default/README.md))
39
+ ### Bug Fixes
40
+
41
+ * **search:** existence-gate search modal form action to `/sitemap/` only when that page is present in the build; falls back to `#` (safe no-op) for remote-theme Pages consumers without a `/sitemap/` stub (closes [#202](https://github.com/bamr87/zer0-mistakes/issues/202))
42
+ * **layouts:** existence-gate `/authors/` breadcrumb link in author.html for remote-theme consumers ([#204](https://github.com/bamr87/zer0-mistakes/issues/204))
43
+ ### Tests
44
+
45
+ * **tests:** add negative-path smoke tests for AI chat render guard — asserts FAB/panel are absent when `proxy_ready: false` and no `api_key` is set (closes #168)
46
+
8
47
  ## [1.22.0](https://github.com/bamr87/zer0-mistakes/compare/v1.21.0...v1.22.0) (2026-06-26)
9
48
 
10
49
 
data/_data/ai.yml ADDED
@@ -0,0 +1,10 @@
1
+ # =============================================================================
2
+ # _data/ai.yml — one place the AI model is configured for zer0-mistakes agents
3
+ # -----------------------------------------------------------------------------
4
+ # Read by .github/actions/claude-run (the issue-autopilot's AI step). Change the
5
+ # model here and every autopilot agent call uses it. Auth is NOT here — it comes
6
+ # from the CLAUDE_CODE_OAUTH_TOKEN (preferred) or ANTHROPIC_API_KEY secret.
7
+ # =============================================================================
8
+ provider: anthropic
9
+ model: claude-opus-4-8
10
+ max_tokens: 8000
data/_data/backlog.yml CHANGED
@@ -62,8 +62,8 @@
62
62
 
63
63
  meta:
64
64
  title: "zer0-mistakes Backlog"
65
- updated: 2026-06-26
66
- next_id: 32
65
+ updated: 2026-06-29
66
+ next_id: 33
67
67
 
68
68
  tasks:
69
69
  # --- Housekeeping (seeded so the loop has work on day one) ------------------
@@ -270,7 +270,7 @@ tasks:
270
270
 
271
271
  - id: T-010
272
272
  title: "Complete v1.9 quickstart docs rewrite with getting-started guide and screenshots"
273
- status: open
273
+ status: done
274
274
  priority: P2
275
275
  area: docs
276
276
  risk: low
@@ -286,9 +286,9 @@ tasks:
286
286
  - "At least one guide beyond `bare-minimum.md` covers the standard fork/remote-theme workflow."
287
287
  - "Each guide includes at least one screenshot stored under `assets/images/docs/quickstart/`."
288
288
  - "All new pages pass `docs-validate.yml` front-matter and link checks."
289
- links: { issue: null, pr: null, roadmap: "1.9" }
289
+ links: { issue: 126, pr: null, roadmap: "1.9" }
290
290
  created: 2026-06-01
291
- updated: 2026-06-01
291
+ updated: 2026-06-29
292
292
 
293
293
  - id: T-011
294
294
  title: "Add Ruby unit tests for uncovered _plugins/ generators"
@@ -358,7 +358,7 @@ tasks:
358
358
 
359
359
  - id: T-013
360
360
  title: "Refresh Playwright snapshot baselines and re-arm the pixel gate"
361
- status: open
361
+ status: done
362
362
  priority: P2
363
363
  area: tests
364
364
  risk: standard
@@ -373,9 +373,9 @@ tasks:
373
373
  - "Baselines under `test/visual/snapshots/` regenerated and committed."
374
374
  - "`continue-on-error: true` removed from the snapshot step in `ci.yml`."
375
375
  - "Snapshot tier passes in CI on an unchanged follow-up commit."
376
- links: { issue: null, pr: null, roadmap: "1.13" }
376
+ links: { issue: 135, pr: null, roadmap: "1.13" }
377
377
  created: 2026-06-10
378
- updated: 2026-06-10
378
+ updated: 2026-06-29
379
379
 
380
380
  - id: T-014
381
381
  title: "Re-arm the docs link-check gate (remove warn-only || true)"
@@ -538,7 +538,7 @@ tasks:
538
538
 
539
539
  - id: T-020
540
540
  title: "CI coverage for installer interactive wizard and upgrade path (coverage rank #2)"
541
- status: open
541
+ status: done
542
542
  priority: P2
543
543
  area: tests
544
544
  risk: standard
@@ -554,13 +554,13 @@ tasks:
554
554
  - "Wizard prompt helpers exercised non-interactively (piped answers) in a test_install_* suite."
555
555
  - "An upgrade fixture covers detect→migrate→verify for at least one version gap."
556
556
  - "Suites run in CI via the standard installer e2e discovery (test_install_*.sh)."
557
- links: { issue: null, pr: null, roadmap: "1.14" }
557
+ links: { issue: 147, pr: null, roadmap: "1.14" }
558
558
  created: 2026-06-12
559
- updated: 2026-06-12
559
+ updated: 2026-06-29
560
560
 
561
561
  - id: T-021
562
562
  title: "Vendor cytoscape.js so the Obsidian graph works without a CDN"
563
- status: open
563
+ status: done
564
564
  priority: P3
565
565
  area: feat
566
566
  risk: standard
@@ -577,9 +577,9 @@ tasks:
577
577
  - "cytoscape.min.js committed under assets/vendor/ and referenced via a local path."
578
578
  - "full-graph.html no longer references cdn.jsdelivr.net."
579
579
  - "The graph page renders nodes/edges with the vendored file (no network)."
580
- links: { issue: null, pr: null, roadmap: null }
580
+ links: { issue: 152, pr: null, roadmap: null }
581
581
  created: 2026-06-13
582
- updated: 2026-06-13
582
+ updated: 2026-06-29
583
583
 
584
584
  # --- 2026-06-15 audit -------------------------------------------------------
585
585
 
@@ -637,7 +637,7 @@ tasks:
637
637
 
638
638
  - id: T-024
639
639
  title: "Unit tests for scripts/content-review.rb scoring engine"
640
- status: open
640
+ status: done
641
641
  priority: P2
642
642
  area: tests
643
643
  risk: standard
@@ -657,13 +657,13 @@ tasks:
657
657
  - "At least one test verifies the `--strict` flag exits non-zero when the fail threshold is crossed."
658
658
  - "Tests run under `LC_ALL=C` (locale-independence parity with T-015 guard)."
659
659
  - "`./scripts/bin/test` stays green with the new tests included."
660
- links: { issue: null, pr: null, roadmap: null }
660
+ links: { issue: 166, pr: null, roadmap: null }
661
661
  created: 2026-06-15
662
- updated: 2026-06-15
662
+ updated: 2026-06-29
663
663
 
664
664
  - id: T-025
665
665
  title: "Playwright smoke tests for the site-wide search modal"
666
- status: open
666
+ status: done
667
667
  priority: P2
668
668
  area: tests
669
669
  risk: standard
@@ -683,13 +683,13 @@ tasks:
683
683
  - "A result-population check: query matching a known post title (e.g. 'Hello World') yields at least one result `<a>` element."
684
684
  - "Mutual-exclusion guard: opening search while Settings offcanvas is open closes the offcanvas first (no stacked backdrops)."
685
685
  - "Spec runs in the smoke project (`npx playwright test --project=smoke`) without a network request to an external index."
686
- links: { issue: null, pr: null, roadmap: null }
686
+ links: { issue: 167, pr: 208, roadmap: null }
687
687
  created: 2026-06-15
688
- updated: 2026-06-15
688
+ updated: 2026-06-29
689
689
 
690
690
  - id: T-026
691
691
  title: "Playwright smoke test for AI chat widget render guard and FAB visibility"
692
- status: open
692
+ status: done
693
693
  priority: P3
694
694
  area: tests
695
695
  risk: standard
@@ -710,9 +710,9 @@ tasks:
710
710
  - "A mock-config test (inject `data-ai-chat-enabled` attribute or use a fixture page with the widget force-enabled) verifies the FAB renders and the chat panel toggles open/closed on click."
711
711
  - "The test passes in the smoke project without a live AI backend."
712
712
  - "`./scripts/bin/test` (including the Playwright smoke tier) stays green."
713
- links: { issue: null, pr: null, roadmap: null }
713
+ links: { issue: 168, pr: 250, roadmap: null }
714
714
  created: 2026-06-15
715
- updated: 2026-06-15
715
+ updated: 2026-06-29
716
716
 
717
717
  # --- Issue intake 2026-06-26: remote-theme 404 cluster + docs ----------------
718
718
  # Cap: 5 of 7 open issues triaged this run. Deferred (next run): #203 (consumer
@@ -721,7 +721,7 @@ tasks:
721
721
 
722
722
  - id: T-027
723
723
  title: "Theme chrome injects unconditional links (/authors/, /news/, /tags/, /posts/, graph) that 404 for remote-theme consumers"
724
- status: open
724
+ status: done
725
725
  priority: P2
726
726
  area: a11y
727
727
  risk: standard
@@ -740,13 +740,13 @@ tasks:
740
740
  - "Theme-injected chrome links render only when their target exists (mirror the footer Quick-Links existence guard)."
741
741
  - "A link check over a built remote-theme `_site` reports no theme-injected 404s."
742
742
  - "No edit to `lib/jekyll-theme-zer0/version.rb` or release files."
743
- links: { issue: 204, pr: null, roadmap: null }
743
+ links: { issue: 204, pr: 252, roadmap: null }
744
744
  created: 2026-06-26
745
- updated: 2026-06-26
745
+ updated: 2026-06-29
746
746
 
747
747
  - id: T-028
748
748
  title: "remote_theme Pages consumers 404 on /search.json and /sitemap/ (generator is plugin-only)"
749
- status: open
749
+ status: done
750
750
  priority: P2
751
751
  area: feat
752
752
  risk: standard
@@ -764,13 +764,13 @@ tasks:
764
764
  - "On a remote-theme Pages build, the navbar search resolves (no /search.json 404) or degrades gracefully."
765
765
  - "The /sitemap/ link resolves (no 404) for a remote-theme consumer."
766
766
  - "No new runtime dependency; no edit to version/release files."
767
- links: { issue: 202, pr: null, roadmap: null }
767
+ links: { issue: 202, pr: 257, roadmap: null }
768
768
  created: 2026-06-26
769
- updated: 2026-06-26
769
+ updated: 2026-06-29
770
770
 
771
771
  - id: T-029
772
772
  title: "Guard the unconditional /tags/ link in section-sidebar.html for remote-theme consumers"
773
- status: open
773
+ status: done
774
774
  priority: P2
775
775
  area: feat
776
776
  risk: low
@@ -786,13 +786,13 @@ tasks:
786
786
  - "The sidebar 'View All Tags' link renders only when the tags index exists (or a documented site flag is set)."
787
787
  - "Remote-theme consumers no longer 404 from the sidebar link; the Jekyll build stays green."
788
788
  - "A `test/visual/*.spec.js` regression asserts the conditional render (theme-ui / visual-evidence)."
789
- links: { issue: 218, pr: null, roadmap: null }
789
+ links: { issue: 218, pr: 254, roadmap: null }
790
790
  created: 2026-06-26
791
- updated: 2026-06-26
791
+ updated: 2026-06-29
792
792
 
793
793
  - id: T-030
794
794
  title: "component-showcase.html hardcodes demo links (/docs/, /pages/, /docs/customization/) that 404 for consumers"
795
- status: open
795
+ status: done
796
796
  priority: P3
797
797
  area: feat
798
798
  risk: low
@@ -809,13 +809,13 @@ tasks:
809
809
  - "The showcase's demo links are parameterized or existence-guarded (no hardcoded absolute paths that can 404)."
810
810
  - "Including the showcase on a consumer site produces no theme-injected 404s; the Jekyll build stays green."
811
811
  - "A `test/visual/*.spec.js` regression covers the parameterized/guarded links (theme-ui / visual-evidence)."
812
- links: { issue: 219, pr: null, roadmap: null }
812
+ links: { issue: 219, pr: 256, roadmap: null }
813
813
  created: 2026-06-26
814
- updated: 2026-06-26
814
+ updated: 2026-06-29
815
815
 
816
816
  - id: T-031
817
817
  title: "Document the safe-mode build-overlay recipe for consumers building outside GitHub Pages"
818
- status: open
818
+ status: done
819
819
  priority: P3
820
820
  area: docs
821
821
  risk: low
@@ -832,6 +832,34 @@ tasks:
832
832
  - "A docs page/section under `pages/_docs/` documents the build-overlay recipe (clone -> overlay -> strip _plugins -> strict build)."
833
833
  - "It is cross-linked from the remote-theme/deploy docs and passes markdownlint + the content-review deterministic tier."
834
834
  - "No code change; docs only."
835
- links: { issue: 220, pr: null, roadmap: null }
835
+ links: { issue: 220, pr: 251, roadmap: null }
836
836
  created: 2026-06-26
837
837
  updated: 2026-06-26
838
+
839
+ - id: T-032
840
+ title: "Add color_mode_default config knob (dark|light|auto) with FOUC prevention"
841
+ status: done
842
+ priority: P2
843
+ area: feat
844
+ risk: low
845
+ effort: S
846
+ source: issue
847
+ route: theme-ui
848
+ summary: >-
849
+ Implement a color_mode_default config key (values dark|light|auto, default auto).
850
+ Server-side: root.html reads site.color_mode_default and sets data-bs-theme
851
+ accordingly (auto resolves to dark as SSR fallback). Client-side: an early inline
852
+ script (_includes/core/color-mode-init.html, before Bootstrap CSS) applies the
853
+ correct theme respecting both the config default and a localStorage["theme"] override,
854
+ preventing FOUC. auto follows prefers-color-scheme — backward-compatible.
855
+ acceptance:
856
+ - "site.color_mode_default: dark|light|auto is documented in _config.yml."
857
+ - "root.html server-renders data-bs-theme from the config (not hardcoded 'dark')."
858
+ - "An early inline script before Bootstrap CSS prevents FOUC for all three modes."
859
+ - "localStorage['theme'] override wins over the config default."
860
+ - "auto follows prefers-color-scheme; backward-compatible with the previous behaviour."
861
+ - "Regression spec test/visual/color-mode-default.spec.js passes (smoke tier)."
862
+ - "Before/after evidence committed under test/visual/evidence/color-mode-default/."
863
+ links: { issue: 241, pr: 253, roadmap: null }
864
+ created: 2026-06-29
865
+ updated: 2026-06-29
@@ -70,6 +70,10 @@
70
70
  url: /docs/deployment/
71
71
  - title: GitHub Pages
72
72
  url: /docs/deployment/github-pages/
73
+ - title: Remote-Theme Consumer Checklist
74
+ url: /docs/deployment/remote-theme-checklist/
75
+ - title: Safe-Mode Build Overlay
76
+ url: /docs/deployment/build-overlay/
73
77
  - title: Netlify
74
78
  url: /docs/deployment/netlify/
75
79
  - title: Custom Domain
@@ -99,3 +99,27 @@ skins:
99
99
  gradient: "assets/backgrounds/gradients/sunrise.svg"
100
100
  pattern: "assets/backgrounds/patterns/sunrise.svg"
101
101
  noise: "assets/backgrounds/noise/sunrise.svg"
102
+
103
+ # `dark` is the DEFAULT theme_skin, and `contrast` is advertised as valid, but
104
+ # neither was mapped here — so the SVG-background feature had no entry for them
105
+ # (issue #240). The gradient/pattern/noise assets already ship; these entries
106
+ # wire them up. Colors are taken from each skin's gradient stops.
107
+ dark:
108
+ label: "Dark"
109
+ colors:
110
+ primary: "#1a1a2e"
111
+ secondary: "#16213e"
112
+ accent: "#0f3460"
113
+ gradient: "assets/backgrounds/gradients/dark.svg"
114
+ pattern: "assets/backgrounds/patterns/dark.svg"
115
+ noise: "assets/backgrounds/noise/dark.svg"
116
+
117
+ contrast:
118
+ label: "Contrast"
119
+ colors:
120
+ primary: "#111111"
121
+ secondary: "#333333"
122
+ accent: "#ffcc00"
123
+ gradient: "assets/backgrounds/gradients/contrast.svg"
124
+ pattern: "assets/backgrounds/patterns/contrast.svg"
125
+ noise: "assets/backgrounds/noise/contrast.svg"
@@ -25,11 +25,12 @@
25
25
  - Bootstrap 5.3 CSS & JS (loaded by theme)
26
26
  - Bootstrap Icons (loaded by theme)
27
27
 
28
- Note: The breadcrumb and list-group links are DEMO links only — they are
29
- intentionally inert (href="#" with onclick="return false;") so the
30
- showcase never injects site-absolute paths (/docs/, /pages/, ...) that
31
- would 404 on consumer sites that lack those exact routes (issue #219).
32
- Do not point them at absolute paths.
28
+ Note: The breadcrumb and list-group demo links are existence-guarded (issue #219).
29
+ Each target URL is resolved against site.html_pages (and collection docs
30
+ for collection-backed routes). When the page exists in the build the link
31
+ is real; when it does not the label renders as plain text. This keeps the
32
+ showcase safe for remote-theme consumers who may lack /docs/, /posts/,
33
+ /categories/, /tags/, or /docs/customization/.
33
34
  ===================================================================
34
35
  -->
35
36
  {% assign sections = include.sections | default: "alerts,buttons,badges,cards,accordion,tabs,progress,breadcrumbs,table,tooltips,list-group" %}
@@ -326,11 +327,29 @@ theme: jekyll-theme-zer0</code></pre>
326
327
  <!-- ===== BREADCRUMBS ===== -->
327
328
  <h3 id="showcase-breadcrumbs">Breadcrumbs</h3>
328
329
  <p>Hierarchical navigation trail showing current page position:</p>
330
+ {% comment %} Existence-guard the demo breadcrumb links so they never 404 on
331
+ remote-theme consumers that lack /docs/ or /docs/customization/. When the
332
+ target page is in the build the crumb is a real link; otherwise plain text.
333
+ Home (/) is always present. Pattern mirrors _includes/navigation/breadcrumbs.html. {% endcomment %}
334
+ {% assign _demo_docs_page = site.html_pages | where: "url", "/docs/" | first %}
335
+ {% unless _demo_docs_page %}
336
+ {% for _col in site.collections %}
337
+ {% assign _demo_docs_page = _col.docs | where: "url", "/docs/" | first %}
338
+ {% if _demo_docs_page %}{% break %}{% endif %}
339
+ {% endfor %}
340
+ {% endunless %}
341
+ {% assign _demo_custom_page = site.html_pages | where: "url", "/docs/customization/" | first %}
342
+ {% unless _demo_custom_page %}
343
+ {% for _col in site.collections %}
344
+ {% assign _demo_custom_page = _col.docs | where: "url", "/docs/customization/" | first %}
345
+ {% if _demo_custom_page %}{% break %}{% endif %}
346
+ {% endfor %}
347
+ {% endunless %}
329
348
  <nav aria-label="breadcrumb example" class="mb-4">
330
349
  <ol class="breadcrumb">
331
- <li class="breadcrumb-item"><a href="#" class="text-decoration-none" onclick="return false;"><i class="bi bi-house me-1"></i>Home</a></li>
332
- <li class="breadcrumb-item"><a href="#" class="text-decoration-none" onclick="return false;">Documentation</a></li>
333
- <li class="breadcrumb-item"><a href="#" class="text-decoration-none" onclick="return false;">Customization</a></li>
350
+ <li class="breadcrumb-item"><a href="{{ '/' | relative_url }}" class="text-decoration-none"><i class="bi bi-house me-1"></i>Home</a></li>
351
+ <li class="breadcrumb-item">{% if _demo_docs_page %}<a href="{{ '/docs/' | relative_url }}" class="text-decoration-none">Documentation</a>{% else %}Documentation{% endif %}</li>
352
+ <li class="breadcrumb-item">{% if _demo_custom_page %}<a href="{{ '/docs/customization/' | relative_url }}" class="text-decoration-none">Customization</a>{% else %}Customization{% endif %}</li>
334
353
  <li class="breadcrumb-item active" aria-current="page">Theme Colors</li>
335
354
  </ol>
336
355
  </nav>
@@ -419,25 +438,67 @@ document.addEventListener('DOMContentLoaded', () => {
419
438
  <!-- ===== LIST GROUP ===== -->
420
439
  <h3 id="showcase-list-group">List Group</h3>
421
440
  <p>Organized content lists with icons, badges, and action states:</p>
441
+ {% comment %} Existence-guard the demo list-group links (issue #219). Each target
442
+ URL is resolved against site.html_pages + collection docs. When present the
443
+ item is a real <a>; when absent it renders as a non-linking <div> so no 404
444
+ is injected on remote-theme consumers that lack /posts/, /docs/, /categories/,
445
+ or /tags/. Pattern mirrors section-sidebar.html and author-bio.html. {% endcomment %}
446
+ {% assign _demo_posts_page = site.html_pages | where: "url", "/posts/" | first %}
447
+ {% unless _demo_posts_page %}
448
+ {% for _col in site.collections %}
449
+ {% assign _demo_posts_page = _col.docs | where: "url", "/posts/" | first %}
450
+ {% if _demo_posts_page %}{% break %}{% endif %}
451
+ {% endfor %}
452
+ {% endunless %}
453
+ {% assign _demo_cats_page = site.html_pages | where: "url", "/categories/" | first %}
454
+ {% unless _demo_cats_page %}
455
+ {% for _col in site.collections %}
456
+ {% assign _demo_cats_page = _col.docs | where: "url", "/categories/" | first %}
457
+ {% if _demo_cats_page %}{% break %}{% endif %}
458
+ {% endfor %}
459
+ {% endunless %}
460
+ {% assign _demo_tags_page = site.html_pages | where: "url", "/tags/" | first %}
461
+ {% unless _demo_tags_page %}
462
+ {% for _col in site.collections %}
463
+ {% assign _demo_tags_page = _col.docs | where: "url", "/tags/" | first %}
464
+ {% if _demo_tags_page %}{% break %}{% endif %}
465
+ {% endfor %}
466
+ {% endunless %}
422
467
  <div class="row g-4 mb-4">
423
468
  <div class="col-md-6">
424
469
  <div class="list-group">
425
- <a href="#" onclick="return false;" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
470
+ {% if _demo_posts_page %}
471
+ <a href="{{ '/posts/' | relative_url }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
472
+ {% else %}
473
+ <div class="list-group-item d-flex justify-content-between align-items-center">
474
+ {% endif %}
426
475
  <span><i class="bi bi-journal-richtext text-primary me-2"></i>Blog Posts</span>
427
476
  <span class="badge bg-primary rounded-pill">{{ site.posts.size | default: "12" }}</span>
428
- </a>
429
- <a href="#" onclick="return false;" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
477
+ {% if _demo_posts_page %}</a>{% else %}</div>{% endif %}
478
+ {% if _demo_docs_page %}
479
+ <a href="{{ '/docs/' | relative_url }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
480
+ {% else %}
481
+ <div class="list-group-item d-flex justify-content-between align-items-center">
482
+ {% endif %}
430
483
  <span><i class="bi bi-book text-success me-2"></i>Documentation</span>
431
484
  <span class="badge bg-success rounded-pill">{{ site.documents.size | default: "8" }}</span>
432
- </a>
433
- <a href="#" onclick="return false;" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
485
+ {% if _demo_docs_page %}</a>{% else %}</div>{% endif %}
486
+ {% if _demo_cats_page %}
487
+ <a href="{{ '/categories/' | relative_url }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
488
+ {% else %}
489
+ <div class="list-group-item d-flex justify-content-between align-items-center">
490
+ {% endif %}
434
491
  <span><i class="bi bi-collection text-info me-2"></i>Categories</span>
435
492
  <span class="badge bg-info rounded-pill">{{ site.categories.size | default: "6" }}</span>
436
- </a>
437
- <a href="#" onclick="return false;" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
493
+ {% if _demo_cats_page %}</a>{% else %}</div>{% endif %}
494
+ {% if _demo_tags_page %}
495
+ <a href="{{ '/tags/' | relative_url }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
496
+ {% else %}
497
+ <div class="list-group-item d-flex justify-content-between align-items-center">
498
+ {% endif %}
438
499
  <span><i class="bi bi-tags text-warning me-2"></i>Tags</span>
439
500
  <span class="badge bg-warning text-dark rounded-pill">{{ site.tags.size | default: "24" }}</span>
440
- </a>
501
+ {% if _demo_tags_page %}</a>{% else %}</div>{% endif %}
441
502
  </div>
442
503
  </div>
443
504
  <div class="col-md-6">
@@ -2,19 +2,43 @@
2
2
  ===================================================================
3
3
  SEARCH MODAL - Site-wide Search Popup
4
4
  ===================================================================
5
-
5
+
6
6
  File: search-modal.html
7
7
  Path: _includes/components/search-modal.html
8
8
  Purpose: Provide a modal search experience with keyboard shortcut support
9
-
9
+
10
10
  Notes:
11
- - Uses /sitemap/ for query-based search results
11
+ - Form action targets /sitemap/ when that page exists in the build;
12
+ falls back to /sitemap.xml if available, otherwise '#' (safe no-op).
13
+ JS (search-modal.js) always intercepts submit and keeps results
14
+ in-modal, so the action is only a no-JS fallback. The gate here
15
+ prevents a dead link on remote-theme Pages builds that lack /sitemap/.
12
16
  - Keyboard shortcut handled by navigation keyboard module ("/")
13
17
  ===================================================================
14
18
  -->
15
19
 
16
20
  {%- assign ui_text = site.data.ui-text[site.locale] | default: site.data.ui-text.en -%}
17
21
  {%- assign search_placeholder = ui_text.search_placeholder_text | default: "Enter your search term..." -%}
22
+ {%- comment -%}
23
+ Existence-gate the form action to /sitemap/ only when that page is present
24
+ in the build (mirroring the footer Quick Links guard, and the section-sidebar
25
+ /tags/ gate). Remote-theme Pages consumers that haven't committed a /sitemap/
26
+ stub get a safe '#' fallback; the JS always intercepts submit anyway, so this
27
+ only affects the no-JS code path.
28
+ {%- endcomment -%}
29
+ {%- assign _search_sitemap_url = "/sitemap/" -%}
30
+ {%- assign _search_sitemap_page = site.html_pages | where: "url", _search_sitemap_url | first -%}
31
+ {%- unless _search_sitemap_page -%}
32
+ {%- for col in site.collections -%}
33
+ {%- assign _search_sitemap_page = col.docs | where: "url", _search_sitemap_url | first -%}
34
+ {%- if _search_sitemap_page -%}{%- break -%}{%- endif -%}
35
+ {%- endfor -%}
36
+ {%- endunless -%}
37
+ {%- if _search_sitemap_page -%}
38
+ {%- assign _search_form_action = "/sitemap/" | relative_url -%}
39
+ {%- else -%}
40
+ {%- assign _search_form_action = "#" -%}
41
+ {%- endif -%}
18
42
 
19
43
  <div class="modal fade search-modal" id="siteSearchModal" tabindex="-1" aria-labelledby="siteSearchModalLabel" aria-hidden="true">
20
44
  <div class="modal-dialog modal-dialog-centered">
@@ -26,7 +50,7 @@
26
50
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
27
51
  </div>
28
52
  <div class="modal-body">
29
- <form action="{{ '/sitemap/' | relative_url }}" method="get" data-search-form>
53
+ <form action="{{ _search_form_action }}" method="get" data-search-form>
30
54
  <label class="visually-hidden" for="site-search-input">Search</label>
31
55
  <div class="input-group">
32
56
  <span class="input-group-text">
@@ -0,0 +1,35 @@
1
+ {%- comment -%}
2
+ Component: color-mode-init
3
+ Path: _includes/core/color-mode-init.html
4
+ Purpose: Early inline script that applies data-bs-theme BEFORE Bootstrap CSS
5
+ loads, preventing FOUC (flash of unstyled/wrong-themed content).
6
+ Params: none — reads data-color-mode-default from the <html> element,
7
+ which root.html sets from site.color_mode_default.
8
+ Depends on: root.html (sets data-color-mode-default attribute server-side)
9
+ Notes: Must be included BEFORE any Bootstrap CSS link tag. The script
10
+ is tiny, synchronous, and runs inline before the browser requests
11
+ external stylesheets, so the correct theme is already applied
12
+ when Bootstrap's [data-bs-theme] selectors are first evaluated.
13
+
14
+ Priority order (highest → lowest):
15
+ 1. localStorage["theme"] — user's explicit override via Appearance panel
16
+ 2. data-color-mode-default — site.color_mode_default config value
17
+ 3. "auto" fallback — resolves via prefers-color-scheme
18
+ {%- endcomment -%}
19
+ <script>
20
+ (function () {
21
+ 'use strict';
22
+ try {
23
+ var root = document.documentElement;
24
+ var configDefault = root.getAttribute('data-color-mode-default') || 'auto';
25
+ var stored = window.localStorage.getItem('theme');
26
+ var mode = stored || configDefault;
27
+ if (mode === 'auto') {
28
+ root.setAttribute('data-bs-theme',
29
+ window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
30
+ } else {
31
+ root.setAttribute('data-bs-theme', mode);
32
+ }
33
+ } catch (e) { /* localStorage / matchMedia unavailable — server-rendered value stands */ }
34
+ })();
35
+ </script>
@@ -127,6 +127,15 @@ window.MathJax = {
127
127
  <!-- CSS FRAMEWORKS AND LIBRARIES -->
128
128
  <!-- ================================ -->
129
129
 
130
+ <!-- ========================== -->
131
+ <!-- COLOR MODE INIT (FOUC FIX) -->
132
+ <!-- ========================== -->
133
+ <!-- Must run BEFORE Bootstrap CSS so the correct data-bs-theme is applied
134
+ when [data-bs-theme=dark/light] selectors are first evaluated.
135
+ Reads site.color_mode_default (via data-color-mode-default attr on <html>)
136
+ and falls back to localStorage["theme"] for the user's explicit preference. -->
137
+ {% include core/color-mode-init.html %}
138
+
130
139
  <!-- ========================== -->
131
140
  <!-- BOOTSTRAP 5 FRAMEWORK -->
132
141
  <!-- ========================== -->
@@ -35,6 +35,11 @@
35
35
  {%- endif -%}
36
36
 
37
37
  {%- comment -%}
38
+ NOTE: The data-bs-theme FOUC-prevention script lives in
39
+ _includes/core/color-mode-init.html (included from head.html BEFORE Bootstrap
40
+ CSS) so the correct theme is applied before any [data-bs-theme] selectors are
41
+ evaluated. Do not duplicate it here.
42
+
38
43
  Restore any user-saved Appearance overrides before paint to avoid a flash
39
44
  of the default palette. The Appearance panel (Track 6) writes JSON like
40
45
  {"primary":"#ff5722"} to localStorage["zer0-appearance"].