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,232 @@
1
+ <!--
2
+ ===================================================================
3
+ ANALYTICS DASHBOARD — PostHog & tracking configuration overview
4
+ ===================================================================
5
+
6
+ File: analytics-dashboard.html
7
+ Path: _includes/components/analytics-dashboard.html
8
+ Purpose: Shows current analytics configuration, privacy settings,
9
+ and tracking status. Reads from site.posthog config.
10
+
11
+ Dependencies: Bootstrap 5 card/table/badge, site.posthog config
12
+ Used by: pages/_about/settings/analytics.md
13
+ ===================================================================
14
+ -->
15
+
16
+ {% assign ph = site.posthog %}
17
+
18
+ <!-- Status cards -->
19
+ <div class="row g-3 mb-4">
20
+ <div class="col-6 col-lg-3">
21
+ <div class="card text-center h-100 {% if ph.enabled %}border-success{% else %}border-warning{% endif %}">
22
+ <div class="card-body py-3">
23
+ <i class="bi bi-graph-up fs-3 {% if ph.enabled %}text-success{% else %}text-warning{% endif %}"></i>
24
+ <div class="fw-semibold mt-1">{{ ph.enabled | default: false }}</div>
25
+ <small class="text-body-secondary">PostHog Enabled</small>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <div class="col-6 col-lg-3">
30
+ <div class="card text-center h-100 border-primary">
31
+ <div class="card-body py-3">
32
+ <i class="bi bi-shield-check fs-3 text-primary"></i>
33
+ <div class="fw-semibold mt-1">{{ ph.respect_dnt | default: false }}</div>
34
+ <small class="text-body-secondary">Respect DNT</small>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div class="col-6 col-lg-3">
39
+ <div class="card text-center h-100 border-primary">
40
+ <div class="card-body py-3">
41
+ <i class="bi bi-camera-video fs-3 text-primary"></i>
42
+ <div class="fw-semibold mt-1">{{ ph.session_recording | default: false }}</div>
43
+ <small class="text-body-secondary">Session Recording</small>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <div class="col-6 col-lg-3">
48
+ <div class="card text-center h-100 border-primary">
49
+ <div class="card-body py-3">
50
+ <i class="bi bi-cookie fs-3 text-primary"></i>
51
+ <div class="fw-semibold mt-1">{{ ph.secure_cookie | default: false }}</div>
52
+ <small class="text-body-secondary">Secure Cookies</small>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- Configuration Details -->
59
+ <div class="row g-4">
60
+
61
+ <!-- PostHog Settings -->
62
+ <div class="col-lg-6">
63
+ <div class="card h-100">
64
+ <div class="card-header"><i class="bi bi-gear me-1"></i> PostHog Configuration</div>
65
+ <div class="card-body p-0">
66
+ <table class="table table-sm mb-0">
67
+ <tbody>
68
+ <tr>
69
+ <td class="fw-semibold">Enabled</td>
70
+ <td>
71
+ {% if ph.enabled %}
72
+ <span class="badge bg-success">Yes</span>
73
+ {% else %}
74
+ <span class="badge bg-secondary">No</span>
75
+ {% endif %}
76
+ </td>
77
+ </tr>
78
+ <tr>
79
+ <td class="fw-semibold">API Host</td>
80
+ <td><code class="small">{{ ph.api_host | default: "not set" }}</code></td>
81
+ </tr>
82
+ <tr>
83
+ <td class="fw-semibold">Person Profiles</td>
84
+ <td><code class="small">{{ ph.person_profiles | default: "not set" }}</code></td>
85
+ </tr>
86
+ <tr>
87
+ <td class="fw-semibold">Autocapture</td>
88
+ <td>{{ ph.autocapture | default: false }}</td>
89
+ </tr>
90
+ <tr>
91
+ <td class="fw-semibold">Page Views</td>
92
+ <td>{{ ph.capture_pageview | default: false }}</td>
93
+ </tr>
94
+ <tr>
95
+ <td class="fw-semibold">Page Leave</td>
96
+ <td>{{ ph.capture_pageleave | default: false }}</td>
97
+ </tr>
98
+ <tr>
99
+ <td class="fw-semibold">Persistence</td>
100
+ <td><code class="small">{{ ph.persistence | default: "not set" }}</code></td>
101
+ </tr>
102
+ </tbody>
103
+ </table>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <!-- Privacy & Compliance -->
109
+ <div class="col-lg-6">
110
+ <div class="card h-100">
111
+ <div class="card-header"><i class="bi bi-shield-lock me-1"></i> Privacy & Compliance</div>
112
+ <div class="card-body p-0">
113
+ <table class="table table-sm mb-0">
114
+ <tbody>
115
+ <tr>
116
+ <td class="fw-semibold">Respect Do Not Track</td>
117
+ <td>
118
+ {% if ph.respect_dnt %}
119
+ <span class="badge bg-success">Yes</span>
120
+ {% else %}
121
+ <span class="badge bg-warning">No</span>
122
+ {% endif %}
123
+ </td>
124
+ </tr>
125
+ <tr>
126
+ <td class="fw-semibold">Session Recording</td>
127
+ <td>
128
+ {% if ph.session_recording %}
129
+ <span class="badge bg-warning">Enabled</span>
130
+ {% else %}
131
+ <span class="badge bg-success">Disabled</span>
132
+ {% endif %}
133
+ </td>
134
+ </tr>
135
+ <tr>
136
+ <td class="fw-semibold">Disable Cookies</td>
137
+ <td>{{ ph.disable_cookie | default: false }}</td>
138
+ </tr>
139
+ <tr>
140
+ <td class="fw-semibold">Secure Cookies</td>
141
+ <td>
142
+ {% if ph.secure_cookie %}
143
+ <span class="badge bg-success">Yes</span>
144
+ {% else %}
145
+ <span class="badge bg-secondary">No</span>
146
+ {% endif %}
147
+ </td>
148
+ </tr>
149
+ <tr>
150
+ <td class="fw-semibold">Cross-Subdomain</td>
151
+ <td>{{ ph.cross_subdomain_cookie | default: false }}</td>
152
+ </tr>
153
+ {% if ph.privacy %}
154
+ <tr><td class="fw-semibold">Mask All Text</td><td>{{ ph.privacy.mask_all_text | default: false }}</td></tr>
155
+ <tr><td class="fw-semibold">Mask All Inputs</td><td>{{ ph.privacy.mask_all_inputs | default: false }}</td></tr>
156
+ <tr><td class="fw-semibold">IP Anonymization</td><td>{{ ph.privacy.ip_anonymization | default: false }}</td></tr>
157
+ {% endif %}
158
+ </tbody>
159
+ </table>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+
165
+ <!-- Custom Events -->
166
+ {% if ph.custom_events %}
167
+ <h5 class="mt-4 mb-3"><i class="bi bi-lightning me-1"></i> Custom Event Tracking</h5>
168
+ <div class="row g-3">
169
+ {% if ph.custom_events.track_downloads != nil %}
170
+ <div class="col-6 col-md-3">
171
+ <div class="card text-center">
172
+ <div class="card-body py-2">
173
+ <i class="bi bi-download fs-5 {% if ph.custom_events.track_downloads %}text-success{% else %}text-body-secondary{% endif %}"></i>
174
+ <div class="small fw-semibold mt-1">Downloads</div>
175
+ <span class="badge {% if ph.custom_events.track_downloads %}bg-success{% else %}bg-secondary{% endif %}">
176
+ {{ ph.custom_events.track_downloads }}
177
+ </span>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ {% endif %}
182
+ {% if ph.custom_events.track_external_links != nil %}
183
+ <div class="col-6 col-md-3">
184
+ <div class="card text-center">
185
+ <div class="card-body py-2">
186
+ <i class="bi bi-box-arrow-up-right fs-5 {% if ph.custom_events.track_external_links %}text-success{% else %}text-body-secondary{% endif %}"></i>
187
+ <div class="small fw-semibold mt-1">External Links</div>
188
+ <span class="badge {% if ph.custom_events.track_external_links %}bg-success{% else %}bg-secondary{% endif %}">
189
+ {{ ph.custom_events.track_external_links }}
190
+ </span>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ {% endif %}
195
+ {% if ph.custom_events.track_search != nil %}
196
+ <div class="col-6 col-md-3">
197
+ <div class="card text-center">
198
+ <div class="card-body py-2">
199
+ <i class="bi bi-search fs-5 {% if ph.custom_events.track_search %}text-success{% else %}text-body-secondary{% endif %}"></i>
200
+ <div class="small fw-semibold mt-1">Search</div>
201
+ <span class="badge {% if ph.custom_events.track_search %}bg-success{% else %}bg-secondary{% endif %}">
202
+ {{ ph.custom_events.track_search }}
203
+ </span>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ {% endif %}
208
+ {% if ph.custom_events.track_scroll_depth != nil %}
209
+ <div class="col-6 col-md-3">
210
+ <div class="card text-center">
211
+ <div class="card-body py-2">
212
+ <i class="bi bi-arrows-vertical fs-5 {% if ph.custom_events.track_scroll_depth %}text-success{% else %}text-body-secondary{% endif %}"></i>
213
+ <div class="small fw-semibold mt-1">Scroll Depth</div>
214
+ <span class="badge {% if ph.custom_events.track_scroll_depth %}bg-success{% else %}bg-secondary{% endif %}">
215
+ {{ ph.custom_events.track_scroll_depth }}
216
+ </span>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ {% endif %}
221
+ </div>
222
+ {% endif %}
223
+
224
+ <!-- External Links -->
225
+ <div class="mt-4">
226
+ <a href="https://us.posthog.com" target="_blank" rel="noopener" class="btn btn-outline-primary btn-sm me-2">
227
+ <i class="bi bi-box-arrow-up-right me-1"></i> Open PostHog Dashboard
228
+ </a>
229
+ <a href="{{ '/about/config/' | relative_url }}" class="btn btn-outline-secondary btn-sm">
230
+ <i class="bi bi-gear me-1"></i> Full Configuration
231
+ </a>
232
+ </div>
@@ -0,0 +1,159 @@
1
+ <!--
2
+ ===================================================================
3
+ BACKGROUND CUSTOMIZER — fffuel-style background settings panel
4
+ ===================================================================
5
+
6
+ File: background-customizer.html
7
+ Path: _includes/components/background-customizer.html
8
+ Purpose: Bootstrap 5 offcanvas panel for switching skins and
9
+ tuning SVG background layers at runtime.
10
+
11
+ Dependencies:
12
+ - assets/js/background-customizer.js (zer0Bg API)
13
+ - _sass/theme/_backgrounds.scss (CSS custom properties)
14
+ - Bootstrap 5 offcanvas + form components
15
+ ===================================================================
16
+ -->
17
+
18
+ {% assign skins = "air,aqua,contrast,dark,dirt,neon,mint,plum,sunrise" | split: "," %}
19
+ {% assign current_skin = site.theme_skin | default: "dark" %}
20
+
21
+ <!-- Floating toggle button -->
22
+ <button class="btn btn-sm btn-outline-secondary position-fixed bottom-0 end-0 m-3 rounded-circle"
23
+ style="z-index: 1090; width: 40px; height: 40px;"
24
+ type="button"
25
+ data-bs-toggle="offcanvas"
26
+ data-bs-target="#zer0BgCustomizer"
27
+ aria-label="Customize background"
28
+ title="Background settings">
29
+ <i class="bi bi-palette2"></i>
30
+ </button>
31
+
32
+ <!-- Offcanvas panel -->
33
+ <div class="offcanvas offcanvas-end" tabindex="-1" id="zer0BgCustomizer" aria-labelledby="zer0BgCustomizerLabel">
34
+ <div class="offcanvas-header">
35
+ <h5 class="offcanvas-title" id="zer0BgCustomizerLabel">
36
+ <i class="bi bi-palette2 me-2"></i>Background Settings
37
+ </h5>
38
+ <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
39
+ </div>
40
+
41
+ <div class="offcanvas-body">
42
+ <!-- Skin selector -->
43
+ <div class="mb-4">
44
+ <label class="form-label fw-semibold">Theme Skin</label>
45
+ <div class="d-flex flex-wrap gap-2" id="zer0SkinButtons">
46
+ {% for skin in skins %}
47
+ <button class="btn btn-sm btn-outline-primary{% if skin == current_skin %} active{% endif %}"
48
+ data-skin="{{ skin }}"
49
+ type="button">
50
+ {{ skin | capitalize }}
51
+ </button>
52
+ {% endfor %}
53
+ </div>
54
+ </div>
55
+
56
+ <hr>
57
+
58
+ <!-- Background toggle -->
59
+ <div class="mb-3">
60
+ <div class="form-check form-switch">
61
+ <input class="form-check-input" type="checkbox" id="zer0BgToggle" checked>
62
+ <label class="form-check-label" for="zer0BgToggle">Enable SVG backgrounds</label>
63
+ </div>
64
+ </div>
65
+
66
+ <hr>
67
+
68
+ <!-- Opacity sliders -->
69
+ <div class="mb-3">
70
+ <label class="form-label" for="zer0GradientOpacity">Gradient opacity</label>
71
+ <input type="range" class="form-range" id="zer0GradientOpacity"
72
+ min="0" max="1" step="0.05" value="0.6">
73
+ <small class="text-body-secondary"><span id="zer0GradientOpacityVal">0.6</span></small>
74
+ </div>
75
+
76
+ <div class="mb-3">
77
+ <label class="form-label" for="zer0TextureOpacity">Texture opacity</label>
78
+ <input type="range" class="form-range" id="zer0TextureOpacity"
79
+ min="0" max="0.2" step="0.005" value="0.04">
80
+ <small class="text-body-secondary"><span id="zer0TextureOpacityVal">0.04</span></small>
81
+ </div>
82
+
83
+ <div class="mb-3">
84
+ <label class="form-label" for="zer0PatternOpacity">Pattern opacity</label>
85
+ <input type="range" class="form-range" id="zer0PatternOpacity"
86
+ min="0" max="0.3" step="0.01" value="0.08">
87
+ <small class="text-body-secondary"><span id="zer0PatternOpacityVal">0.08</span></small>
88
+ </div>
89
+
90
+ <hr>
91
+
92
+ <button class="btn btn-sm btn-outline-danger" id="zer0BgReset" type="button">
93
+ <i class="bi bi-arrow-counterclockwise me-1"></i>Reset to defaults
94
+ </button>
95
+ </div>
96
+ </div>
97
+
98
+ <!-- Customizer interaction logic -->
99
+ <script>
100
+ document.addEventListener("DOMContentLoaded", function () {
101
+ if (typeof zer0Bg === "undefined") return;
102
+
103
+ // Skin buttons
104
+ document.querySelectorAll("#zer0SkinButtons [data-skin]").forEach(function (btn) {
105
+ btn.addEventListener("click", function () {
106
+ var skin = this.getAttribute("data-skin");
107
+ zer0Bg.setSkin(skin);
108
+ // Update active state
109
+ document.querySelectorAll("#zer0SkinButtons .btn").forEach(function (b) { b.classList.remove("active"); });
110
+ this.classList.add("active");
111
+ });
112
+ });
113
+
114
+ // Background toggle
115
+ var toggle = document.getElementById("zer0BgToggle");
116
+ toggle.checked = document.documentElement.getAttribute("data-zer0-bg") !== "off";
117
+ toggle.addEventListener("change", function () { zer0Bg.toggle(this.checked); });
118
+
119
+ // Opacity sliders
120
+ function bindSlider(id, layer) {
121
+ var slider = document.getElementById(id);
122
+ var valSpan = document.getElementById(id + "Val");
123
+ slider.addEventListener("input", function () {
124
+ zer0Bg.setOpacity(layer, this.value);
125
+ valSpan.textContent = this.value;
126
+ });
127
+ }
128
+ bindSlider("zer0GradientOpacity", "gradient");
129
+ bindSlider("zer0TextureOpacity", "texture");
130
+ bindSlider("zer0PatternOpacity", "pattern");
131
+
132
+ // Reset
133
+ document.getElementById("zer0BgReset").addEventListener("click", function () {
134
+ zer0Bg.setSkin("{{ current_skin }}");
135
+ zer0Bg.toggle(true);
136
+ zer0Bg.setOpacity("gradient", 0.6);
137
+ zer0Bg.setOpacity("texture", 0.04);
138
+ zer0Bg.setOpacity("pattern", 0.08);
139
+ document.getElementById("zer0GradientOpacity").value = 0.6;
140
+ document.getElementById("zer0TextureOpacity").value = 0.04;
141
+ document.getElementById("zer0PatternOpacity").value = 0.08;
142
+ document.getElementById("zer0GradientOpacityVal").textContent = "0.6";
143
+ document.getElementById("zer0TextureOpacityVal").textContent = "0.04";
144
+ document.getElementById("zer0PatternOpacityVal").textContent = "0.08";
145
+ toggle.checked = true;
146
+ // Re-highlight correct skin button
147
+ document.querySelectorAll("#zer0SkinButtons .btn").forEach(function (b) { b.classList.remove("active"); });
148
+ var active = document.querySelector('#zer0SkinButtons [data-skin="{{ current_skin }}"]');
149
+ if (active) active.classList.add("active");
150
+ });
151
+
152
+ // Sync on external skin change events
153
+ document.addEventListener("zer0:skin-change", function (e) {
154
+ document.querySelectorAll("#zer0SkinButtons .btn").forEach(function (b) { b.classList.remove("active"); });
155
+ var active = document.querySelector('#zer0SkinButtons [data-skin="' + e.detail.skin + '"]');
156
+ if (active) active.classList.add("active");
157
+ });
158
+ });
159
+ </script>
@@ -0,0 +1,137 @@
1
+ <!--
2
+ ===================================================================
3
+ BACKGROUND SETTINGS — fffuel-style background controls (tab content)
4
+ ===================================================================
5
+
6
+ File: background-settings.html
7
+ Path: _includes/components/background-settings.html
8
+ Purpose: Background skin selector and opacity controls, rendered
9
+ inside the Settings offcanvas Background tab.
10
+
11
+ Dependencies:
12
+ - assets/js/background-customizer.js (zer0Bg API)
13
+ - _sass/theme/_backgrounds.scss (CSS custom properties)
14
+ - Bootstrap 5 form components
15
+ ===================================================================
16
+ -->
17
+
18
+ {% assign skins = "air,aqua,contrast,dark,dirt,neon,mint,plum,sunrise" | split: "," %}
19
+ {% assign current_skin = site.theme_skin | default: "dark" %}
20
+
21
+ <!-- Skin selector -->
22
+ <div class="mb-4">
23
+ <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
24
+ <i class="bi bi-palette2 me-1"></i>Theme Skin
25
+ </h6>
26
+ <div class="d-flex flex-wrap gap-2" id="zer0SkinButtons">
27
+ {% for skin in skins %}
28
+ <button class="btn btn-sm btn-outline-primary{% if skin == current_skin %} active{% endif %}"
29
+ data-skin="{{ skin }}"
30
+ type="button">
31
+ {{ skin | capitalize }}
32
+ </button>
33
+ {% endfor %}
34
+ </div>
35
+ </div>
36
+
37
+ <!-- Background toggle -->
38
+ <div class="mb-4">
39
+ <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
40
+ <i class="bi bi-image me-1"></i>SVG Backgrounds
41
+ </h6>
42
+ <div class="d-flex align-items-center justify-content-between p-3 bg-body-tertiary rounded">
43
+ <label class="form-check-label" for="zer0BgToggle">Enable SVG backgrounds</label>
44
+ <div class="form-check form-switch mb-0">
45
+ <input class="form-check-input" type="checkbox" id="zer0BgToggle" checked>
46
+ </div>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Opacity sliders -->
51
+ <div class="mb-4">
52
+ <h6 class="text-body-secondary small text-uppercase fw-semibold mb-2">
53
+ <i class="bi bi-sliders2 me-1"></i>Layer Opacity
54
+ </h6>
55
+
56
+ <div class="mb-3">
57
+ <label class="form-label small" for="zer0GradientOpacity">Gradient opacity</label>
58
+ <input type="range" class="form-range" id="zer0GradientOpacity"
59
+ min="0" max="1" step="0.05" value="0.6">
60
+ <small class="text-body-secondary"><span id="zer0GradientOpacityVal">0.6</span></small>
61
+ </div>
62
+
63
+ <div class="mb-3">
64
+ <label class="form-label small" for="zer0TextureOpacity">Texture opacity</label>
65
+ <input type="range" class="form-range" id="zer0TextureOpacity"
66
+ min="0" max="0.2" step="0.005" value="0.04">
67
+ <small class="text-body-secondary"><span id="zer0TextureOpacityVal">0.04</span></small>
68
+ </div>
69
+
70
+ <div class="mb-3">
71
+ <label class="form-label small" for="zer0PatternOpacity">Pattern opacity</label>
72
+ <input type="range" class="form-range" id="zer0PatternOpacity"
73
+ min="0" max="0.3" step="0.01" value="0.08">
74
+ <small class="text-body-secondary"><span id="zer0PatternOpacityVal">0.08</span></small>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- Reset -->
79
+ <div>
80
+ <button class="btn btn-sm btn-outline-danger" id="zer0BgReset" type="button">
81
+ <i class="bi bi-arrow-counterclockwise me-1"></i>Reset to defaults
82
+ </button>
83
+ </div>
84
+
85
+ <!-- Customizer interaction logic -->
86
+ <script>
87
+ document.addEventListener("DOMContentLoaded", function () {
88
+ if (typeof zer0Bg === "undefined") return;
89
+
90
+ // Skin buttons
91
+ document.querySelectorAll("#zer0SkinButtons [data-skin]").forEach(function (btn) {
92
+ btn.addEventListener("click", function () {
93
+ var skin = this.getAttribute("data-skin");
94
+ zer0Bg.setSkin(skin);
95
+ document.querySelectorAll("#zer0SkinButtons .btn").forEach(function (b) { b.classList.remove("active"); });
96
+ this.classList.add("active");
97
+ });
98
+ });
99
+
100
+ // Background toggle
101
+ var toggle = document.getElementById("zer0BgToggle");
102
+ toggle.checked = document.documentElement.getAttribute("data-zer0-bg") !== "off";
103
+ toggle.addEventListener("change", function () { zer0Bg.toggle(this.checked); });
104
+
105
+ // Opacity sliders
106
+ function bindSlider(id, layer) {
107
+ var slider = document.getElementById(id);
108
+ var valSpan = document.getElementById(id + "Val");
109
+ slider.addEventListener("input", function () {
110
+ zer0Bg.setOpacity(layer, this.value);
111
+ valSpan.textContent = this.value;
112
+ });
113
+ }
114
+ bindSlider("zer0GradientOpacity", "gradient");
115
+ bindSlider("zer0TextureOpacity", "texture");
116
+ bindSlider("zer0PatternOpacity", "pattern");
117
+
118
+ // Reset
119
+ document.getElementById("zer0BgReset").addEventListener("click", function () {
120
+ zer0Bg.setSkin("{{ current_skin }}");
121
+ zer0Bg.toggle(true);
122
+ zer0Bg.setOpacity("gradient", 0.6);
123
+ zer0Bg.setOpacity("texture", 0.04);
124
+ zer0Bg.setOpacity("pattern", 0.08);
125
+ document.getElementById("zer0GradientOpacity").value = 0.6;
126
+ document.getElementById("zer0TextureOpacity").value = 0.04;
127
+ document.getElementById("zer0PatternOpacity").value = 0.08;
128
+ document.getElementById("zer0GradientOpacityVal").textContent = "0.6";
129
+ document.getElementById("zer0TextureOpacityVal").textContent = "0.04";
130
+ document.getElementById("zer0PatternOpacityVal").textContent = "0.08";
131
+ toggle.checked = true;
132
+ document.querySelectorAll("#zer0SkinButtons .btn").forEach(function (b) { b.classList.remove("active"); });
133
+ var active = document.querySelector('#zer0SkinButtons [data-skin="{{ current_skin }}"]');
134
+ if (active) active.classList.add("active");
135
+ });
136
+ });
137
+ </script>