jekyll-theme-zer0 0.22.0 → 0.22.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +236 -0
  3. data/README.md +66 -19
  4. data/_data/navigation/admin.yml +53 -0
  5. data/_data/theme_backgrounds.yml +121 -0
  6. data/_includes/components/admin-tabs.html +59 -0
  7. data/_includes/components/analytics-dashboard.html +232 -0
  8. data/_includes/components/background-customizer.html +159 -0
  9. data/_includes/components/background-settings.html +137 -0
  10. data/_includes/components/collection-manager.html +151 -0
  11. data/_includes/components/component-showcase.html +452 -0
  12. data/_includes/components/config-editor.html +207 -0
  13. data/_includes/components/config-viewer.html +479 -0
  14. data/_includes/components/env-dashboard.html +154 -0
  15. data/_includes/components/feature-card.html +94 -0
  16. data/_includes/components/info-section.html +172 -149
  17. data/_includes/components/js-cdn.html +4 -1
  18. data/_includes/components/nav-editor.html +99 -0
  19. data/_includes/components/setup-banner.html +28 -0
  20. data/_includes/components/setup-check.html +53 -0
  21. data/_includes/components/svg-background.html +42 -0
  22. data/_includes/components/theme-customizer.html +46 -0
  23. data/_includes/content/seo.html +68 -135
  24. data/_includes/core/footer.html +1 -1
  25. data/_includes/core/head.html +3 -2
  26. data/_includes/core/header.html +14 -7
  27. data/_includes/landing/landing-install-cards.html +18 -7
  28. data/_includes/navigation/admin-nav.html +95 -0
  29. data/_includes/navigation/navbar.html +43 -5
  30. data/_includes/navigation/sidebar-left.html +1 -1
  31. data/_includes/setup/wizard.html +330 -0
  32. data/_layouts/admin.html +166 -0
  33. data/_layouts/landing.html +23 -9
  34. data/_layouts/root.html +12 -6
  35. data/_layouts/setup.html +73 -0
  36. data/_plugins/preview_image_generator.rb +26 -12
  37. data/_sass/core/_navbar.scss +2 -2
  38. data/_sass/custom.scss +28 -6
  39. data/_sass/theme/_background-mixins.scss +95 -0
  40. data/_sass/theme/_backgrounds.scss +156 -0
  41. data/_sass/theme/_color-modes.scss +2 -1
  42. data/assets/backgrounds/gradients/air.svg +15 -0
  43. data/assets/backgrounds/gradients/aqua.svg +15 -0
  44. data/assets/backgrounds/gradients/contrast.svg +15 -0
  45. data/assets/backgrounds/gradients/dark.svg +15 -0
  46. data/assets/backgrounds/gradients/dirt.svg +15 -0
  47. data/assets/backgrounds/gradients/mint.svg +15 -0
  48. data/assets/backgrounds/gradients/neon.svg +15 -0
  49. data/assets/backgrounds/gradients/plum.svg +15 -0
  50. data/assets/backgrounds/gradients/sunrise.svg +15 -0
  51. data/assets/backgrounds/noise/air.svg +8 -0
  52. data/assets/backgrounds/noise/aqua.svg +8 -0
  53. data/assets/backgrounds/noise/contrast.svg +8 -0
  54. data/assets/backgrounds/noise/dark.svg +8 -0
  55. data/assets/backgrounds/noise/dirt.svg +8 -0
  56. data/assets/backgrounds/noise/mint.svg +8 -0
  57. data/assets/backgrounds/noise/neon.svg +8 -0
  58. data/assets/backgrounds/noise/plum.svg +8 -0
  59. data/assets/backgrounds/noise/sunrise.svg +8 -0
  60. data/assets/backgrounds/patterns/air.svg +7 -0
  61. data/assets/backgrounds/patterns/aqua.svg +7 -0
  62. data/assets/backgrounds/patterns/contrast.svg +4 -0
  63. data/assets/backgrounds/patterns/dark.svg +5 -0
  64. data/assets/backgrounds/patterns/dirt.svg +5 -0
  65. data/assets/backgrounds/patterns/mint.svg +6 -0
  66. data/assets/backgrounds/patterns/neon.svg +6 -0
  67. data/assets/backgrounds/patterns/plum.svg +6 -0
  68. data/assets/backgrounds/patterns/sunrise.svg +5 -0
  69. data/assets/js/background-customizer.js +73 -0
  70. data/assets/js/code-copy.js +18 -47
  71. data/assets/js/config-utility.js +307 -0
  72. data/assets/js/nav-editor.js +39 -0
  73. data/assets/js/palette-generator.js +415 -0
  74. data/assets/js/search-modal.js +31 -11
  75. data/assets/js/setup-wizard.js +306 -0
  76. data/assets/js/skin-editor.js +645 -0
  77. data/assets/js/theme-customizer.js +102 -0
  78. data/assets/js/ui-enhancements.js +15 -24
  79. data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
  80. data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
  81. data/scripts/README.md +45 -0
  82. data/scripts/features/generate-preview-images +297 -7
  83. data/scripts/features/install-preview-generator +51 -33
  84. data/scripts/fork-cleanup.sh +92 -19
  85. data/scripts/github-setup.sh +284 -0
  86. data/scripts/init_setup.sh +0 -1
  87. data/scripts/lib/frontmatter.sh +543 -0
  88. data/scripts/lib/migrate.sh +265 -0
  89. data/scripts/lib/preview_generator.py +607 -32
  90. data/scripts/lint-pages +505 -0
  91. data/scripts/migrate.sh +201 -0
  92. data/scripts/platform/setup-linux.sh +244 -0
  93. data/scripts/platform/setup-macos.sh +187 -0
  94. data/scripts/platform/setup-wsl.sh +196 -0
  95. metadata +71 -6
@@ -0,0 +1,42 @@
1
+ <!--
2
+ ===================================================================
3
+ SVG-BACKGROUND — Apply fffuel-style SVG background to a zone
4
+ ===================================================================
5
+
6
+ File: svg-background.html
7
+ Path: _includes/components/svg-background.html
8
+ Purpose: Adds a CSS class for the background zone and injects
9
+ per-skin CSS custom-property overrides from _config.yml.
10
+
11
+ Parameters:
12
+ zone — "hero" | "body" | "surface" | "footer" (default: "body")
13
+
14
+ Usage:
15
+ include components/svg-background.html zone="hero"
16
+ <div class="zer0-bg-hero"> ... </div>
17
+
18
+ Dependencies:
19
+ - _sass/theme/_backgrounds.scss (utility classes)
20
+ - _data/theme_backgrounds.yml (asset paths)
21
+ - _config.yml theme_background (opacity / blend settings)
22
+ ===================================================================
23
+ -->
24
+
25
+ {% assign zone = include.zone | default: "body" %}
26
+ {% assign skin = site.theme_skin | default: "dark" %}
27
+ {% assign bg = site.theme_background %}
28
+
29
+ {% comment %}
30
+ Inject runtime overrides from _config.yml into inline <style> once per page.
31
+ These override the SCSS defaults so site owners can tune via config alone.
32
+ {% endcomment %}
33
+ {% if bg.enabled != false %}
34
+ <style data-zer0-bg-overrides>
35
+ [data-theme-skin="{{ skin }}"] {
36
+ {% if bg.gradient_opacity %}--zer0-bg-gradient-opacity: {{ bg.gradient_opacity }};{% endif %}
37
+ {% if bg.texture_opacity %}--zer0-bg-texture-opacity: {{ bg.texture_opacity }};{% endif %}
38
+ {% if bg.pattern_opacity %}--zer0-bg-pattern-opacity: {{ bg.pattern_opacity }};{% endif %}
39
+ {% if bg.blend_mode %}--zer0-bg-blend: {{ bg.blend_mode }};{% endif %}
40
+ }
41
+ </style>
42
+ {% endif %}
@@ -0,0 +1,46 @@
1
+ <!--
2
+ ===================================================================
3
+ THEME CUSTOMIZER — Skin preview grid with live class swapping
4
+ ===================================================================
5
+
6
+ File: theme-customizer.html
7
+ Path: _includes/components/theme-customizer.html
8
+ Purpose: Displays all available theme skins in a card grid.
9
+ Clicking a card applies the skin class and highlights it.
10
+ Current skin comes from site.theme_skin in _config.yml.
11
+
12
+ Dependencies: Bootstrap 5 grid/card, Bootstrap Icons
13
+ Used by: pages/_about/settings/theme.md (Skin Preview tab)
14
+ ===================================================================
15
+ -->
16
+
17
+ {% assign current_skin = site.theme_skin | default: "dark" %}
18
+ {% assign skins = "air,aqua,contrast,dark,dirt,neon,mint,plum,sunrise" | split: "," %}
19
+
20
+ <p class="text-body-secondary mb-3">
21
+ Current skin: <strong>{{ current_skin }}</strong>. Click a card to preview (page-level only — does not persist).
22
+ </p>
23
+
24
+ <div class="row g-3" id="skin-grid">
25
+ {% for skin in skins %}
26
+ <div class="col-6 col-md-4 col-lg-3">
27
+ <div class="card h-100 skin-card {% if skin == current_skin %}border-primary{% else %}border-secondary{% endif %}"
28
+ data-skin="{{ skin }}" role="button" tabindex="0" style="cursor:pointer">
29
+ <div class="card-body text-center py-4">
30
+ <i class="bi bi-circle-fill fs-3 mb-2 d-block {% if skin == current_skin %}text-primary{% else %}text-body-secondary{% endif %}"></i>
31
+ <h6 class="card-title mb-1 text-capitalize">{{ skin }}</h6>
32
+ {% if skin == current_skin %}
33
+ <span class="badge bg-primary"><i class="bi bi-check-circle me-1"></i>Active</span>
34
+ {% else %}
35
+ <small class="text-body-tertiary">Click to preview</small>
36
+ {% endif %}
37
+ </div>
38
+ </div>
39
+ </div>
40
+ {% endfor %}
41
+ </div>
42
+
43
+ <div class="alert alert-info mt-3 small" role="alert">
44
+ <i class="bi bi-info-circle me-1"></i>
45
+ To permanently change the skin, update <code>theme_skin</code> in <code>_config.yml</code> or use the <strong>Export YAML</strong> tab.
46
+ </div>
@@ -1,142 +1,75 @@
1
- <!--
2
- file: seo.html
3
- path: _includes/seo.html
4
- includes: none
5
- description: SEO meta tags for the site
6
- purpose: This file is used to generate the SEO meta tags for the site. It is included in the head section of the HTML document.
1
+ <!--
2
+ ===================================================================
3
+ SEO SUPPLEMENT - Additional meta tags not covered by jekyll-seo-tag
4
+ ===================================================================
5
+
6
+ File: seo.html
7
+ Path: _includes/content/seo.html
8
+ Purpose: Outputs ONLY meta tags that the jekyll-seo-tag plugin does
9
+ not generate. The bulk of SEO (title, description, og:*,
10
+ twitter:*, canonical, JSON-LD) is handled by seo tags in
11
+ _layouts/root.html.
12
+
13
+ What this file adds:
14
+ - Custom og:image with preview_images.assets_prefix path normalisation
15
+ for the theme-specific page.preview and page.header.og_image keys.
16
+ When page.image is set, jekyll-seo-tag handles og:image and this
17
+ file skips its own og:image output to avoid duplicate tags.
18
+ - Non-Google site verification tags (Bing, Yandex, Naver, Baidu)
19
+
20
+ Dependencies:
21
+ - jekyll-seo-tag plugin (loaded in _layouts/root.html via seo tags)
22
+ - site.preview_images config in _config.yml
23
+ ===================================================================
7
24
  -->
8
25
 
9
- <!-- -->
10
-
11
- {%- assign seo_url = site.url | append: site.baseurl -%}
12
- {%- assign canonical_url = site.url %}
13
- {% assign title_separator = site.title_separator | default: '-' %}
14
- {%- assign seo_title = page.title | default: site.title | append: " " | append: title_separator | append: " " | append: site.title -%}
15
- {%- assign seo_description = page.description | default: site.description -%}
16
-
17
- {%- assign author = page.author | default: site.author -%}
18
-
19
- <!-- TODO: build author data set for SEO -->
20
-
21
- {%- assign author_twitter = site.author.twitter_username %}
22
-
23
- {%- assign page_large_image = page.header.og_image | default: page.header.overlay_image | default: page.header.image | absolute_url -%}
24
- {%- assign page_large_image = page_large_image | escape -%}
25
-
26
- {%- comment -%} Handle preview image with configurable assets prefix {%- endcomment -%}
27
- {%- assign page_teaser_image = page.preview | default: site.og_image -%}
28
- {%- assign assets_prefix = site.preview_images.assets_prefix | default: '/assets' -%}
29
- {%- assign auto_prefix = site.preview_images.auto_prefix | default: true -%}
30
-
31
- {%- if page_teaser_image contains '://' -%}
32
- {%- comment -%} External URL - use as-is {%- endcomment -%}
33
- {%- elsif auto_prefix and page_teaser_image != blank -%}
34
- {%- unless page_teaser_image contains assets_prefix -%}
35
- {%- assign page_teaser_image = assets_prefix | append: page_teaser_image -%}
36
- {%- endunless -%}
37
- {%- endif -%}
38
- {%- assign page_teaser_image = page_teaser_image | absolute_url | escape -%}
39
-
40
- {%- assign site_og_image = site.og_image | absolute_url -%}
41
- {%- assign site_og_image = site_og_image | escape -%}
42
-
43
- {%- if page.date -%}
44
- {%- assign og_type = "article" -%}
45
- {%- else -%}
46
- {%- assign og_type = "website" -%}
47
- {%- endif -%}
48
-
49
- <title>{{ seo_title | default: site.title }} {% if paginator %}{% unless paginator.page == 1 %} {{ site.data.ui-text[site.locale].page | default: "Page" }} {{ paginator.page }}{% endunless %}{% endif %}</title>
50
- <meta name="description" content="{{ seo_description }}">
51
- <meta name="author" content="{{ author.name | default: author }}">
52
-
53
- {% if og_type == "article" %}
54
- <meta property="article:author" content="{{ author.name | default: author }}">
55
- {% endif %}
56
-
57
- <meta property="og:type" content="{{ og_type }}">
58
- <meta property="og:locale" content="{{ site.locale | replace: "-", "_" | default: "en_US" }}">
59
- <meta property="og:site_name" content="{{ site.title }}">
60
- <meta property="og:title" content="{{ page.title | default: site.title | markdownify | strip_html | strip_newlines | escape_once }}">
61
- <meta property="og:url" content="{{ canonical_url }}">
62
-
63
- <meta property="og:description" content="{{ seo_description }}">
64
-
65
-
66
- {% if page_large_image %}
67
- <meta property="og:image" content="{{ page_large_image }}">
68
- {% elsif page_teaser_image %}
69
- <meta property="og:image" content="{{ page_teaser_image }}">
70
- {% endif %}
71
-
72
- {% if site.twitter.username %}
73
- <meta name="twitter:site" content="@{{ site.twitter.username | replace: "@", "" }}">
74
- <meta name="twitter:title" content="{{ page.title | default: site.title | markdownify | strip_html | strip_newlines | escape_once }}">
75
- <meta name="twitter:description" content="{{ seo_description }}">
76
- <meta name="twitter:url" content="{{ canonical_url }}">
77
-
78
- {% if page_large_image %}
79
- <meta name="twitter:card" content="summary_large_image">
80
- <meta name="twitter:image" content="{{ page_large_image }}">
81
- {% else %}
82
- <meta name="twitter:card" content="summary">
83
- {% if page_teaser_image %}
84
- <meta name="twitter:image" content="{{ page_teaser_image }}">
85
- {% endif %}
86
- {% endif %}
87
-
88
- {% if author_twitter %}
89
- <meta name="twitter:creator" content="@{{ author_twitter }}">
90
- {% endif %}
91
- {% endif %}
92
-
93
- <meta property="article:published_time" content="{{ page.date | default: page.lastmod | date_to_xmlschema }}">
94
-
95
- {% if og_type == "article" and page.last_modified_at %}
96
- <meta property="article:modified_time" content="{{ page.last_modified_at | date_to_xmlschema }}">
97
- {% endif %}
98
-
99
- {% if site.facebook %}
100
- {% if site.facebook.publisher %}
101
- <meta property="article:publisher" content="{{ site.facebook.publisher }}">
102
- {% endif %}
103
-
104
- {% if site.facebook.app_id %}
105
- <meta property="fb:app_id" content="{{ site.facebook.app_id }}">
106
- {% endif %}
107
- {% endif %}
108
-
109
- <link rel="canonical" href="{{ canonical_url }}">
110
-
111
- {% if paginator.previous_page %}
112
- <link rel="prev" href="{{ paginator.previous_page_path | absolute_url }}">
113
- {% endif %}
114
- {% if paginator.next_page %}
115
- <link rel="next" href="{{ paginator.next_page_path | absolute_url }}">
116
- {% endif %}
117
-
118
- <script type="application/ld+json">
119
- {
120
- "@context": "https://schema.org",
121
- {% if site.social.type == "Organization" %}
122
- "@type": "Organization",
123
- "url": {{ '/' | absolute_url | jsonify }}{% if site.og_image %},
124
- "logo": {{ site_og_image | jsonify }}{% endif %}
125
- {% else %}
126
- "@type": "Person",
127
- "name": {{ site.social.name | default: site.name | jsonify }},
128
- "url": {{ '/' | absolute_url |jsonify }}{% if site.social.links %},
129
- "sameAs": {{ site.social.links | jsonify }}{% endif %}
130
- {% endif %}
131
- }
132
- </script>
133
-
134
- <!-- Site verification -->
135
-
136
- <meta name="google-site-verification" content="{{ site.google_site_verification }}" />
26
+ {%- comment -%}
27
+ Custom og:image handling: supports page.preview and page.header.og_image
28
+ with automatic assets_prefix path normalisation from _config.yml.
29
+ jekyll-seo-tag handles page.image / page.image.path, so we only emit
30
+ our own og:image when those standard keys are absent.
31
+ {%- endcomment -%}
32
+
33
+ {%- unless page.image -%}
34
+ {%- assign page_large_image = page.header.og_image | default: page.header.overlay_image | default: page.header.image -%}
35
+ {%- assign page_teaser_image = page.preview | default: site.og_image -%}
36
+ {%- assign assets_prefix = site.preview_images.assets_prefix | default: '/assets' -%}
37
+ {%- assign auto_prefix = site.preview_images.auto_prefix | default: true -%}
38
+
39
+ {%- if page_teaser_image contains '://' -%}
40
+ {%- comment -%} External URL - use as-is {%- endcomment -%}
41
+ {%- elsif auto_prefix and page_teaser_image != blank -%}
42
+ {%- unless page_teaser_image contains assets_prefix -%}
43
+ {%- assign page_teaser_image = assets_prefix | append: page_teaser_image -%}
44
+ {%- endunless -%}
45
+ {%- endif -%}
46
+
47
+ {%- if page_large_image != blank -%}
48
+ {%- assign seo_image = page_large_image | absolute_url | escape -%}
49
+ {%- elsif page_teaser_image != blank -%}
50
+ {%- assign seo_image = page_teaser_image | absolute_url | escape -%}
51
+ {%- endif -%}
52
+
53
+ {%- if seo_image -%}
54
+ <meta property="og:image" content="{{ seo_image }}">
55
+ <meta name="twitter:image" content="{{ seo_image }}">
56
+ {%- endif -%}
57
+ {%- endunless -%}
58
+
59
+ <!-- Site verification (non-Google; Google is handled by jekyll-seo-tag) -->
60
+ {% if site.bing_site_verification %}
137
61
  <meta name="msvalidate.01" content="{{ site.bing_site_verification }}">
62
+ {% endif %}
63
+ {% if site.alexa_site_verification %}
138
64
  <meta name="alexaVerifyID" content="{{ site.alexa_site_verification }}">
65
+ {% endif %}
66
+ {% if site.yandex_site_verification %}
139
67
  <meta name="yandex-verification" content="{{ site.yandex_site_verification }}">
68
+ {% endif %}
69
+ {% if site.naver_site_verification %}
140
70
  <meta name="naver-site-verification" content="{{ site.naver_site_verification }}">
71
+ {% endif %}
72
+ {% if site.baidu_site_verification %}
141
73
  <meta name="baidu-site-verification" content="{{ site.baidu_site_verification }}">
142
- <!-- end _includes/seo.html -->
74
+ {% endif %}
75
+ <!-- end _includes/content/seo.html -->
@@ -56,7 +56,7 @@
56
56
  <!-- Theme Info Button -->
57
57
  <li class="btn align-items-end">
58
58
  <a href="#"
59
- data-bs-toggle="modal"
59
+ data-bs-toggle="offcanvas"
60
60
  data-bs-target="#info-section"
61
61
  aria-label="View theme information and system details"
62
62
  title="Theme Info">
@@ -88,9 +88,10 @@
88
88
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
89
89
 
90
90
  <!-- ================================ -->
91
- <!-- SEO AND SOCIAL MEDIA META TAGS -->
91
+ <!-- SEO SUPPLEMENT -->
92
92
  <!-- ================================ -->
93
- <!-- Comprehensive SEO optimization including Open Graph and Twitter Cards -->
93
+ <!-- Additional SEO tags not covered by jekyll-seo-tag plugin ( in root.html).
94
+ Outputs custom og:image with assets_prefix normalisation and non-Google site verification. -->
94
95
  {% include content/seo.html %}
95
96
 
96
97
  <!-- ========================== -->
@@ -105,11 +105,18 @@
105
105
  <div class="navbar-brand-group d-inline-flex align-items-center gap-2">
106
106
  <!-- Home Navigation Button (Desktop only) -->
107
107
  <div class="navbar-home-links d-none d-lg-inline-flex" role="navigation" aria-label="Quick navigation">
108
- {%- for home in site.data.navigation.home -%}
109
- <a class="btn" href="{{ home.url | relative_url }}" aria-label="{{ home.title }}" title="{{ home.title }}" {% if home.url == page.url %}aria-current="page"{% endif %}>
110
- <i class="{{ site.default_icon}} {{ home.icon }}"></i>
111
- </a>
112
- {% endfor %}
108
+ {%- assign nav_home = site.data.navigation.home -%}
109
+ {%- if nav_home and nav_home.size > 0 -%}
110
+ {%- for home in nav_home -%}
111
+ <a class="btn" href="{{ home.url | relative_url }}" aria-label="{{ home.title }}" title="{{ home.title }}" {%- if home.url == page.url %} aria-current="page"{% endif %}>
112
+ <i class="{{ site.default_icon}} {{ home.icon }}"></i>
113
+ </a>
114
+ {%- endfor -%}
115
+ {%- else -%}
116
+ <a class="btn" href="{{ '/' | relative_url }}" aria-label="Home" title="Home — {{ site.title }}">
117
+ <i class="{{ site.default_icon }} bi-house"></i>
118
+ </a>
119
+ {%- endif -%}
113
120
  </div>
114
121
 
115
122
  <!-- Brand Logo Link to Root -->
@@ -151,11 +158,11 @@
151
158
  <span class="nav-link-text d-none d-xl-inline ms-1">Search</span>
152
159
  </button>
153
160
 
154
- <!-- Settings Modal Toggle -->
161
+ <!-- Settings Offcanvas Toggle -->
155
162
  <button
156
163
  class="btn nav-settings-button d-none d-lg-inline-flex"
157
164
  type="button"
158
- data-bs-toggle="modal"
165
+ data-bs-toggle="offcanvas"
159
166
  data-bs-target="#info-section"
160
167
  aria-label="Open settings"
161
168
  aria-haspopup="dialog"
@@ -30,15 +30,26 @@ docker run -p 4000:4000 {{ site.resources.docker.image | default: '' | join: ''
30
30
  <div class="col-12 col-sm-10 col-md-6 col-lg-4">
31
31
  <div class="card h-100 border-0 shadow-sm">
32
32
  <div class="card-header bg-secondary text-white">
33
- <h5 class="card-title mb-0"><i class="bi bi-git me-2"></i>Fork Repository</h5>
33
+ <h5 class="card-title mb-0"><i class="bi bi-git me-2"></i>Fork & Deploy</h5>
34
34
  </div>
35
35
  <div class="card-body p-4 d-flex flex-column text-center">
36
- <p class="card-text mb-3">Improve the theme by forking the repo and opening a PR.</p>
37
- <pre class="bg-dark text-light p-3 rounded mb-3 small text-start overflow-auto"><code>git clone {{ site.resources.github_repo | default: '' | join: '' | default: site.github.repository_url }}
38
- cd {{ site.local_repo | default: site.repository_name | default: 'repo' }}
39
- git remote add upstream {{ site.resources.github_repo | default: '' | join: '' | default: site.github.repository_url }}
40
- git checkout -b feature/your-change</code></pre>
41
- <a class="btn btn-outline-secondary btn-sm mt-auto" href="{{ site.resources.github_fork | default: '' | join: '' | default: site.github.repository_url | append: '/fork' }}" target="_blank" rel="noopener">Fork on GitHub</a>
36
+ {% assign github_fork_base = site.resources.github_fork | default: site.github.repository_url | join: '' %}
37
+ {% assign github_fork_suffix = github_fork_base | slice: -5, 5 %}
38
+ {% if github_fork_suffix == '/fork' %}
39
+ {% assign github_fork_url = github_fork_base %}
40
+ {% else %}
41
+ {% assign github_fork_url = github_fork_base | append: '/fork' %}
42
+ {% endif %}
43
+ <p class="card-text mb-3">Fork into <code>&lt;username&gt;.github.io</code> for a personal site in minutes.</p>
44
+ <ol class="text-start small mb-3">
45
+ <li>Fork <a href="{{ github_fork_url }}" target="_blank" rel="noopener">bamr87/zer0-mistakes</a></li>
46
+ <li>Name it <code>&lt;your-username&gt;.github.io</code></li>
47
+ <li>Enable GitHub Pages in Settings</li>
48
+ </ol>
49
+ <pre class="bg-dark text-light p-3 rounded mb-3 small text-start overflow-auto"><code>git clone https://github.com/&lt;you&gt;/&lt;you&gt;.github.io.git
50
+ cd &lt;you&gt;.github.io
51
+ ./scripts/fork-cleanup.sh</code></pre>
52
+ <a class="btn btn-outline-secondary btn-sm mt-auto" href="{{ github_fork_url }}" target="_blank" rel="noopener">Fork on GitHub</a>
42
53
  </div>
43
54
  </div>
44
55
  </div>
@@ -0,0 +1,95 @@
1
+ <!--
2
+ ===================================================================
3
+ ADMIN NAV — Data-driven sidebar navigation for admin/settings pages
4
+ ===================================================================
5
+
6
+ File: admin-nav.html
7
+ Path: _includes/navigation/admin-nav.html
8
+ Purpose: Renders a vertical nav list for administrative pages.
9
+ Items are read from _data/navigation/admin.yml.
10
+ Highlights the active page based on page.url or
11
+ page.admin_section matching.
12
+
13
+ Dependencies: Bootstrap 5 nav component, Bootstrap Icons,
14
+ _data/navigation/admin.yml
15
+ Used by: _layouts/admin.html
16
+ ===================================================================
17
+ -->
18
+
19
+ {% assign current_path = page.url %}
20
+ {% assign admin_items = site.data.navigation.admin %}
21
+
22
+ <ul class="nav nav-pills flex-column">
23
+
24
+ {% assign rendered_separator = false %}
25
+ {% for item in admin_items %}
26
+
27
+ {% if item.external and rendered_separator == false %}
28
+ {% assign rendered_separator = true %}
29
+ <li role="separator"><hr class="my-2"></li>
30
+ {% endif %}
31
+
32
+ {% comment %} Active detection: exact URL match or admin_section match {% endcomment %}
33
+ {% assign is_active = false %}
34
+ {% if current_path == item.url %}
35
+ {% assign is_active = true %}
36
+ {% elsif page.admin_section and page.admin_section == item.title %}
37
+ {% assign is_active = true %}
38
+ {% endif %}
39
+
40
+ {% comment %} Dynamic badges when no static badge is set {% endcomment %}
41
+ {% assign dynamic_badge = nil %}
42
+ {% assign badge_class = "bg-secondary" %}
43
+ {% unless item.badge or item.external %}
44
+ {% if item.url == "/about/settings/collections/" %}
45
+ {% assign dynamic_badge = site.collections | size %}
46
+ {% elsif item.url == "/about/settings/analytics/" %}
47
+ {% if site.posthog.enabled %}
48
+ {% assign dynamic_badge = "On" %}
49
+ {% assign badge_class = "bg-success" %}
50
+ {% else %}
51
+ {% assign dynamic_badge = "Off" %}
52
+ {% assign badge_class = "bg-secondary" %}
53
+ {% endif %}
54
+ {% elsif item.url == "/about/settings/environment/" %}
55
+ {% if jekyll.environment == "production" %}
56
+ {% assign dynamic_badge = "Prod" %}
57
+ {% assign badge_class = "bg-success" %}
58
+ {% else %}
59
+ {% assign dynamic_badge = "Dev" %}
60
+ {% assign badge_class = "bg-warning text-dark" %}
61
+ {% endif %}
62
+ {% endif %}
63
+ {% endunless %}
64
+
65
+ <li class="nav-item">
66
+ {% if item.external %}
67
+ {% comment %} Resolve placeholder tokens for external URLs {% endcomment %}
68
+ {% assign resolved_url = item.url
69
+ | replace: '{github_user}', site.github_user
70
+ | replace: '{repository_name}', site.repository_name %}
71
+ <a class="nav-link d-flex align-items-center text-body-secondary"
72
+ href="{{ resolved_url }}"
73
+ target="_blank" rel="noopener">
74
+ <i class="{{ item.icon }} me-2"></i>
75
+ {{ item.title }}
76
+ <i class="bi bi-box-arrow-up-right ms-auto small"></i>
77
+ </a>
78
+ {% else %}
79
+ <a class="nav-link d-flex align-items-center{% if is_active %} active{% endif %}"
80
+ href="{{ item.url | relative_url }}"
81
+ {% if item.description %}title="{{ item.description }}"{% endif %}>
82
+ <i class="{{ item.icon }} me-2"></i>
83
+ {{ item.title }}
84
+ {% if item.badge %}
85
+ <span class="badge bg-secondary ms-auto">{{ item.badge }}</span>
86
+ {% elsif dynamic_badge %}
87
+ <span class="badge {{ badge_class }} ms-auto">{{ dynamic_badge }}</span>
88
+ {% endif %}
89
+ </a>
90
+ {% endif %}
91
+ </li>
92
+
93
+ {% endfor %}
94
+
95
+ </ul>
@@ -6,8 +6,8 @@
6
6
  with hover-activated dropdowns for desktop and accessible interactions
7
7
  -->
8
8
 
9
- <!-- Navigation Links - Offcanvas -->
10
- <div class="offcanvas offcanvas-end col-lg-2" tabindex="-1" id="bdNavbar" aria-labelledby="mainNavOffcanvasLabel" aria-modal="true" role="dialog">
9
+ <!-- Navigation Links - Responsive Offcanvas (inline at lg+, offcanvas below) -->
10
+ <div class="offcanvas-lg offcanvas-end" tabindex="-1" id="bdNavbar" aria-labelledby="mainNavOffcanvasLabel">
11
11
 
12
12
  <!-- Main Navigation Header - Offcanvas -->
13
13
  <div class="offcanvas-header border-bottom">
@@ -26,7 +26,17 @@
26
26
  Home
27
27
  </a>
28
28
  </li>
29
- {%- for link in site.data.navigation.main -%}
29
+ {%- comment -%}
30
+ Navigation: data-driven when _data/navigation/main.yml exists;
31
+ otherwise auto-generated from site.collections as a fallback so
32
+ a consumer repo needs only markdown files and _config.yml.
33
+ Exclude a collection from auto-nav by setting nav_exclude: true
34
+ under its entry in _config.yml collections:.
35
+ {%- endcomment -%}
36
+ {%- assign nav_main = site.data.navigation.main -%}
37
+ {%- if nav_main and nav_main.size > 0 -%}
38
+
39
+ {%- for link in nav_main -%}
30
40
  {%- assign has_children = link.children and link.children.size > 0 -%}
31
41
 
32
42
  {%- if has_children -%}
@@ -93,7 +103,35 @@
93
103
  </a>
94
104
  </li>
95
105
  {%- endif -%}
96
- {%- endfor -%}
106
+ {%- endfor -%}
107
+
108
+ {%- else -%}
109
+ {%- comment -%}
110
+ Dynamic navigation: one nav item per collection that has output
111
+ documents. The "pages" collection is skipped (structural only).
112
+ Set nav_exclude: true on a collection in _config.yml to hide it.
113
+ {%- endcomment -%}
114
+ {%- for collection in site.collections -%}
115
+ {%- unless collection.label == "pages" or collection.nav_exclude -%}
116
+ {%- if collection.docs.size > 0 -%}
117
+ {%- assign col_title = collection.label | replace: "-", " " | replace: "_", " " | capitalize -%}
118
+ {%- assign col_url = "/" | append: collection.label | append: "/" -%}
119
+ <li class="nav-item" role="none">
120
+ <a
121
+ class="nav-link"
122
+ href="{{ col_url | relative_url }}"
123
+ role="menuitem"
124
+ title="{{ col_title }}"
125
+ {%- if page.collection == collection.label -%} aria-current="page"{%- endif -%}
126
+ >
127
+ <span class="nav-link-text">{{ col_title }}</span>
128
+ </a>
129
+ </li>
130
+ {%- endif -%}
131
+ {%- endunless -%}
132
+ {%- endfor -%}
133
+
134
+ {%- endif -%}
97
135
  <li class="nav-item d-lg-none" role="none">
98
136
  <button
99
137
  class="nav-link btn btn-link text-start w-100"
@@ -110,7 +148,7 @@
110
148
  <button
111
149
  class="nav-link btn btn-link text-start w-100"
112
150
  type="button"
113
- data-bs-toggle="modal"
151
+ data-bs-toggle="offcanvas"
114
152
  data-bs-target="#info-section"
115
153
  aria-controls="info-section"
116
154
  aria-haspopup="dialog"
@@ -1,4 +1,4 @@
1
- <!--
1
+ <!--
2
2
  ===================================================================
3
3
  SIDEBAR LEFT - Dynamic Navigation Sidebar
4
4
  ===================================================================