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,73 @@
1
+ ---
2
+ layout: root
3
+ ---
4
+ <!--
5
+ ===================================================================
6
+ SETUP LAYOUT - Onboarding and configuration pages
7
+ ===================================================================
8
+
9
+ File: setup.html
10
+ Path: _layouts/setup.html
11
+ Inherits: root.html
12
+ Purpose: Clean single-column layout for setup, onboarding, and
13
+ configuration guide pages. No sidebar clutter.
14
+
15
+ Features:
16
+ - Centered, readable content column
17
+ - Optional progress indicator via page.setup_step frontmatter
18
+ - Setup banner when site is unconfigured
19
+ - Minimal chrome — lets the user focus on getting configured
20
+
21
+ Usage:
22
+ layout: setup
23
+ setup_step: 2 # optional, highlights step in progress bar
24
+ setup_total: 4 # optional, total number of steps
25
+ ===================================================================
26
+ -->
27
+
28
+ {% include components/setup-check.html %}
29
+
30
+ <div class="container-lg py-4">
31
+ <div class="row justify-content-center">
32
+ <div class="col-lg-9">
33
+
34
+ {% if site_needs_setup %}
35
+ <div class="alert alert-warning d-flex align-items-center mb-4" role="alert">
36
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
37
+ <div>
38
+ <strong>Setup required.</strong>
39
+ Your <code>_config.yml</code> still has default values.
40
+ Follow the instructions on this page or see the
41
+ <a href="{{ '/404.html' | relative_url }}" class="alert-link">setup guide</a>.
42
+ </div>
43
+ </div>
44
+ {% endif %}
45
+
46
+ {% if page.setup_step and page.setup_total %}
47
+ <!-- Progress indicator -->
48
+ <div class="mb-4">
49
+ <div class="d-flex justify-content-between small text-body-secondary mb-1">
50
+ <span>Step {{ page.setup_step }} of {{ page.setup_total }}</span>
51
+ <span>{{ page.setup_step | times: 100 | divided_by: page.setup_total }}% complete</span>
52
+ </div>
53
+ <div class="progress" style="height: 6px;">
54
+ <div class="progress-bar bg-primary"
55
+ role="progressbar"
56
+ style="width: {{ page.setup_step | times: 100 | divided_by: page.setup_total }}%"
57
+ aria-valuenow="{{ page.setup_step }}"
58
+ aria-valuemin="0"
59
+ aria-valuemax="{{ page.setup_total }}">
60
+ </div>
61
+ </div>
62
+ </div>
63
+ {% endif %}
64
+
65
+ {% if page.title %}
66
+ <h1 class="mb-3">{{ page.title }}</h1>
67
+ {% endif %}
68
+
69
+ {{ content }}
70
+
71
+ </div>
72
+ </div>
73
+ </div>
@@ -50,23 +50,31 @@ module Jekyll
50
50
  preview = doc.data['preview']
51
51
  return false if preview.nil? || preview.to_s.strip.empty?
52
52
 
53
+ # Reject non-path values (text descriptions used as preview)
54
+ return false unless preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http')
55
+
53
56
  # Check if the preview file actually exists
54
57
  site = doc.site
55
58
  config = self.config(site)
56
59
 
60
+ # External URL — assume it exists
61
+ return true if preview.start_with?('http')
62
+
57
63
  # Normalize the preview path using assets_prefix
58
64
  normalized_preview = normalize_preview_path(preview, config)
59
65
 
60
- # Build the full path
61
- preview_path = if normalized_preview.start_with?('/')
62
- File.join(site.source, normalized_preview.sub(/^\//, ''))
63
- elsif normalized_preview.start_with?('http')
64
- return true # External URL, assume it exists
65
- else
66
- File.join(site.source, config['output_dir'], normalized_preview)
67
- end
66
+ # Build candidate file paths to check
67
+ candidates = []
68
+ if normalized_preview.start_with?('/')
69
+ candidates << File.join(site.source, normalized_preview.sub(/^\//, ''))
70
+ candidates << File.join(site.source, 'assets', normalized_preview.sub(/^\//, ''))
71
+ else
72
+ candidates << File.join(site.source, 'assets', normalized_preview)
73
+ candidates << File.join(site.source, normalized_preview)
74
+ candidates << File.join(site.source, config['output_dir'], normalized_preview)
75
+ end
68
76
 
69
- File.exist?(preview_path)
77
+ candidates.any? { |path| File.exist?(path) }
70
78
  end
71
79
 
72
80
  # Normalize a preview path by adding assets_prefix if needed
@@ -90,6 +98,9 @@ module Jekyll
90
98
  preview = doc.data['preview']
91
99
  return nil if preview.nil? || preview.to_s.strip.empty?
92
100
 
101
+ # Reject non-path values
102
+ return nil unless preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http')
103
+
93
104
  site = doc.site
94
105
  config = self.config(site)
95
106
 
@@ -162,7 +173,9 @@ module Jekyll
162
173
  # Handle both Hash (from assign) and Document objects
163
174
  if doc.is_a?(Hash)
164
175
  preview = doc['preview']
165
- !preview.nil? && !preview.to_s.strip.empty?
176
+ return false if preview.nil? || preview.to_s.strip.empty?
177
+ # Only consider values that look like image paths or URLs
178
+ preview.to_s.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.to_s.start_with?('http')
166
179
  else
167
180
  PreviewImageGenerator.has_preview?(doc)
168
181
  end
@@ -176,12 +189,13 @@ module Jekyll
176
189
  if doc.is_a?(Hash)
177
190
  preview = doc['preview']
178
191
  return nil if preview.nil? || preview.to_s.strip.empty?
192
+ return nil unless preview.to_s.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.to_s.start_with?('http')
193
+
194
+ return preview if preview.start_with?('/') || preview.start_with?('http')
179
195
 
180
196
  # Get config from context if available
181
197
  site_config = @context.registers[:site].config['preview_images'] || {}
182
198
  output_dir = site_config['output_dir'] || 'assets/images/previews'
183
-
184
- return preview if preview.start_with?('/') || preview.start_with?('http')
185
199
  "#{output_dir}/#{preview}"
186
200
  else
187
201
  PreviewImageGenerator.preview_path(doc)
@@ -743,7 +743,7 @@
743
743
  }
744
744
 
745
745
  // -----------------------------------------------------------------------------
746
- // Info Section Modal Tabs
746
+ // Info Section Offcanvas Tabs
747
747
  // -----------------------------------------------------------------------------
748
748
 
749
749
  #info-section {
@@ -775,7 +775,7 @@
775
775
  padding-top: 1rem;
776
776
  }
777
777
 
778
- // Theme switcher (halfmoon): avoid stretched static menu inside scrollable modal; Popper uses strategy fixed from markup
778
+ // Theme switcher (halfmoon): keep dropdown positioned within offcanvas panel
779
779
  .bd-theme-dropdown {
780
780
  position: relative;
781
781
  }
data/_sass/custom.scss CHANGED
@@ -35,6 +35,28 @@ html, body {
35
35
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, .35), inset 0 -1px 0 rgba(0, 0, 0, .15);
36
36
  }
37
37
 
38
+ // Vendored Bootstrap build may omit vh utilities; landing hero uses this for stable min height
39
+ .min-vh-50 {
40
+ min-height: 50vh;
41
+ }
42
+
43
+ // Landing layout: stable hero media slot (ratio) + no reliance on scroll script for first paint
44
+ .landing-hero {
45
+ .landing-hero-media {
46
+ max-width: min(100%, 28rem);
47
+ }
48
+
49
+ .landing-hero-media.ratio > img {
50
+ object-position: center;
51
+ }
52
+ }
53
+
54
+ @media (prefers-reduced-motion: reduce) {
55
+ .landing-hero .landing-hero-media {
56
+ transition: none;
57
+ }
58
+ }
59
+
38
60
  #TableOfContents {
39
61
  overflow: hidden;
40
62
  text-overflow: ellipsis;
@@ -160,10 +182,7 @@ html, body {
160
182
  border-left-color: var(--bs-primary) !important;
161
183
  }
162
184
 
163
- // Smooth scroll behavior
164
- html {
165
- scroll-behavior: smooth;
166
- }
185
+ // Smooth scroll behavior is declared once below (see "Smooth Scroll Enhancement")
167
186
 
168
187
  // Category active state
169
188
  .btn-toggle-nav a.active {
@@ -454,7 +473,8 @@ img {
454
473
  // ==============================================================================
455
474
 
456
475
  // Hero Section Enhancements
457
- .bg-primary {
476
+ // NOTE: Exclude .zer0-bg-hero so skin-specific gradients from _backgrounds.scss take precedence
477
+ .bg-primary:not(.zer0-bg-hero) {
458
478
  position: relative;
459
479
  overflow: hidden;
460
480
 
@@ -617,8 +637,10 @@ html {
617
637
  }
618
638
 
619
639
  // Section Spacing Improvements
640
+ // Ensure anchored elements clear the fixed navbar when scrolled to
620
641
  section,
621
- .py-5 {
642
+ .py-5,
643
+ [id] {
622
644
  scroll-margin-top: 80px;
623
645
  }
624
646
 
@@ -0,0 +1,95 @@
1
+ // ==========================================================================
2
+ // Background Mixins — reusable fffuel-style layered backgrounds
3
+ // ==========================================================================
4
+ // Usage:
5
+ // @include zer0-bg-gradient; → applies gradient layer
6
+ // @include zer0-bg-noise; → applies noise texture overlay
7
+ // @include zer0-bg-pattern; → applies pattern overlay
8
+ // @include zer0-bg-layered; → combines gradient + noise
9
+ // ==========================================================================
10
+
11
+ /// Gradient background layer (hero / header areas)
12
+ @mixin zer0-bg-gradient {
13
+ position: relative;
14
+ overflow: hidden;
15
+
16
+ &::before {
17
+ content: "";
18
+ position: absolute;
19
+ inset: 0;
20
+ background-image: var(--zer0-bg-gradient);
21
+ background-size: cover;
22
+ background-position: center;
23
+ opacity: var(--zer0-bg-gradient-opacity, 0.6);
24
+ mix-blend-mode: var(--zer0-bg-blend, overlay);
25
+ pointer-events: none;
26
+ z-index: 0;
27
+ }
28
+ }
29
+
30
+ /// Noise texture overlay (body / footer areas)
31
+ @mixin zer0-bg-noise {
32
+ position: relative;
33
+
34
+ &::after {
35
+ content: "";
36
+ position: absolute;
37
+ inset: 0;
38
+ background-image: var(--zer0-bg-noise);
39
+ background-repeat: repeat;
40
+ background-size: 300px 300px;
41
+ opacity: var(--zer0-bg-texture-opacity, 0.04);
42
+ mix-blend-mode: var(--zer0-bg-blend, overlay);
43
+ pointer-events: none;
44
+ z-index: 0;
45
+ }
46
+ }
47
+
48
+ /// Pattern overlay (surface / card areas)
49
+ @mixin zer0-bg-pattern {
50
+ position: relative;
51
+
52
+ &::after {
53
+ content: "";
54
+ position: absolute;
55
+ inset: 0;
56
+ background-image: var(--zer0-bg-pattern);
57
+ background-repeat: repeat;
58
+ background-size: var(--zer0-bg-pattern-size, 60px 60px);
59
+ opacity: var(--zer0-bg-pattern-opacity, 0.08);
60
+ mix-blend-mode: var(--zer0-bg-blend, overlay);
61
+ pointer-events: none;
62
+ z-index: 0;
63
+ }
64
+ }
65
+
66
+ /// Layered: gradient + noise (for hero sections with depth)
67
+ @mixin zer0-bg-layered {
68
+ position: relative;
69
+ overflow: hidden;
70
+
71
+ &::before {
72
+ content: "";
73
+ position: absolute;
74
+ inset: 0;
75
+ background-image: var(--zer0-bg-gradient);
76
+ background-size: cover;
77
+ background-position: center;
78
+ opacity: var(--zer0-bg-gradient-opacity, 0.6);
79
+ mix-blend-mode: var(--zer0-bg-blend, overlay);
80
+ pointer-events: none;
81
+ z-index: 0;
82
+ }
83
+
84
+ &::after {
85
+ content: "";
86
+ position: absolute;
87
+ inset: 0;
88
+ background-image: var(--zer0-bg-noise);
89
+ background-repeat: repeat;
90
+ background-size: 300px 300px;
91
+ opacity: var(--zer0-bg-texture-opacity, 0.04);
92
+ pointer-events: none;
93
+ z-index: 0;
94
+ }
95
+ }
@@ -0,0 +1,156 @@
1
+ // ==========================================================================
2
+ // Backgrounds — fffuel-style SVG background system for zer0-mistakes theme
3
+ // ==========================================================================
4
+ // Each skin maps to gradient, pattern, and noise SVGs.
5
+ // CSS custom properties drive the layering; mixins in _background-mixins.scss
6
+ // apply them to layout zones.
7
+ // ==========================================================================
8
+
9
+ @import "background-mixins";
10
+
11
+ // --------------------------------------------------------------------------
12
+ // Default custom properties (no skin / fallback)
13
+ // --------------------------------------------------------------------------
14
+ :root {
15
+ --zer0-bg-gradient: none;
16
+ --zer0-bg-pattern: none;
17
+ --zer0-bg-noise: none;
18
+ --zer0-bg-gradient-opacity: 0.6;
19
+ --zer0-bg-texture-opacity: 0.04;
20
+ --zer0-bg-pattern-opacity: 0.08;
21
+ --zer0-bg-blend: overlay;
22
+ --zer0-bg-pattern-size: 60px 60px;
23
+ --zer0-bg-enabled: 1; // 1 = on, 0 = off
24
+ }
25
+
26
+ // --------------------------------------------------------------------------
27
+ // Per-skin custom properties
28
+ // Use #{} interpolation so legacy sass-converter passes url() through as-is.
29
+ // Paths are relative to assets/css/main.css so they resolve correctly under
30
+ // any baseurl (e.g. /zer0-pages-remote/ on GitHub Pages subdirectory deploys).
31
+ // --------------------------------------------------------------------------
32
+
33
+ [data-theme-skin="air"] {
34
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/air.svg')"};
35
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/air.svg')"};
36
+ --zer0-bg-noise: #{"url('../backgrounds/noise/air.svg')"};
37
+ --zer0-bg-pattern-size: 50px 50px;
38
+ }
39
+
40
+ [data-theme-skin="aqua"] {
41
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/aqua.svg')"};
42
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/aqua.svg')"};
43
+ --zer0-bg-noise: #{"url('../backgrounds/noise/aqua.svg')"};
44
+ --zer0-bg-pattern-size: 48px 48px;
45
+ }
46
+
47
+ [data-theme-skin="contrast"] {
48
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/contrast.svg')"};
49
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/contrast.svg')"};
50
+ --zer0-bg-noise: #{"url('../backgrounds/noise/contrast.svg')"};
51
+ --zer0-bg-pattern-size: 40px 40px;
52
+ }
53
+
54
+ [data-theme-skin="dark"] {
55
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/dark.svg')"};
56
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/dark.svg')"};
57
+ --zer0-bg-noise: #{"url('../backgrounds/noise/dark.svg')"};
58
+ --zer0-bg-pattern-size: 60px 60px;
59
+ }
60
+
61
+ [data-theme-skin="dirt"] {
62
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/dirt.svg')"};
63
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/dirt.svg')"};
64
+ --zer0-bg-noise: #{"url('../backgrounds/noise/dirt.svg')"};
65
+ --zer0-bg-pattern-size: 50px 50px;
66
+ }
67
+
68
+ [data-theme-skin="neon"] {
69
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/neon.svg')"};
70
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/neon.svg')"};
71
+ --zer0-bg-noise: #{"url('../backgrounds/noise/neon.svg')"};
72
+ --zer0-bg-pattern-size: 48px 48px;
73
+ }
74
+
75
+ [data-theme-skin="mint"] {
76
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/mint.svg')"};
77
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/mint.svg')"};
78
+ --zer0-bg-noise: #{"url('../backgrounds/noise/mint.svg')"};
79
+ --zer0-bg-pattern-size: 50px 50px;
80
+ }
81
+
82
+ [data-theme-skin="plum"] {
83
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/plum.svg')"};
84
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/plum.svg')"};
85
+ --zer0-bg-noise: #{"url('../backgrounds/noise/plum.svg')"};
86
+ --zer0-bg-pattern-size: 55px 55px;
87
+ }
88
+
89
+ [data-theme-skin="sunrise"] {
90
+ --zer0-bg-gradient: #{"url('../backgrounds/gradients/sunrise.svg')"};
91
+ --zer0-bg-pattern: #{"url('../backgrounds/patterns/sunrise.svg')"};
92
+ --zer0-bg-noise: #{"url('../backgrounds/noise/sunrise.svg')"};
93
+ --zer0-bg-pattern-size: 50px 50px;
94
+ }
95
+
96
+ // --------------------------------------------------------------------------
97
+ // Utility classes — apply these to layout zones
98
+ // --------------------------------------------------------------------------
99
+
100
+ .zer0-bg-hero {
101
+ @include zer0-bg-layered;
102
+
103
+ > * { position: relative; z-index: 1; }
104
+ }
105
+
106
+ .zer0-bg-body {
107
+ @include zer0-bg-noise;
108
+
109
+ // Elevate children above the ::before / ::after pseudo-element backgrounds,
110
+ // but exclude fixed-position, offcanvas, and modal elements whose
111
+ // positioning must not be overridden.
112
+ > *:not(.fixed-top):not(.offcanvas):not(.modal) { position: relative; z-index: 1; }
113
+ }
114
+
115
+ .zer0-bg-surface {
116
+ @include zer0-bg-pattern;
117
+
118
+ > * { position: relative; z-index: 1; }
119
+ }
120
+
121
+ .zer0-bg-footer {
122
+ @include zer0-bg-noise;
123
+
124
+ > * { position: relative; z-index: 1; }
125
+ }
126
+
127
+ // --------------------------------------------------------------------------
128
+ // Accessibility: respect reduced-motion
129
+ // --------------------------------------------------------------------------
130
+ @media (prefers-reduced-motion: reduce) {
131
+ .zer0-bg-hero,
132
+ .zer0-bg-body,
133
+ .zer0-bg-surface,
134
+ .zer0-bg-footer {
135
+ &::before,
136
+ &::after {
137
+ animation: none !important;
138
+ transition: none !important;
139
+ }
140
+ }
141
+ }
142
+
143
+ // --------------------------------------------------------------------------
144
+ // Kill switch: when backgrounds are disabled
145
+ // --------------------------------------------------------------------------
146
+ [data-zer0-bg="off"] {
147
+ .zer0-bg-hero,
148
+ .zer0-bg-body,
149
+ .zer0-bg-surface,
150
+ .zer0-bg-footer {
151
+ &::before,
152
+ &::after {
153
+ display: none !important;
154
+ }
155
+ }
156
+ }
@@ -1,3 +1,4 @@
1
- // Barrel: wizard Sass/CSS overrides, then shared --bd-* tokens.
1
+ // Barrel: wizard Sass/CSS overrides, then shared --bd-* tokens, then fffuel backgrounds.
2
2
  @import "theme/wizard-mode";
3
3
  @import "theme/css-variables";
4
+ @import "theme/backgrounds";
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#e8f4f8"/>
5
+ <stop offset="50%" stop-color="#b8d4e3"/>
6
+ <stop offset="100%" stop-color="#6fa8dc"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.006" numOctaves="4" seed="2"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="60"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0077b6"/>
5
+ <stop offset="50%" stop-color="#00b4d8"/>
6
+ <stop offset="100%" stop-color="#90e0ef"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.008" numOctaves="4" seed="5"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="70"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#111111"/>
5
+ <stop offset="50%" stop-color="#333333"/>
6
+ <stop offset="100%" stop-color="#ffcc00"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.005" numOctaves="3" seed="8"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="50"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.4"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#1a1a2e"/>
5
+ <stop offset="50%" stop-color="#16213e"/>
6
+ <stop offset="100%" stop-color="#0f3460"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.007" numOctaves="4" seed="12"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="55"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#5c4033"/>
5
+ <stop offset="50%" stop-color="#8b6914"/>
6
+ <stop offset="100%" stop-color="#d4a574"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.009" numOctaves="4" seed="20"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="65"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#2d6a4f"/>
5
+ <stop offset="50%" stop-color="#52b788"/>
6
+ <stop offset="100%" stop-color="#95d5b2"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.007" numOctaves="4" seed="30"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="60"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#ff006e"/>
5
+ <stop offset="50%" stop-color="#8338ec"/>
6
+ <stop offset="100%" stop-color="#3a86ff"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.006" numOctaves="5" seed="42"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="80"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.6"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#4a0e4e"/>
5
+ <stop offset="50%" stop-color="#812f85"/>
6
+ <stop offset="100%" stop-color="#c77dff"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.006" numOctaves="4" seed="15"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="65"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400">
2
+ <defs>
3
+ <linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#ff6b35"/>
5
+ <stop offset="50%" stop-color="#f7c59f"/>
6
+ <stop offset="100%" stop-color="#efefd0"/>
7
+ </linearGradient>
8
+ <filter id="f1">
9
+ <feTurbulence type="fractalNoise" baseFrequency="0.008" numOctaves="4" seed="7"/>
10
+ <feDisplacementMap in="SourceGraphic" scale="60"/>
11
+ </filter>
12
+ </defs>
13
+ <rect width="100%" height="100%" fill="url(#g1)"/>
14
+ <rect width="100%" height="100%" fill="url(#g1)" filter="url(#f1)" opacity="0.5"/>
15
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
2
+ <filter id="n1">
3
+ <feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
4
+ <feColorMatrix type="saturate" values="0"/>
5
+ </filter>
6
+ <rect width="100%" height="100%" fill="transparent"/>
7
+ <rect width="100%" height="100%" filter="url(#n1)" opacity="0.04"/>
8
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
2
+ <filter id="n1">
3
+ <feTurbulence type="fractalNoise" baseFrequency="0.7" numOctaves="3" stitchTiles="stitch"/>
4
+ <feColorMatrix type="saturate" values="0"/>
5
+ </filter>
6
+ <rect width="100%" height="100%" fill="transparent"/>
7
+ <rect width="100%" height="100%" filter="url(#n1)" opacity="0.05"/>
8
+ </svg>