rizzo-css 0.0.68 → 0.0.69

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 (86) hide show
  1. package/README.md +3 -3
  2. package/bin/rizzo-css.js +6 -6
  3. package/dist/rizzo.min.css +8 -5
  4. package/package.json +1 -1
  5. package/scaffold/astro/Accordion.astro +1 -1
  6. package/scaffold/astro/Alert.astro +1 -1
  7. package/scaffold/astro/AlertDialog.astro +1 -1
  8. package/scaffold/astro/BackToTop.astro +1 -1
  9. package/scaffold/astro/Breadcrumb.astro +1 -1
  10. package/scaffold/astro/Collapsible.astro +1 -1
  11. package/scaffold/astro/CopyToClipboard.astro +2 -2
  12. package/scaffold/astro/DocsSidebar.astro +2 -1
  13. package/scaffold/astro/Dropdown.astro +1 -1
  14. package/scaffold/astro/FontSwitcher.astro +2 -2
  15. package/scaffold/astro/Footer.astro +1 -1
  16. package/scaffold/astro/Modal.astro +1 -1
  17. package/scaffold/astro/RangeCalendar.astro +215 -0
  18. package/scaffold/astro/Settings.astro +1 -1
  19. package/scaffold/astro/Sheet.astro +1 -1
  20. package/scaffold/astro/Table.astro +2 -2
  21. package/scaffold/astro/Tabs.astro +25 -8
  22. package/scaffold/astro/ThemeIcon.astro +17 -17
  23. package/scaffold/astro/ThemeSwitcher.astro +19 -19
  24. package/scaffold/astro/base/README-RIZZO.md +1 -1
  25. package/scaffold/astro/variants/full/README-RIZZO.md +1 -1
  26. package/scaffold/svelte/Navbar.svelte +14 -3
  27. package/scaffold/svelte/RangeCalendar.svelte +167 -0
  28. package/scaffold/svelte/base/README-RIZZO.md +1 -1
  29. package/scaffold/svelte/index.ts +1 -0
  30. package/scaffold/svelte/variants/full/README-RIZZO.md +1 -1
  31. package/scaffold/vanilla/README-RIZZO.md +2 -2
  32. package/scaffold/vanilla/components/accordion.html +48 -0
  33. package/scaffold/vanilla/components/alert-dialog.html +48 -0
  34. package/scaffold/vanilla/components/alert.html +48 -0
  35. package/scaffold/vanilla/components/aspect-ratio.html +48 -0
  36. package/scaffold/vanilla/components/avatar.html +48 -0
  37. package/scaffold/vanilla/components/back-to-top.html +48 -0
  38. package/scaffold/vanilla/components/badge.html +48 -0
  39. package/scaffold/vanilla/components/breadcrumb.html +48 -0
  40. package/scaffold/vanilla/components/button-group.html +48 -0
  41. package/scaffold/vanilla/components/button.html +48 -0
  42. package/scaffold/vanilla/components/calendar.html +48 -0
  43. package/scaffold/vanilla/components/cards.html +48 -0
  44. package/scaffold/vanilla/components/carousel.html +48 -0
  45. package/scaffold/vanilla/components/collapsible.html +48 -0
  46. package/scaffold/vanilla/components/context-menu.html +48 -0
  47. package/scaffold/vanilla/components/copy-to-clipboard.html +48 -0
  48. package/scaffold/vanilla/components/dashboard.html +48 -0
  49. package/scaffold/vanilla/components/divider.html +48 -0
  50. package/scaffold/vanilla/components/docs-sidebar.html +48 -0
  51. package/scaffold/vanilla/components/dropdown.html +48 -0
  52. package/scaffold/vanilla/components/empty.html +48 -0
  53. package/scaffold/vanilla/components/font-switcher.html +48 -0
  54. package/scaffold/vanilla/components/footer.html +48 -0
  55. package/scaffold/vanilla/components/forms.html +48 -0
  56. package/scaffold/vanilla/components/hover-card.html +48 -0
  57. package/scaffold/vanilla/components/icons.html +48 -0
  58. package/scaffold/vanilla/components/index.html +49 -0
  59. package/scaffold/vanilla/components/input-group.html +48 -0
  60. package/scaffold/vanilla/components/kbd.html +48 -0
  61. package/scaffold/vanilla/components/label.html +48 -0
  62. package/scaffold/vanilla/components/modal.html +48 -0
  63. package/scaffold/vanilla/components/navbar.html +48 -0
  64. package/scaffold/vanilla/components/pagination.html +48 -0
  65. package/scaffold/vanilla/components/popover.html +48 -0
  66. package/scaffold/vanilla/components/progress-bar.html +48 -0
  67. package/scaffold/vanilla/components/range-calendar.html +812 -0
  68. package/scaffold/vanilla/components/resizable.html +48 -0
  69. package/scaffold/vanilla/components/scroll-area.html +48 -0
  70. package/scaffold/vanilla/components/search.html +48 -0
  71. package/scaffold/vanilla/components/separator.html +48 -0
  72. package/scaffold/vanilla/components/settings.html +48 -0
  73. package/scaffold/vanilla/components/sheet.html +48 -0
  74. package/scaffold/vanilla/components/skeleton.html +48 -0
  75. package/scaffold/vanilla/components/slider.html +48 -0
  76. package/scaffold/vanilla/components/sound-effects.html +48 -0
  77. package/scaffold/vanilla/components/spinner.html +48 -0
  78. package/scaffold/vanilla/components/switch.html +48 -0
  79. package/scaffold/vanilla/components/table.html +48 -0
  80. package/scaffold/vanilla/components/tabs.html +48 -0
  81. package/scaffold/vanilla/components/theme-switcher.html +48 -0
  82. package/scaffold/vanilla/components/toast.html +48 -0
  83. package/scaffold/vanilla/components/toggle-group.html +48 -0
  84. package/scaffold/vanilla/components/toggle.html +48 -0
  85. package/scaffold/vanilla/components/tooltip.html +48 -0
  86. package/scaffold/vanilla/index.html +48 -0
@@ -13,6 +13,8 @@ interface Props {
13
13
  tabs: Tab[];
14
14
  id?: string;
15
15
  defaultTab?: string;
16
+ /** When set, selected tab id is stored and restored so framework tabs stay in sync across pages */
17
+ syncStorageKey?: string;
16
18
  variant?: 'default' | 'pills' | 'underline';
17
19
  class?: string;
18
20
  }
@@ -21,17 +23,19 @@ const {
21
23
  tabs,
22
24
  id,
23
25
  defaultTab,
26
+ syncStorageKey,
24
27
  variant = 'default',
25
28
  class: className = '',
26
29
  } = Astro.props;
27
30
 
28
31
  const tabsId = id || `tabs-${Math.random().toString(36).substr(2, 9)}`;
29
- const activeTabId = defaultTab || tabs[0]?.id || '';
32
+ const serverDefaultTab = defaultTab || tabs[0]?.id || '';
33
+ const activeTabId = serverDefaultTab;
30
34
  const variantClass = variant !== 'default' ? `tabs--${variant}` : '';
31
35
  const classes = `tabs ${variantClass} ${className}`.trim();
32
36
  ---
33
37
 
34
- <div class={classes} data-tabs={tabsId}>
38
+ <div class={classes} data-tabs={tabsId} data-sync-storage-key={syncStorageKey || undefined}>
35
39
  <div
36
40
  class="tabs__list"
37
41
  role="tablist"
@@ -91,7 +95,7 @@ const classes = `tabs ${variantClass} ${className}`.trim();
91
95
  </div>
92
96
  </div>
93
97
 
94
- <script define:vars={{ tabsId, activeTabId }}>
98
+ <script define:vars={{ tabsId, activeTabId, syncStorageKey }}>
95
99
  (function initTabs() {
96
100
  const init = () => {
97
101
  const tabsContainer = document.querySelector(`[data-tabs="${tabsId}"]`);
@@ -110,20 +114,33 @@ const classes = `tabs ${variantClass} ${className}`.trim();
110
114
  return;
111
115
  }
112
116
 
113
- // Set initial active state
114
- let activeIndex = Array.from(tabButtons).findIndex(
115
- (btn) => btn.getAttribute('data-tab-id') === activeTabId
116
- );
117
+ const tabIds = Array.from(tabButtons).map((btn) => btn.getAttribute('data-tab-id')).filter(Boolean);
118
+ let initialTabId = activeTabId;
119
+ if (syncStorageKey && typeof sessionStorage !== 'undefined') {
120
+ try {
121
+ const stored = sessionStorage.getItem(syncStorageKey);
122
+ if (stored && tabIds.indexOf(stored) !== -1) {
123
+ initialTabId = stored;
124
+ }
125
+ } catch (_) {}
126
+ }
127
+
128
+ let activeIndex = tabIds.indexOf(initialTabId);
117
129
  if (activeIndex === -1) activeIndex = 0;
118
130
 
119
131
  const activateTab = (index) => {
120
- // Validate index
121
132
  if (index < 0 || index >= tabButtons.length) return;
122
133
 
123
134
  const targetButton = tabButtons[index];
124
135
  const targetTabId = targetButton.getAttribute('data-tab-id');
125
136
  if (!targetTabId) return;
126
137
 
138
+ if (syncStorageKey && typeof sessionStorage !== 'undefined') {
139
+ try {
140
+ sessionStorage.setItem(syncStorageKey, targetTabId);
141
+ } catch (_) {}
142
+ }
143
+
127
144
  // Update all buttons
128
145
  tabButtons.forEach((btn, idx) => {
129
146
  const isActive = idx === index;
@@ -1,21 +1,21 @@
1
1
  ---
2
- import type { ThemeIconKey } from './themes';
3
- import { ALL_THEMES } from './themes';
4
- import Gear from './icons/Gear.astro';
5
- import Owl from './icons/Owl.astro';
6
- import Palette from './icons/Palette.astro';
7
- import Flame from './icons/Flame.astro';
8
- import Sunset from './icons/Sunset.astro';
9
- import Zap from './icons/Zap.astro';
10
- import Shield from './icons/Shield.astro';
11
- import Heart from './icons/Heart.astro';
12
- import Sun from './icons/Sun.astro';
13
- import Cake from './icons/Cake.astro';
14
- import Lemon from './icons/Lemon.astro';
15
- import Rainbow from './icons/Rainbow.astro';
16
- import Leaf from './icons/Leaf.astro';
17
- import Cherry from './icons/Cherry.astro';
18
- import Brush from './icons/Brush.astro';
2
+ import type { ThemeIconKey } from '../../config/themes';
3
+ import { ALL_THEMES } from '../../config/themes';
4
+ import Gear from '../icons/Gear.astro';
5
+ import Owl from '../icons/Owl.astro';
6
+ import Palette from '../icons/Palette.astro';
7
+ import Flame from '../icons/Flame.astro';
8
+ import Sunset from '../icons/Sunset.astro';
9
+ import Zap from '../icons/Zap.astro';
10
+ import Shield from '../icons/Shield.astro';
11
+ import Heart from '../icons/Heart.astro';
12
+ import Sun from '../icons/Sun.astro';
13
+ import Cake from '../icons/Cake.astro';
14
+ import Lemon from '../icons/Lemon.astro';
15
+ import Rainbow from '../icons/Rainbow.astro';
16
+ import Leaf from '../icons/Leaf.astro';
17
+ import Cherry from '../icons/Cherry.astro';
18
+ import Brush from '../icons/Brush.astro';
19
19
 
20
20
  const iconMap: Record<ThemeIconKey, typeof Owl> = {
21
21
  gear: Gear,
@@ -1,22 +1,22 @@
1
1
  ---
2
- import ChevronDown from './icons/ChevronDown.astro';
3
- import Gear from './icons/Gear.astro';
4
- import Palette from './icons/Palette.astro';
5
- import Owl from './icons/Owl.astro';
6
- import Sun from './icons/Sun.astro';
7
- import Flame from './icons/Flame.astro';
8
- import Heart from './icons/Heart.astro';
9
- import Leaf from './icons/Leaf.astro';
10
- import Shield from './icons/Shield.astro';
11
- import Zap from './icons/Zap.astro';
12
- import Cake from './icons/Cake.astro';
13
- import Sunset from './icons/Sunset.astro';
14
- import Cherry from './icons/Cherry.astro';
15
- import Brush from './icons/Brush.astro';
16
- import Lemon from './icons/Lemon.astro';
17
- import Rainbow from './icons/Rainbow.astro';
18
- import type { ThemeIconKey } from './themes';
19
- import { THEMES_DARK, THEMES_LIGHT } from './themes';
2
+ import ChevronDown from '../icons/ChevronDown.astro';
3
+ import Gear from '../icons/Gear.astro';
4
+ import Palette from '../icons/Palette.astro';
5
+ import Owl from '../icons/Owl.astro';
6
+ import Sun from '../icons/Sun.astro';
7
+ import Flame from '../icons/Flame.astro';
8
+ import Heart from '../icons/Heart.astro';
9
+ import Leaf from '../icons/Leaf.astro';
10
+ import Shield from '../icons/Shield.astro';
11
+ import Zap from '../icons/Zap.astro';
12
+ import Cake from '../icons/Cake.astro';
13
+ import Sunset from '../icons/Sunset.astro';
14
+ import Cherry from '../icons/Cherry.astro';
15
+ import Brush from '../icons/Brush.astro';
16
+ import Lemon from '../icons/Lemon.astro';
17
+ import Rainbow from '../icons/Rainbow.astro';
18
+ import type { ThemeIconKey } from '../../config/themes';
19
+ import { THEMES_DARK, THEMES_LIGHT } from '../../config/themes';
20
20
 
21
21
  const iconMap: Record<ThemeIconKey, typeof Owl> = {
22
22
  gear: Gear,
@@ -192,7 +192,7 @@ const InitialIcon = initialTheme ? initialTheme.icon : null;
192
192
  resolveSystemTheme,
193
193
  getThemeLabel,
194
194
  THEME_SYSTEM,
195
- } from '../utils/theme';
195
+ } from '../../utils/theme';
196
196
 
197
197
  function initThemeSwitcher() {
198
198
  const getIconSVG = (themeValue: string) => {
@@ -15,7 +15,7 @@
15
15
  Design system · Vanilla · Astro · Svelte
16
16
  </pre>
17
17
 
18
- Astro project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Astro, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = base + component picker (all 52 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
18
+ Astro project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Astro, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = base + component picker (all 53 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
19
19
 
20
20
  ## Setup
21
21
 
@@ -15,7 +15,7 @@
15
15
  Design system · Vanilla · Astro · Svelte
16
16
  </pre>
17
17
 
18
- Astro project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Astro, then **Full** template. **Full** = clone of the Rizzo docs site (no component picker). **Landing** / **Docs** / **Dashboard** = base + component picker (all 52 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
18
+ Astro project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Astro, then **Full** template. **Full** = clone of the Rizzo docs site (no component picker). **Landing** / **Docs** / **Dashboard** = base + component picker (all 53 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
19
19
 
20
20
  ## Setup
21
21
 
@@ -10,6 +10,13 @@
10
10
  let { siteName = 'Site', logo }: Props = $props();
11
11
  let menuOpen = $state(false);
12
12
 
13
+ const menuLinks = [
14
+ { href: '/docs', label: 'Docs' },
15
+ { href: '/docs/components', label: 'Components' },
16
+ { href: '/blocks', label: 'Blocks' },
17
+ { href: '/themes', label: 'Themes' },
18
+ ];
19
+
13
20
  // Click outside and Escape to close mobile menu
14
21
  $effect(() => {
15
22
  if (!menuOpen) return;
@@ -44,7 +51,7 @@
44
51
  </div>
45
52
  <div class="navbar__actions-desktop">
46
53
  <Search id="search-navbar" />
47
- <button type="button" class="navbar__settings-btn" aria-label="Open settings" onclick="window.openSettings && window.openSettings()">
54
+ <button type="button" class="navbar__settings-btn" aria-label="Open settings" onclick={() => window.openSettings?.()}>
48
55
  <Gear width={20} height={20} class="navbar__settings-icon" />
49
56
  <span class="navbar__settings-label">Settings</span>
50
57
  </button>
@@ -58,8 +65,12 @@
58
65
  >
59
66
  <span class="navbar__toggle-icon" aria-hidden="true"><span></span><span></span><span></span></span>
60
67
  </button>
61
- <div class="navbar__menu" class:navbar__menu--open={menuOpen} aria-hidden={!menuOpen}>
62
- <a href="/" class="navbar__link">Home</a>
68
+ <div class="navbar__menu" class:navbar__menu--open={menuOpen} aria-hidden={!menuOpen} role="navigation" aria-label="Mobile menu">
69
+ {#each menuLinks as link}
70
+ <div class="navbar__item">
71
+ <a href={link.href} class="navbar__link" tabindex={menuOpen ? 0 : -1}>{link.label}</a>
72
+ </div>
73
+ {/each}
63
74
  </div>
64
75
  </div>
65
76
  </nav>
@@ -0,0 +1,167 @@
1
+ <script lang="ts">
2
+ const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
3
+ const WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
4
+
5
+ interface Props {
6
+ id?: string;
7
+ initialMonth?: string;
8
+ rangeStart?: string | null;
9
+ rangeEnd?: string | null;
10
+ label?: string;
11
+ class?: string;
12
+ }
13
+
14
+ let {
15
+ id: calendarId,
16
+ initialMonth,
17
+ rangeStart: controlledStart,
18
+ rangeEnd: controlledEnd,
19
+ label = 'Choose date range',
20
+ class: className = '',
21
+ onRangeSelect,
22
+ }: Props & { onRangeSelect?: (start: string, end: string) => void } = $props();
23
+
24
+ const id = $derived(calendarId ?? `range-calendar-${Math.random().toString(36).slice(2, 9)}`);
25
+ const now = new Date();
26
+
27
+ function parseInitial(): { year: number; month: number } {
28
+ if (!initialMonth) return { year: now.getFullYear(), month: now.getMonth() };
29
+ const [y, m] = initialMonth.split('-').map(Number);
30
+ if (!y || !m || m < 1 || m > 12) return { year: now.getFullYear(), month: now.getMonth() };
31
+ return { year: y, month: m - 1 };
32
+ }
33
+
34
+ let view = $state(parseInitial());
35
+ let internalStart = $state<string | null>(null);
36
+ let internalEnd = $state<string | null>(null);
37
+
38
+ const start = $derived(controlledStart !== undefined ? controlledStart : internalStart);
39
+ const end = $derived(controlledEnd !== undefined ? controlledEnd : internalEnd);
40
+
41
+ function toYYYYMMDD(year: number, month: number, day: number): string {
42
+ return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
43
+ }
44
+
45
+ function dateStrToMs(s: string): number | null {
46
+ const d = new Date(s);
47
+ return Number.isNaN(d.getTime()) ? null : d.getTime();
48
+ }
49
+
50
+ function getMonthGrid(year: number, month: number): { day: number; year: number; month: number; currentMonth: boolean }[][] {
51
+ const first = new Date(year, month, 1);
52
+ const last = new Date(year, month + 1, 0);
53
+ const startDay = first.getDay();
54
+ const daysInMonth = last.getDate();
55
+ const weeks: { day: number; year: number; month: number; currentMonth: boolean }[][] = [];
56
+ let week: { day: number; year: number; month: number; currentMonth: boolean }[] = [];
57
+ const prevMonth = month === 0 ? 11 : month - 1;
58
+ const prevYear = month === 0 ? year - 1 : year;
59
+ const prevLast = new Date(prevYear, prevMonth + 1, 0).getDate();
60
+ for (let i = 0; i < startDay; i++) {
61
+ week.push({ day: prevLast - startDay + i + 1, year: prevYear, month: prevMonth, currentMonth: false });
62
+ }
63
+ for (let d = 1; d <= daysInMonth; d++) {
64
+ week.push({ day: d, year, month, currentMonth: true });
65
+ if (week.length === 7) {
66
+ weeks.push(week);
67
+ week = [];
68
+ }
69
+ }
70
+ const nextMonth = month === 11 ? 0 : month + 1;
71
+ const nextYear = month === 11 ? year + 1 : year;
72
+ for (let i = 1; week.length < 7; i++) {
73
+ week.push({ day: i, year: nextYear, month: nextMonth, currentMonth: false });
74
+ }
75
+ weeks.push(week);
76
+ return weeks;
77
+ }
78
+
79
+ const todayStr = $derived(toYYYYMMDD(now.getFullYear(), now.getMonth(), now.getDate()));
80
+ const weeks = $derived(getMonthGrid(view.year, view.month));
81
+ const monthLabel = $derived(`${MONTHS[view.month]} ${view.year}`);
82
+
83
+ const startMs = $derived(start ? dateStrToMs(start) : null);
84
+ const endMs = $derived(end ? dateStrToMs(end) : null);
85
+ const minMax = $derived(
86
+ startMs != null && endMs != null
87
+ ? (startMs <= endMs ? [startMs, endMs] as const : [endMs, startMs] as const)
88
+ : [null, null] as const
89
+ );
90
+
91
+ function isInRange(dateStr: string): boolean {
92
+ const [minMs, maxMs] = minMax;
93
+ if (minMs == null || maxMs == null) return false;
94
+ const ms = dateStrToMs(dateStr);
95
+ if (ms == null) return false;
96
+ if (start === dateStr || end === dateStr) return false;
97
+ return ms >= minMs && ms <= maxMs;
98
+ }
99
+
100
+ function prevMonth() {
101
+ view = view.month === 0 ? { year: view.year - 1, month: 11 } : { year: view.year, month: view.month - 1 };
102
+ }
103
+
104
+ function nextMonth() {
105
+ view = view.month === 11 ? { year: view.year + 1, month: 0 } : { year: view.year, month: view.month + 1 };
106
+ }
107
+
108
+ function onDayClick(dateStr: string) {
109
+ if (controlledStart !== undefined && controlledEnd !== undefined) return;
110
+ if (start === null || (start !== null && end !== null)) {
111
+ internalStart = dateStr;
112
+ internalEnd = null;
113
+ } else {
114
+ const s = dateStrToMs(start!);
115
+ const e = dateStrToMs(dateStr);
116
+ if (s != null && e != null && e < s) {
117
+ internalEnd = start;
118
+ internalStart = dateStr;
119
+ onRangeSelect?.(dateStr, start!);
120
+ } else {
121
+ internalEnd = dateStr;
122
+ onRangeSelect?.(start!, dateStr);
123
+ }
124
+ }
125
+ }
126
+ </script>
127
+
128
+ <div class="calendar calendar--range {className}" {id} role="group" aria-label={label} data-range-calendar>
129
+ <div class="calendar__header">
130
+ <button type="button" class="calendar__prev" aria-label="Previous month" onclick={prevMonth}>
131
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
132
+ </button>
133
+ <div class="calendar__month" aria-live="polite">{monthLabel}</div>
134
+ <button type="button" class="calendar__next" aria-label="Next month" onclick={nextMonth}>
135
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>
136
+ </button>
137
+ </div>
138
+ <div class="calendar__grid" role="grid" aria-label={monthLabel}>
139
+ <div class="calendar__row" role="row">
140
+ {#each WEEKDAYS as day}
141
+ <div class="calendar__weekday" role="columnheader" aria-label={day}>{day}</div>
142
+ {/each}
143
+ </div>
144
+ {#each weeks as week}
145
+ <div class="calendar__row" role="row">
146
+ {#each week as cell}
147
+ {@const dateStr = toYYYYMMDD(cell.year, cell.month, cell.day)}
148
+ <div role="gridcell">
149
+ <button
150
+ type="button"
151
+ class="calendar__day
152
+ {!cell.currentMonth ? ' calendar__day--other-month' : ''}
153
+ {dateStr === todayStr ? ' calendar__day--today' : ''}
154
+ {start === dateStr ? ' calendar__day--range-start' : ''}
155
+ {end === dateStr ? ' calendar__day--range-end' : ''}
156
+ {isInRange(dateStr) ? ' calendar__day--in-range' : ''}"
157
+ aria-label="Choose {MONTHS[cell.month]} {cell.day}, {cell.year}"
158
+ onclick={() => onDayClick(dateStr)}
159
+ >
160
+ {cell.day}
161
+ </button>
162
+ </div>
163
+ {/each}
164
+ </div>
165
+ {/each}
166
+ </div>
167
+ </div>
@@ -15,7 +15,7 @@
15
15
  Design system · Vanilla · Astro · Svelte
16
16
  </pre>
17
17
 
18
- SvelteKit project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Svelte, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = base + component picker (all 52 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
18
+ SvelteKit project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Svelte, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = base + component picker (all 53 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
19
19
 
20
20
  ## Setup
21
21
 
@@ -6,6 +6,7 @@ export { default as Button } from './Button.svelte';
6
6
  export { default as Badge } from './Badge.svelte';
7
7
  export { default as Card } from './Card.svelte';
8
8
  export { default as Calendar } from './Calendar.svelte';
9
+ export { default as RangeCalendar } from './RangeCalendar.svelte';
9
10
  export { default as Carousel } from './Carousel.svelte';
10
11
  export { default as Dashboard } from './Dashboard.svelte';
11
12
  export { default as Divider } from './Divider.svelte';
@@ -15,7 +15,7 @@
15
15
  Design system · Vanilla · Astro · Svelte
16
16
  </pre>
17
17
 
18
- SvelteKit project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Svelte, then **Full** template. **Full** = clone of the Rizzo docs site (no component picker). **Landing** / **Docs** / **Dashboard** = base + component picker (all 52 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
18
+ SvelteKit project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and Svelte, then **Full** template. **Full** = clone of the Rizzo docs site (no component picker). **Landing** / **Docs** / **Dashboard** = base + component picker (all 53 or pick). Same template choice for **Add to existing** (`npx rizzo-css add`).
19
19
 
20
20
  ## Setup
21
21
 
@@ -15,7 +15,7 @@
15
15
  Design system · Vanilla · Astro · Svelte
16
16
  </pre>
17
17
 
18
- This project was scaffolded with `npx rizzo-css init` when you chose **Create new project** and Vanilla JS, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = CSS, fonts, icons, sfx + component picker (all 52 or pick); Landing adds hero/features; Docs adds sidebar + sample doc; Dashboard adds sidebar + stats/table. **Add to existing project** (or `npx rizzo-css add`) uses the **same template choice**; you must add the stylesheet `<link>` yourself (CLI prints the exact tag).
18
+ This project was scaffolded with `npx rizzo-css init` when you chose **Create new project** and Vanilla JS, then **Landing**, **Docs**, **Dashboard**, or **Full**. **Full** = clone of the docs site. **Landing** / **Docs** / **Dashboard** = CSS, fonts, icons, sfx + component picker (all 53 or pick); Landing adds hero/features; Docs adds sidebar + sample doc; Dashboard adds sidebar + stats/table. **Add to existing project** (or `npx rizzo-css add`) uses the **same template choice**; you must add the stylesheet `<link>` yourself (CLI prints the exact tag).
19
19
 
20
20
  ## First-time setup
21
21
 
@@ -28,7 +28,7 @@ If you prefer to load CSS from a CDN instead of the local file, replace the `<li
28
28
  - `<link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />`
29
29
  - Or jsDelivr: `https://cdn.jsdelivr.net/npm/rizzo-css@latest/dist/rizzo.min.css`
30
30
 
31
- (Replace `@latest` with a specific version, e.g. `@0.0.68`, in production.)
31
+ (Replace `@latest` with a specific version, e.g. `@0.0.69`, in production.)
32
32
 
33
33
  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.
34
34