rizzo-css 0.0.23 → 0.0.25
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.
- package/README.md +2 -2
- package/bin/rizzo-css.js +183 -27
- package/package.json +1 -1
- package/scaffold/astro/Navbar.astro +7 -0
- package/scaffold/astro/Settings.astro +379 -5
- package/scaffold/astro/ThemeSwitcher.astro +7 -2
- package/scaffold/astro-minimal/src/layouts/Layout.astro +3 -0
- package/scaffold/vanilla/README-RIZZO.md +1 -1
- package/scaffold/vanilla/components/accordion.html +14 -0
- package/scaffold/vanilla/components/alert.html +14 -0
- package/scaffold/vanilla/components/avatar.html +14 -0
- package/scaffold/vanilla/components/badge.html +14 -0
- package/scaffold/vanilla/components/breadcrumb.html +14 -0
- package/scaffold/vanilla/components/button.html +14 -0
- package/scaffold/vanilla/components/cards.html +14 -0
- package/scaffold/vanilla/components/copy-to-clipboard.html +14 -0
- package/scaffold/vanilla/components/divider.html +14 -0
- package/scaffold/vanilla/components/dropdown.html +14 -0
- package/scaffold/vanilla/components/forms.html +14 -0
- package/scaffold/vanilla/components/icons.html +14 -0
- package/scaffold/vanilla/components/index.html +14 -0
- package/scaffold/vanilla/components/modal.html +14 -0
- package/scaffold/vanilla/components/navbar.html +14 -0
- package/scaffold/vanilla/components/pagination.html +14 -0
- package/scaffold/vanilla/components/progress-bar.html +14 -0
- package/scaffold/vanilla/components/search.html +14 -0
- package/scaffold/vanilla/components/settings.html +14 -0
- package/scaffold/vanilla/components/spinner.html +14 -0
- package/scaffold/vanilla/components/table.html +14 -0
- package/scaffold/vanilla/components/tabs.html +14 -0
- package/scaffold/vanilla/components/theme-switcher.html +14 -0
- package/scaffold/vanilla/components/toast.html +14 -0
- package/scaffold/vanilla/components/tooltip.html +14 -0
- package/scaffold/vanilla/index.html +14 -0
|
@@ -1,13 +1,387 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
import ThemeSwitcher from './ThemeSwitcher.astro';
|
|
3
|
+
import Close from './icons/Close.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
open?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { open = false } = Astro.props;
|
|
3
10
|
---
|
|
4
|
-
|
|
11
|
+
|
|
12
|
+
<div class="settings" data-settings aria-hidden={open ? 'false' : 'true'}>
|
|
5
13
|
<div class="settings__overlay" data-settings-overlay aria-hidden="true"></div>
|
|
6
|
-
|
|
14
|
+
|
|
15
|
+
<div class="settings__panel" role="dialog" aria-modal="true" aria-labelledby="settings-title" aria-hidden={open ? 'false' : 'true'}>
|
|
7
16
|
<div class="settings__header">
|
|
8
17
|
<h2 id="settings-title" class="settings__title">Settings</h2>
|
|
9
|
-
<button
|
|
18
|
+
<button
|
|
19
|
+
class="settings__close"
|
|
20
|
+
type="button"
|
|
21
|
+
aria-label="Close settings"
|
|
22
|
+
data-settings-close
|
|
23
|
+
>
|
|
24
|
+
<Close width={20} height={20} />
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="settings__content">
|
|
29
|
+
<!-- Theme Section -->
|
|
30
|
+
<section class="settings__section">
|
|
31
|
+
<h3 class="settings__section-title">Theme</h3>
|
|
32
|
+
<div class="settings__control">
|
|
33
|
+
<ThemeSwitcher />
|
|
34
|
+
</div>
|
|
35
|
+
</section>
|
|
36
|
+
|
|
37
|
+
<!-- Font Size Section -->
|
|
38
|
+
<section class="settings__section">
|
|
39
|
+
<h3 class="settings__section-title">Font Size</h3>
|
|
40
|
+
<div class="settings__control">
|
|
41
|
+
<label for="font-size-slider" class="settings__label">
|
|
42
|
+
<span class="settings__label-text">Adjust text size</span>
|
|
43
|
+
<span class="settings__label-value" data-font-size-value>100%</span>
|
|
44
|
+
</label>
|
|
45
|
+
<input
|
|
46
|
+
type="range"
|
|
47
|
+
id="font-size-slider"
|
|
48
|
+
class="settings__slider"
|
|
49
|
+
min="0.75"
|
|
50
|
+
max="1.5"
|
|
51
|
+
step="0.05"
|
|
52
|
+
value="1"
|
|
53
|
+
aria-label="Font size"
|
|
54
|
+
data-font-size-slider
|
|
55
|
+
style="--slider-progress: 50%;"
|
|
56
|
+
/>
|
|
57
|
+
<div class="settings__slider-labels">
|
|
58
|
+
<span>Small</span>
|
|
59
|
+
<span>Default</span>
|
|
60
|
+
<span>Large</span>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</section>
|
|
64
|
+
|
|
65
|
+
<!-- Accessibility Section -->
|
|
66
|
+
<section class="settings__section">
|
|
67
|
+
<h3 class="settings__section-title">Accessibility</h3>
|
|
68
|
+
|
|
69
|
+
<div class="settings__control">
|
|
70
|
+
<label class="settings__checkbox-label">
|
|
71
|
+
<input
|
|
72
|
+
type="checkbox"
|
|
73
|
+
class="settings__checkbox"
|
|
74
|
+
data-reduced-motion
|
|
75
|
+
aria-label="Reduce motion"
|
|
76
|
+
/>
|
|
77
|
+
<span>Reduce motion</span>
|
|
78
|
+
</label>
|
|
79
|
+
<p class="settings__help-text">Minimize animations and transitions</p>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="settings__control">
|
|
83
|
+
<label class="settings__checkbox-label">
|
|
84
|
+
<input
|
|
85
|
+
type="checkbox"
|
|
86
|
+
class="settings__checkbox"
|
|
87
|
+
data-high-contrast
|
|
88
|
+
aria-label="High contrast"
|
|
89
|
+
/>
|
|
90
|
+
<span>High contrast</span>
|
|
91
|
+
</label>
|
|
92
|
+
<p class="settings__help-text">Increase contrast for better visibility</p>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="settings__control">
|
|
96
|
+
<div class="settings__label">
|
|
97
|
+
<span class="settings__label-text">Scrollbar style</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="settings__radio-group" role="radiogroup" aria-label="Scrollbar style">
|
|
100
|
+
<label class="settings__radio-label">
|
|
101
|
+
<input
|
|
102
|
+
type="radio"
|
|
103
|
+
name="scrollbar-style"
|
|
104
|
+
value="thin"
|
|
105
|
+
class="settings__radio"
|
|
106
|
+
data-scrollbar-style
|
|
107
|
+
aria-label="Thin scrollbar"
|
|
108
|
+
checked
|
|
109
|
+
/>
|
|
110
|
+
<span>Thin</span>
|
|
111
|
+
</label>
|
|
112
|
+
<label class="settings__radio-label">
|
|
113
|
+
<input
|
|
114
|
+
type="radio"
|
|
115
|
+
name="scrollbar-style"
|
|
116
|
+
value="thick"
|
|
117
|
+
class="settings__radio"
|
|
118
|
+
data-scrollbar-style
|
|
119
|
+
aria-label="Thick scrollbar"
|
|
120
|
+
/>
|
|
121
|
+
<span>Thick</span>
|
|
122
|
+
</label>
|
|
123
|
+
<label class="settings__radio-label">
|
|
124
|
+
<input
|
|
125
|
+
type="radio"
|
|
126
|
+
name="scrollbar-style"
|
|
127
|
+
value="hidden"
|
|
128
|
+
class="settings__radio"
|
|
129
|
+
data-scrollbar-style
|
|
130
|
+
aria-label="Hidden scrollbars"
|
|
131
|
+
/>
|
|
132
|
+
<span>Hidden</span>
|
|
133
|
+
</label>
|
|
134
|
+
</div>
|
|
135
|
+
<p class="settings__help-text">Choose your preferred scrollbar appearance</p>
|
|
136
|
+
</div>
|
|
137
|
+
</section>
|
|
10
138
|
</div>
|
|
11
|
-
<div class="settings__content"><p>Theme, font size, and accessibility options. Wire to your state and <code>window.openSettings</code>.</p></div>
|
|
12
139
|
</div>
|
|
13
140
|
</div>
|
|
141
|
+
|
|
142
|
+
<script>
|
|
143
|
+
function initSettings() {
|
|
144
|
+
const settings = document.querySelector('[data-settings]');
|
|
145
|
+
if (!settings) return;
|
|
146
|
+
|
|
147
|
+
const overlay = settings.querySelector('[data-settings-overlay]');
|
|
148
|
+
const panel = settings.querySelector('.settings__panel');
|
|
149
|
+
const closeBtn = settings.querySelector('[data-settings-close]');
|
|
150
|
+
const fontSizeSlider = settings.querySelector('[data-font-size-slider]');
|
|
151
|
+
const fontSizeValue = settings.querySelector('[data-font-size-value]');
|
|
152
|
+
const reducedMotion = settings.querySelector('[data-reduced-motion]');
|
|
153
|
+
const highContrast = settings.querySelector('[data-high-contrast]');
|
|
154
|
+
const scrollbarStyleRadios = settings.querySelectorAll('[data-scrollbar-style]');
|
|
155
|
+
const html = document.documentElement;
|
|
156
|
+
|
|
157
|
+
if (!panel || !overlay || !closeBtn) return;
|
|
158
|
+
|
|
159
|
+
// Update slider progress fill
|
|
160
|
+
const updateSliderProgress = (slider) => {
|
|
161
|
+
const min = parseFloat(slider.min);
|
|
162
|
+
const max = parseFloat(slider.max);
|
|
163
|
+
const value = parseFloat(slider.value);
|
|
164
|
+
const progress = ((value - min) / (max - min)) * 100;
|
|
165
|
+
slider.style.setProperty('--slider-progress', `${progress}%`);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Load saved settings
|
|
169
|
+
const loadSettings = () => {
|
|
170
|
+
const savedFontScale = localStorage.getItem('fontSizeScale');
|
|
171
|
+
if (savedFontScale && fontSizeSlider) {
|
|
172
|
+
fontSizeSlider.value = savedFontScale;
|
|
173
|
+
const scale = parseFloat(savedFontScale);
|
|
174
|
+
applyFontSize(scale);
|
|
175
|
+
updateSliderProgress(fontSizeSlider);
|
|
176
|
+
} else if (fontSizeSlider) {
|
|
177
|
+
// Initialize with default value
|
|
178
|
+
updateSliderProgress(fontSizeSlider);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true';
|
|
182
|
+
if (reducedMotion) {
|
|
183
|
+
reducedMotion.checked = savedReducedMotion;
|
|
184
|
+
html.classList.toggle('reduced-motion', savedReducedMotion);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const savedHighContrast = localStorage.getItem('highContrast') === 'true';
|
|
188
|
+
if (highContrast) {
|
|
189
|
+
highContrast.checked = savedHighContrast;
|
|
190
|
+
html.classList.toggle('high-contrast', savedHighContrast);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Load scrollbar style preference
|
|
194
|
+
const savedScrollbarStyle = localStorage.getItem('scrollbarStyle') || 'thin';
|
|
195
|
+
scrollbarStyleRadios.forEach((radio) => {
|
|
196
|
+
const radioInput = radio as HTMLInputElement;
|
|
197
|
+
if (radioInput && radioInput.value === savedScrollbarStyle) {
|
|
198
|
+
radioInput.checked = true;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
applyScrollbarStyle(savedScrollbarStyle);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Apply font size
|
|
205
|
+
const applyFontSize = (scale) => {
|
|
206
|
+
html.style.setProperty('--font-size-scale', scale.toString());
|
|
207
|
+
if (fontSizeValue) {
|
|
208
|
+
fontSizeValue.textContent = `${Math.round(scale * 100)}%`;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Focus trap helper
|
|
213
|
+
const getFocusableElements = (container) => {
|
|
214
|
+
const focusableSelectors = [
|
|
215
|
+
'button:not([disabled])',
|
|
216
|
+
'a[href]',
|
|
217
|
+
'input:not([disabled])',
|
|
218
|
+
'select:not([disabled])',
|
|
219
|
+
'textarea:not([disabled])',
|
|
220
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
221
|
+
].join(', ');
|
|
222
|
+
return Array.from(container.querySelectorAll(focusableSelectors));
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
let previousActiveElement = null;
|
|
226
|
+
|
|
227
|
+
// Open settings
|
|
228
|
+
const openSettings = () => {
|
|
229
|
+
previousActiveElement = document.activeElement;
|
|
230
|
+
|
|
231
|
+
// First, make elements visible but keep panel off-screen
|
|
232
|
+
settings.setAttribute('aria-hidden', 'false');
|
|
233
|
+
if (overlay) overlay.setAttribute('aria-hidden', 'false');
|
|
234
|
+
panel.setAttribute('aria-hidden', 'false');
|
|
235
|
+
|
|
236
|
+
// Ensure panel starts in closed position (remove data-open if it exists)
|
|
237
|
+
panel.removeAttribute('data-open');
|
|
238
|
+
|
|
239
|
+
// Force a reflow to ensure the closed state is rendered
|
|
240
|
+
void panel.offsetHeight;
|
|
241
|
+
|
|
242
|
+
// Then animate in - use double requestAnimationFrame for reliable animation
|
|
243
|
+
requestAnimationFrame(() => {
|
|
244
|
+
requestAnimationFrame(() => {
|
|
245
|
+
panel.setAttribute('data-open', 'true');
|
|
246
|
+
if (closeBtn) closeBtn.focus();
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Close settings
|
|
252
|
+
const closeSettings = () => {
|
|
253
|
+
// Remove data-open first to trigger close animation
|
|
254
|
+
panel.removeAttribute('data-open');
|
|
255
|
+
|
|
256
|
+
// Wait for animation to complete before hiding
|
|
257
|
+
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
258
|
+
const animationDuration = prefersReducedMotion ? 0 : 300;
|
|
259
|
+
|
|
260
|
+
setTimeout(() => {
|
|
261
|
+
settings.setAttribute('aria-hidden', 'true');
|
|
262
|
+
overlay?.setAttribute('aria-hidden', 'true');
|
|
263
|
+
panel.setAttribute('aria-hidden', 'true');
|
|
264
|
+
|
|
265
|
+
// Return focus to previous element
|
|
266
|
+
if (previousActiveElement) {
|
|
267
|
+
previousActiveElement.focus();
|
|
268
|
+
previousActiveElement = null;
|
|
269
|
+
}
|
|
270
|
+
}, animationDuration);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Event listeners
|
|
274
|
+
closeBtn?.addEventListener('click', closeSettings);
|
|
275
|
+
|
|
276
|
+
overlay?.addEventListener('click', closeSettings);
|
|
277
|
+
|
|
278
|
+
// Focus trapping and keyboard handlers
|
|
279
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
280
|
+
if (panel.getAttribute('data-open') !== 'true') return;
|
|
281
|
+
|
|
282
|
+
if (e.key === 'Escape') {
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
closeSettings();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Focus trap: Tab key
|
|
289
|
+
if (e.key === 'Tab') {
|
|
290
|
+
const focusableElements = getFocusableElements(panel);
|
|
291
|
+
if (focusableElements.length === 0) return;
|
|
292
|
+
|
|
293
|
+
const firstElement = focusableElements[0];
|
|
294
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
295
|
+
const activeElement = document.activeElement as HTMLElement;
|
|
296
|
+
|
|
297
|
+
if (e.shiftKey) {
|
|
298
|
+
// Shift + Tab: move backwards
|
|
299
|
+
if (activeElement === firstElement || !panel.contains(activeElement)) {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
lastElement.focus();
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
// Tab: move forwards
|
|
305
|
+
if (activeElement === lastElement || !panel.contains(activeElement)) {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
firstElement.focus();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
314
|
+
|
|
315
|
+
// Font size slider
|
|
316
|
+
if (fontSizeSlider) {
|
|
317
|
+
fontSizeSlider.addEventListener('input', (e) => {
|
|
318
|
+
const target = e.target as HTMLInputElement;
|
|
319
|
+
if (target) {
|
|
320
|
+
const scale = parseFloat(target.value);
|
|
321
|
+
applyFontSize(scale);
|
|
322
|
+
updateSliderProgress(target);
|
|
323
|
+
localStorage.setItem('fontSizeScale', scale.toString());
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Reduced motion
|
|
329
|
+
if (reducedMotion) {
|
|
330
|
+
reducedMotion.addEventListener('change', (e) => {
|
|
331
|
+
const target = e.target as HTMLInputElement;
|
|
332
|
+
if (target) {
|
|
333
|
+
html.classList.toggle('reduced-motion', target.checked);
|
|
334
|
+
localStorage.setItem('reducedMotion', target.checked.toString());
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// High contrast
|
|
340
|
+
if (highContrast) {
|
|
341
|
+
highContrast.addEventListener('change', (e) => {
|
|
342
|
+
const target = e.target as HTMLInputElement;
|
|
343
|
+
if (target) {
|
|
344
|
+
html.classList.toggle('high-contrast', target.checked);
|
|
345
|
+
localStorage.setItem('highContrast', target.checked.toString());
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Apply scrollbar style
|
|
351
|
+
const applyScrollbarStyle = (style: string) => {
|
|
352
|
+
// Remove all scrollbar classes
|
|
353
|
+
html.classList.remove('scrollbar-thin', 'scrollbar-thick', 'scrollbar-hidden', 'hide-scrollbars');
|
|
354
|
+
|
|
355
|
+
// Add the appropriate class
|
|
356
|
+
if (style === 'thick') {
|
|
357
|
+
html.classList.add('scrollbar-thick');
|
|
358
|
+
} else if (style === 'hidden') {
|
|
359
|
+
html.classList.add('scrollbar-hidden', 'hide-scrollbars'); // Keep hide-scrollbars for backward compatibility
|
|
360
|
+
}
|
|
361
|
+
// 'thin' is the default, no class needed
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// Scrollbar style radio buttons
|
|
365
|
+
scrollbarStyleRadios.forEach((radio) => {
|
|
366
|
+
radio.addEventListener('change', (e) => {
|
|
367
|
+
const target = e.target as HTMLInputElement;
|
|
368
|
+
if (target && target.checked) {
|
|
369
|
+
const style = target.value;
|
|
370
|
+
applyScrollbarStyle(style);
|
|
371
|
+
localStorage.setItem('scrollbarStyle', style);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Load settings on init
|
|
377
|
+
loadSettings();
|
|
378
|
+
|
|
379
|
+
// Expose open function globally
|
|
380
|
+
window.openSettings = openSettings;
|
|
381
|
+
}
|
|
382
|
+
if (document.readyState === 'loading') {
|
|
383
|
+
document.addEventListener('DOMContentLoaded', initSettings);
|
|
384
|
+
} else {
|
|
385
|
+
initSettings();
|
|
386
|
+
}
|
|
387
|
+
</script>
|
|
@@ -183,7 +183,7 @@ const InitialIcon = initialTheme ? initialTheme.icon : null;
|
|
|
183
183
|
THEME_SYSTEM,
|
|
184
184
|
} from '../utils/theme';
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
function initThemeSwitcher() {
|
|
187
187
|
const getIconSVG = (themeValue: string) => {
|
|
188
188
|
const svgMap: Record<string, string> = {
|
|
189
189
|
'system': '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" /><circle cx="12" cy="12" r="3" /></svg>',
|
|
@@ -500,5 +500,10 @@ const InitialIcon = initialTheme ? initialTheme.icon : null;
|
|
|
500
500
|
updateAllSwitchers();
|
|
501
501
|
}
|
|
502
502
|
});
|
|
503
|
-
}
|
|
503
|
+
}
|
|
504
|
+
if (document.readyState === 'loading') {
|
|
505
|
+
document.addEventListener('DOMContentLoaded', initThemeSwitcher);
|
|
506
|
+
} else {
|
|
507
|
+
initThemeSwitcher();
|
|
508
|
+
}
|
|
504
509
|
</script>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const DATA_THEME = '{{DATA_THEME}}';
|
|
5
5
|
/** @type {{ title?: string }} */
|
|
6
6
|
const { title = '{{TITLE}}' } = Astro.props;
|
|
7
|
+
{{RIZZO_LAYOUT_IMPORTS}}
|
|
7
8
|
---
|
|
8
9
|
<!doctype html>
|
|
9
10
|
<html lang="en" data-theme={DATA_THEME}>
|
|
@@ -19,8 +20,10 @@ const { title = '{{TITLE}}' } = Astro.props;
|
|
|
19
20
|
</head>
|
|
20
21
|
<body>
|
|
21
22
|
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
23
|
+
{{RIZZO_LAYOUT_BODY_TOP}}
|
|
22
24
|
<main id="main-content">
|
|
23
25
|
<slot />
|
|
24
26
|
</main>
|
|
27
|
+
{{RIZZO_LAYOUT_BODY_BOTTOM}}
|
|
25
28
|
</body>
|
|
26
29
|
</html>
|
|
@@ -13,7 +13,7 @@ If you prefer to load CSS from a CDN instead of the local file, replace the `<li
|
|
|
13
13
|
- `<link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />`
|
|
14
14
|
- Or jsDelivr: `https://cdn.jsdelivr.net/npm/rizzo-css@latest/dist/rizzo.min.css`
|
|
15
15
|
|
|
16
|
-
(Replace `@latest` with a specific version, e.g. `@0.0.
|
|
16
|
+
(Replace `@latest` with a specific version, e.g. `@0.0.25`, in production.)
|
|
17
17
|
|
|
18
18
|
The CLI replaces placeholders in `index.html` (e.g. `{{DATA_THEME}}`, `{{TITLE}}`) when you run `rizzo-css init`. The theme selected during init is used on first load when you have no saved preference in the browser.
|
|
19
19
|
|