rizzo-css 0.0.80 → 0.0.82

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 (111) hide show
  1. package/README.md +1 -1
  2. package/bin/rizzo-css.js +15 -3
  3. package/package.json +1 -1
  4. package/scaffold/react/DocsSidebar.tsx +130 -28
  5. package/scaffold/react/ThemeIcon.tsx +57 -0
  6. package/scaffold/react/base/package.json +1 -1
  7. package/scaffold/react/themes.ts +54 -0
  8. package/scaffold/react/variants/full/README-RIZZO.md +35 -0
  9. package/scaffold/react/variants/full/package.json +24 -0
  10. package/scaffold/react/variants/full/src/App.tsx +41 -0
  11. package/scaffold/react/variants/full/src/config/componentCategories.ts +43 -0
  12. package/scaffold/react/variants/full/src/config/docsNav.ts +70 -0
  13. package/scaffold/react/variants/full/src/index.css +96 -0
  14. package/scaffold/react/variants/full/src/layouts/BlocksLayout.tsx +124 -0
  15. package/scaffold/react/variants/full/src/layouts/DocsLayout.tsx +93 -0
  16. package/scaffold/react/variants/full/src/main.tsx +13 -0
  17. package/scaffold/react/variants/full/src/views/BlocksIndex.tsx +56 -0
  18. package/scaffold/react/variants/full/src/views/DocsComponents.tsx +56 -0
  19. package/scaffold/react/variants/full/src/views/DocsGettingStarted.tsx +27 -0
  20. package/scaffold/react/variants/full/src/views/DocsIndex.tsx +39 -0
  21. package/scaffold/react/variants/full/src/views/DocsOverview.tsx +39 -0
  22. package/scaffold/react/variants/full/src/views/Home.tsx +104 -0
  23. package/scaffold/react/variants/full/src/views/Themes.tsx +118 -0
  24. package/scaffold/svelte/Settings.svelte +11 -48
  25. package/scaffold/svelte/variants/full/README-RIZZO.md +1 -0
  26. package/scaffold/svelte/variants/full/src/config/docsNav.ts +23 -3
  27. package/scaffold/svelte/variants/full/src/routes/+layout.svelte +4 -1
  28. package/scaffold/svelte/variants/full/src/routes/blocks/+layout.svelte +4 -2
  29. package/scaffold/svelte/variants/full/src/routes/docs/+layout.svelte +3 -1
  30. package/scaffold/svelte/variants/full/src/routes/themes/+page.svelte +130 -69
  31. package/scaffold/vanilla/README-RIZZO.md +1 -1
  32. package/scaffold/vanilla/components/accordion.html +8 -0
  33. package/scaffold/vanilla/components/alert-dialog.html +8 -0
  34. package/scaffold/vanilla/components/alert.html +8 -0
  35. package/scaffold/vanilla/components/aspect-ratio.html +8 -0
  36. package/scaffold/vanilla/components/avatar.html +8 -0
  37. package/scaffold/vanilla/components/back-to-top.html +8 -0
  38. package/scaffold/vanilla/components/badge.html +8 -0
  39. package/scaffold/vanilla/components/breadcrumb.html +8 -0
  40. package/scaffold/vanilla/components/button-group.html +8 -0
  41. package/scaffold/vanilla/components/button.html +8 -0
  42. package/scaffold/vanilla/components/calendar.html +8 -0
  43. package/scaffold/vanilla/components/cards.html +8 -0
  44. package/scaffold/vanilla/components/carousel.html +8 -0
  45. package/scaffold/vanilla/components/chart.html +8 -0
  46. package/scaffold/vanilla/components/collapsible.html +8 -0
  47. package/scaffold/vanilla/components/command.html +8 -0
  48. package/scaffold/vanilla/components/context-menu.html +8 -0
  49. package/scaffold/vanilla/components/copy-to-clipboard.html +8 -0
  50. package/scaffold/vanilla/components/dashboard.html +8 -0
  51. package/scaffold/vanilla/components/direction.html +8 -0
  52. package/scaffold/vanilla/components/divider.html +8 -0
  53. package/scaffold/vanilla/components/docs-sidebar.html +8 -0
  54. package/scaffold/vanilla/components/dropdown.html +8 -0
  55. package/scaffold/vanilla/components/empty.html +8 -0
  56. package/scaffold/vanilla/components/font-switcher.html +8 -0
  57. package/scaffold/vanilla/components/footer.html +8 -0
  58. package/scaffold/vanilla/components/forms.html +8 -0
  59. package/scaffold/vanilla/components/hover-card.html +8 -0
  60. package/scaffold/vanilla/components/icons.html +8 -0
  61. package/scaffold/vanilla/components/index.html +8 -0
  62. package/scaffold/vanilla/components/input-group.html +8 -0
  63. package/scaffold/vanilla/components/input-otp.html +8 -0
  64. package/scaffold/vanilla/components/kbd.html +8 -0
  65. package/scaffold/vanilla/components/label.html +8 -0
  66. package/scaffold/vanilla/components/menubar.html +8 -0
  67. package/scaffold/vanilla/components/modal.html +8 -0
  68. package/scaffold/vanilla/components/navbar.html +8 -0
  69. package/scaffold/vanilla/components/pagination.html +8 -0
  70. package/scaffold/vanilla/components/popover.html +8 -0
  71. package/scaffold/vanilla/components/progress-bar.html +8 -0
  72. package/scaffold/vanilla/components/range-calendar.html +8 -0
  73. package/scaffold/vanilla/components/resizable.html +8 -0
  74. package/scaffold/vanilla/components/scroll-area.html +8 -0
  75. package/scaffold/vanilla/components/search.html +8 -0
  76. package/scaffold/vanilla/components/separator.html +8 -0
  77. package/scaffold/vanilla/components/settings.html +8 -0
  78. package/scaffold/vanilla/components/sheet.html +8 -0
  79. package/scaffold/vanilla/components/skeleton.html +8 -0
  80. package/scaffold/vanilla/components/slider.html +8 -0
  81. package/scaffold/vanilla/components/sound-effects.html +8 -0
  82. package/scaffold/vanilla/components/spinner.html +8 -0
  83. package/scaffold/vanilla/components/switch.html +8 -0
  84. package/scaffold/vanilla/components/table.html +8 -0
  85. package/scaffold/vanilla/components/tabs.html +8 -0
  86. package/scaffold/vanilla/components/theme-switcher.html +8 -0
  87. package/scaffold/vanilla/components/toast.html +8 -0
  88. package/scaffold/vanilla/components/toggle-group.html +8 -0
  89. package/scaffold/vanilla/components/toggle.html +8 -0
  90. package/scaffold/vanilla/components/tooltip.html +8 -0
  91. package/scaffold/vanilla/index.html +8 -0
  92. package/scaffold/vue/DocsSidebar.vue +132 -4
  93. package/scaffold/vue/ThemeIcon.vue +50 -0
  94. package/scaffold/vue/base/package.json +1 -1
  95. package/scaffold/vue/themes.ts +54 -0
  96. package/scaffold/vue/variants/full/README-RIZZO.md +35 -0
  97. package/scaffold/vue/variants/full/package.json +22 -0
  98. package/scaffold/vue/variants/full/src/App.vue +17 -0
  99. package/scaffold/vue/variants/full/src/config/componentCategories.ts +43 -0
  100. package/scaffold/vue/variants/full/src/config/docsNav.ts +70 -0
  101. package/scaffold/vue/variants/full/src/layouts/BlocksLayout.vue +136 -0
  102. package/scaffold/vue/variants/full/src/layouts/DocsLayout.vue +145 -0
  103. package/scaffold/vue/variants/full/src/main.ts +6 -0
  104. package/scaffold/vue/variants/full/src/router/index.ts +38 -0
  105. package/scaffold/vue/variants/full/src/views/BlocksIndex.vue +111 -0
  106. package/scaffold/vue/variants/full/src/views/DocsComponents.vue +115 -0
  107. package/scaffold/vue/variants/full/src/views/DocsGettingStarted.vue +45 -0
  108. package/scaffold/vue/variants/full/src/views/DocsIndex.vue +69 -0
  109. package/scaffold/vue/variants/full/src/views/DocsOverview.vue +66 -0
  110. package/scaffold/vue/variants/full/src/views/Home.vue +240 -0
  111. package/scaffold/vue/variants/full/src/views/Themes.vue +257 -0
package/README.md CHANGED
@@ -72,7 +72,7 @@ import 'rizzo-css';
72
72
  **Without a bundler (plain HTML):** Use a CDN. Both unpkg and jsDelivr resolve the package root to the built CSS (via the `unpkg` / `jsdelivr` fields in this package). For reliability or to pin a version, use the explicit path:
73
73
 
74
74
  ```html
75
- <!-- unpkg (pin version: replace @latest with @0.0.80 or any version) -->
75
+ <!-- unpkg (pin version: replace @latest with @0.0.82 or any version) -->
76
76
  <link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />
77
77
 
78
78
  <!-- or jsDelivr -->
package/bin/rizzo-css.js CHANGED
@@ -199,7 +199,7 @@ const SVELTE_COMPONENTS = [
199
199
  'Button', 'Badge', 'Card', 'Calendar', 'RangeCalendar', 'Carousel', 'Chart', 'Dashboard', 'Divider', 'DocsSidebar', 'Footer', 'Spinner', 'ProgressBar', 'Avatar', 'Alert',
200
200
  'BackToTop', 'Breadcrumb', 'Command', 'FormGroup', 'Input', 'InputGroup', 'InputOtp', 'Checkbox', 'Textarea', 'Select', 'Radio',
201
201
  'CopyToClipboard', 'Tooltip', 'Pagination', 'Tabs', 'Accordion', 'Dropdown', 'Direction', 'Menubar',
202
- 'Modal', 'Toast', 'Table', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
202
+ 'Modal', 'Toast', 'Table', 'ThemeIcon', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
203
203
  'Navbar', 'Settings', 'Search', 'Icons', 'Skeleton', 'Switch',
204
204
  'Label', 'Kbd', 'Collapsible', 'AlertDialog', 'AspectRatio', 'ButtonGroup', 'Empty', 'Separator',
205
205
  'Slider', 'Sheet', 'Popover', 'Toggle', 'ToggleGroup', 'ScrollArea', 'HoverCard', 'ContextMenu', 'ResizablePaneGroup', 'ResizablePane', 'ResizableHandle',
@@ -208,7 +208,7 @@ const ASTRO_COMPONENTS = [
208
208
  'Button', 'Badge', 'Card', 'Calendar', 'RangeCalendar', 'Carousel', 'Chart', 'Dashboard', 'Divider', 'DocsSidebar', 'Footer', 'Spinner', 'ProgressBar', 'Avatar', 'Alert',
209
209
  'BackToTop', 'Breadcrumb', 'Command', 'FormGroup', 'Input', 'InputGroup', 'InputOtp', 'Checkbox', 'Textarea', 'Select', 'Radio',
210
210
  'CopyToClipboard', 'Tooltip', 'Pagination', 'Tabs', 'Accordion', 'Dropdown', 'Direction', 'Menubar',
211
- 'Modal', 'Toast', 'Table', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
211
+ 'Modal', 'Toast', 'Table', 'ThemeIcon', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
212
212
  'Navbar', 'Settings', 'Search', 'Icons', 'Skeleton', 'Switch',
213
213
  'Label', 'Kbd', 'Collapsible', 'AlertDialog', 'AspectRatio', 'ButtonGroup', 'Empty', 'Separator',
214
214
  'Slider', 'Sheet', 'Popover', 'Toggle', 'ToggleGroup', 'ScrollArea', 'HoverCard', 'ContextMenu', 'ResizablePaneGroup', 'ResizablePane', 'ResizableHandle',
@@ -221,7 +221,7 @@ const RECOMMENDED_COMPONENTS = [
221
221
  'Button', 'Badge', 'Card', 'Calendar', 'RangeCalendar', 'Carousel', 'Dashboard', 'Divider', 'DocsSidebar', 'Footer', 'Spinner', 'ProgressBar', 'Avatar', 'Alert',
222
222
  'BackToTop', 'Breadcrumb', 'FormGroup', 'Input', 'Checkbox', 'Textarea', 'Select', 'Radio',
223
223
  'CopyToClipboard', 'Tooltip', 'Pagination', 'Tabs', 'Accordion', 'Dropdown',
224
- 'Modal', 'Toast', 'Table', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
224
+ 'Modal', 'Toast', 'Table', 'ThemeIcon', 'ThemeSwitcher', 'FontSwitcher', 'SoundEffects',
225
225
  'Navbar', 'Search', 'Settings', 'Icons', 'Skeleton', 'Switch',
226
226
  'Label', 'Kbd', 'Collapsible', 'AlertDialog', 'AspectRatio', 'ButtonGroup', 'Empty', 'Separator',
227
227
  'Slider', 'Sheet', 'Popover', 'Toggle', 'ToggleGroup', 'ScrollArea', 'HoverCard', 'ContextMenu', 'ResizablePaneGroup', 'ResizablePane', 'ResizableHandle',
@@ -2036,6 +2036,12 @@ function copyReactComponents(projectDir, selectedNames, opts) {
2036
2036
  }
2037
2037
  const iconsSrc = join(scaffoldDir, 'icons');
2038
2038
  if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
2039
+ if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
2040
+ const themesSrc = join(scaffoldDir, 'themes.ts');
2041
+ if (existsSync(themesSrc)) {
2042
+ copyFileSync(themesSrc, join(targetDir, 'themes.ts'));
2043
+ }
2044
+ }
2039
2045
  if (toCopy.includes('Settings')) {
2040
2046
  const configDir = getScaffoldConfigDir();
2041
2047
  const fontsSrc = join(configDir, 'fonts.ts');
@@ -2098,6 +2104,12 @@ function copyVueComponents(projectDir, selectedNames, opts) {
2098
2104
  }
2099
2105
  const iconsSrc = join(scaffoldDir, 'icons');
2100
2106
  if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
2107
+ if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
2108
+ const themesSrc = join(scaffoldDir, 'themes.ts');
2109
+ if (existsSync(themesSrc)) {
2110
+ copyFileSync(themesSrc, join(targetDir, 'themes.ts'));
2111
+ }
2112
+ }
2101
2113
  if (toCopy.includes('Settings')) {
2102
2114
  const configDir = getScaffoldConfigDir();
2103
2115
  const fontsSrc = join(configDir, 'fonts.ts');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rizzo-css",
3
- "version": "0.0.80",
3
+ "version": "0.0.82",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },
@@ -1,46 +1,148 @@
1
- import type { HTMLAttributes, ReactNode } from 'react';
1
+ import { useEffect, useState } from 'react';
2
+ import type { HTMLAttributes } from 'react';
2
3
 
3
- export interface DocsSidebarLink {
4
+ export interface DocsNavSection {
5
+ id: string;
6
+ label: string;
7
+ }
8
+
9
+ export interface DocsNavLink {
4
10
  href: string;
5
11
  label: string;
6
- active?: boolean;
12
+ frameworkOnly?: boolean;
13
+ absolute?: boolean;
14
+ sections?: DocsNavSection[];
15
+ }
16
+
17
+ export interface DocsNavGroup {
18
+ label: string;
19
+ links: DocsNavLink[];
7
20
  }
8
21
 
9
22
  export interface DocsSidebarProps extends HTMLAttributes<HTMLElement> {
10
- links?: DocsSidebarLink[];
11
- children?: ReactNode;
23
+ /** Current URL pathname (e.g. location.pathname or useLocation().pathname). */
24
+ currentPath: string;
25
+ /** Path prefix for framework-specific links (e.g. /docs). */
26
+ pathPrefix?: string;
27
+ /** When true, omit the aside id to avoid duplicate ids when used inside a demo box. */
28
+ omitId?: boolean;
29
+ /** Nav config (when not provided, sidebar renders minimal placeholder for base template). */
30
+ nav?: DocsNavGroup[];
12
31
  className?: string;
13
32
  }
14
33
 
15
- const DEFAULT_LINKS: DocsSidebarLink[] = [
16
- { href: '/docs', label: 'Overview' },
17
- { href: '/docs/getting-started', label: 'Getting started' },
18
- { href: '/docs/components', label: 'Components', active: true },
19
- ];
34
+ function fullHref(link: { href: string; frameworkOnly?: boolean; absolute?: boolean }, pathPrefix: string): string {
35
+ if (link.absolute && link.href.startsWith('/')) return link.href;
36
+ const base = link.frameworkOnly ? pathPrefix : '/docs';
37
+ return `${base}/${link.href}`;
38
+ }
39
+
40
+ function isActive(link: DocsNavLink, currentPath: string, pathPrefix: string): boolean {
41
+ const path = currentPath.replace(/\/$/, '');
42
+ const href = fullHref(link, pathPrefix);
43
+ return path === href;
44
+ }
20
45
 
21
46
  export function DocsSidebar({
22
- links = DEFAULT_LINKS,
23
- children,
47
+ currentPath,
48
+ pathPrefix = '/docs',
49
+ omitId = false,
50
+ nav = [],
24
51
  className = '',
25
52
  ...rest
26
53
  }: DocsSidebarProps) {
54
+ const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
55
+
56
+ useEffect(() => {
57
+ function setActiveFromHash() {
58
+ setActiveSectionId(window.location.hash.slice(1) || null);
59
+ }
60
+ setActiveFromHash();
61
+ window.addEventListener('hashchange', setActiveFromHash);
62
+
63
+ let io: IntersectionObserver | null = null;
64
+ const content = document.querySelector('.docs__content');
65
+ if (content) {
66
+ const headings = content.querySelectorAll<HTMLHeadingElement>('h2[id]');
67
+ if (headings.length) {
68
+ io = new IntersectionObserver(
69
+ (entries: IntersectionObserverEntry[]) => {
70
+ for (const e of entries) {
71
+ if (!e.isIntersecting) continue;
72
+ const id = (e.target as HTMLElement).id;
73
+ if (id) setActiveSectionId(id);
74
+ break;
75
+ }
76
+ },
77
+ { rootMargin: '-80px 0px -60% 0px', threshold: 0 }
78
+ );
79
+ headings.forEach((h) => io!.observe(h));
80
+ }
81
+ }
82
+ return () => {
83
+ window.removeEventListener('hashchange', setActiveFromHash);
84
+ io?.disconnect();
85
+ };
86
+ }, []);
87
+
88
+ const hasNav = nav && nav.length > 0;
89
+
27
90
  return (
28
- <aside className={`docs-sidebar ${className}`.trim()} {...rest}>
29
- <nav className="docs-sidebar__nav" aria-label="Docs">
30
- <ul className="docs-sidebar__list">
31
- {children ??
32
- links.map((link, i) => (
33
- <li key={i} className="docs-sidebar__item">
34
- <a
35
- href={link.href}
36
- className={`docs-sidebar__link ${link.active ? 'docs-sidebar__link--active' : ''}`.trim()}
37
- >
38
- {link.label}
39
- </a>
40
- </li>
41
- ))}
42
- </ul>
43
- </nav>
91
+ <aside
92
+ id={omitId ? undefined : 'docs-sidebar'}
93
+ className={`docs-sidebar ${className}`.trim()}
94
+ aria-label="Documentation navigation"
95
+ {...rest}
96
+ >
97
+ {hasNav ? (
98
+ <nav className="docs-sidebar__nav">
99
+ {nav.map((group) => (
100
+ <div key={group.label} className="docs-sidebar__group">
101
+ <h2 className="docs-sidebar__group-label">{group.label}</h2>
102
+ <ul className="docs-sidebar__list">
103
+ {group.links.map((link) => {
104
+ const href = fullHref(link, pathPrefix);
105
+ const active = isActive(link, currentPath, pathPrefix);
106
+ const sections = link.sections ?? [];
107
+ return (
108
+ <li key={link.href + link.label} className="docs-sidebar__item">
109
+ <a
110
+ href={href}
111
+ className={`docs-sidebar__link ${active && (sections.length === 0 || activeSectionId == null) ? 'docs-sidebar__link--active' : ''}`.trim()}
112
+ aria-current={active && (sections.length === 0 || activeSectionId == null) ? 'page' : undefined}
113
+ >
114
+ {link.label}
115
+ </a>
116
+ {sections.length > 0 && (
117
+ <ul className="docs-sidebar__sublist" aria-label={`Sections in ${link.label}`}>
118
+ {sections.map((section) => {
119
+ const sublinkActive = activeSectionId === section.id && currentPath.replace(/\/$/, '') === href;
120
+ return (
121
+ <li key={section.id} className="docs-sidebar__subitem">
122
+ <a
123
+ href={`${href}#${section.id}`}
124
+ className={`docs-sidebar__sublink ${sublinkActive ? 'docs-sidebar__sublink--active' : ''}`.trim()}
125
+ aria-current={sublinkActive ? 'location' : undefined}
126
+ >
127
+ {section.label}
128
+ </a>
129
+ </li>
130
+ );
131
+ })}
132
+ </ul>
133
+ )}
134
+ </li>
135
+ );
136
+ })}
137
+ </ul>
138
+ </div>
139
+ ))}
140
+ </nav>
141
+ ) : (
142
+ <div className="docs-sidebar__nav docs-sidebar__nav--placeholder">
143
+ <p className="docs-sidebar__placeholder">Docs sidebar — add <code>nav</code> prop with DOCS_NAV for full nav.</p>
144
+ </div>
145
+ )}
44
146
  </aside>
45
147
  );
46
148
  }
@@ -0,0 +1,57 @@
1
+ import type { HTMLAttributes } from 'react';
2
+
3
+ const THEME_ICON_SVG: Record<string, string> = {
4
+ 'github-dark-classic':
5
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M16 7h.01" /><path d="M3.4 18H12a8 8 0 0 0 8-8V7a4 4 0 0 0-7.28-2.3L2 20" /><path d="m20 7 2 .5-2 .5" /><path d="M10 18v3" /><path d="M14 17.75V21" /><path d="M7 18a6 6 0 0 0 3.84-10.61" /></svg>',
6
+ 'github-light':
7
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" /></svg>',
8
+ 'red-velvet-cupcake':
9
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M20 21v-8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8" /><path d="M4 16s1-1 4-1 5 2 8 2 4-1 4-1V4" /><path d="M2 16v4M22 16v4M8 8h.01M16 8h.01M8 12h.01M16 12h.01" /></svg>',
10
+ 'orangy-one-light':
11
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M15.5 6.5c.5-2.5 2.5-4 5-4 1.5 0 2.5.5 3 1" /><path d="M12 12c-2 2-3 4-3 6 0 3 2 5 5 5 2 0 4-1 6-3" /><path d="M18 12c2 2 3 4 3 6 0 3-2 5-5 5-2 0-4-1-6-3" /></svg>',
12
+ sunflower:
13
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M22 17a10 10 0 0 0-20 0" /><path d="M6 17a6 6 0 0 1 12 0" /><path d="M10 17a2 2 0 0 1 4 0" /></svg>',
14
+ 'shades-of-purple':
15
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 21a9 9 0 0 1 0 -18c4.97 0 9 3.582 9 8c0 1.06 -.474 2.078 -1.318 2.828c-.844 .75 -1.989 1.172 -3.182 1.172h-2.5a2 2 0 0 0 -1 3.75a1.3 1.3 0 0 1 -1 2.25"/><path d="M7.5 10.5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11.5 7.5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M15.5 10.5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/></svg>',
16
+ 'sandstorm-classic':
17
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z" /></svg>',
18
+ 'rocky-blood-orange':
19
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M12 10V2M4.93 10.93l1.41 1.41M2 18h2M20 18h2M17.66 10.93l1.41-1.41M22 22H2M8 6l4-4 4 4M16 18a4 4 0 0 0-8 0" /><path d="M12 22v-4" /></svg>',
20
+ 'minimal-dark-neon-yellow':
21
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" /></svg>',
22
+ 'hack-the-box':
23
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /></svg>',
24
+ 'green-breeze-light':
25
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z" /><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12" /></svg>',
26
+ 'pink-cat-boo':
27
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M19.5 12.572l-7.5 7.428l-7.5 -7.428a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" /></svg>',
28
+ 'cute-pink':
29
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M9 18c0-2 1.5-4 4-4s4 2 4 4c0 2-1.5 4-4 4s-4-2-4-4Z" /><path d="M15 18c0-2 1.5-4 4-4s4 2 4 4c0 2-1.5 4-4 4s-4-2-4-4Z" /><path d="M12 8v4M10 10l-2 2M14 10l2 2" /></svg>',
30
+ 'semi-light-purple':
31
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="m9.06 11.9 8.07-8.06a2.85 2.85 0 1 1 4.03 4.03l-8.06 8.08" /><path d="M7.87 14.14c-.32.32-.67.68-1.06 1.06a5.04 5.04 0 0 1-2.17 1.22c-.47.15-.85.2-1.15.2-.16 0-.3-.02-.41-.03a1 1 0 0 1-.63-.97c-.01-.14.02-.31.07-.51.1-.41.3-.95.6-1.59.3-.64.67-1.33 1.08-2.05" /></svg>',
32
+ };
33
+
34
+ export interface ThemeIconProps extends HTMLAttributes<HTMLSpanElement> {
35
+ themeId: string;
36
+ size?: number;
37
+ }
38
+
39
+ export function ThemeIcon({ themeId, size = 24, className = '', ...rest }: ThemeIconProps) {
40
+ const iconSvg = THEME_ICON_SVG[themeId] ?? null;
41
+ const sizedSvg = iconSvg
42
+ ? iconSvg.replace(/width="16"/, `width="${size}"`).replace(/height="16"/, `height="${size}"`)
43
+ : null;
44
+
45
+ if (!sizedSvg) return null;
46
+
47
+ return (
48
+ <span
49
+ className={className}
50
+ style={{ display: 'inline-flex', verticalAlign: 'middle' }}
51
+ dangerouslySetInnerHTML={{ __html: sizedSvg }}
52
+ {...rest}
53
+ />
54
+ );
55
+ }
56
+
57
+ export default ThemeIcon;
@@ -16,7 +16,7 @@
16
16
  "@types/react": "^18.3.12",
17
17
  "@types/react-dom": "^18.3.1",
18
18
  "@vitejs/plugin-react": "^4.3.3",
19
- "rizzo-css": "^0.0.80",
19
+ "rizzo-css": "^0.0.82",
20
20
  "typescript": "~5.6.2",
21
21
  "vite": "^6.0.1"
22
22
  }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Single source of truth for theme id, label, type, icon, and preview colors.
3
+ * Used by ThemeSwitcher and Navbar so theme icons and labels stay consistent.
4
+ */
5
+
6
+ export type ThemeIconKey =
7
+ | 'gear'
8
+ | 'owl'
9
+ | 'palette'
10
+ | 'flame'
11
+ | 'sunset'
12
+ | 'zap'
13
+ | 'shield'
14
+ | 'heart'
15
+ | 'sun'
16
+ | 'cake'
17
+ | 'lemon'
18
+ | 'rainbow'
19
+ | 'leaf'
20
+ | 'cherry'
21
+ | 'brush';
22
+
23
+ export interface ThemeEntry {
24
+ value: string;
25
+ label: string;
26
+ type: 'dark' | 'light';
27
+ iconKey: ThemeIconKey;
28
+ /** OKLCH background for ThemeSwitcher preview */
29
+ bg: string;
30
+ /** OKLCH accent for ThemeSwitcher preview */
31
+ accent: string;
32
+ }
33
+
34
+ export const THEMES_DARK: ThemeEntry[] = [
35
+ { value: 'github-dark-classic', label: 'GitHub Dark Classic', type: 'dark', iconKey: 'owl', bg: 'oklch(18% 0.012 264deg)', accent: 'oklch(72% 0.12 250deg)' },
36
+ { value: 'shades-of-purple', label: 'Shades of Purple', type: 'dark', iconKey: 'palette', bg: 'oklch(18% 0.08 290deg)', accent: 'oklch(65% 0.25 290deg)' },
37
+ { value: 'sandstorm-classic', label: 'Sandstorm Classic', type: 'dark', iconKey: 'flame', bg: 'oklch(16% 0.025 25deg)', accent: 'oklch(58% 0.18 25deg)' },
38
+ { value: 'rocky-blood-orange', label: 'Rocky Blood Orange', type: 'dark', iconKey: 'sunset', bg: 'oklch(16% 0.03 45deg)', accent: 'oklch(62% 0.16 55deg)' },
39
+ { value: 'minimal-dark-neon-yellow', label: 'Minimal Dark Neon Yellow', type: 'dark', iconKey: 'zap', bg: 'oklch(14% 0.01 95deg)', accent: 'oklch(88% 0.18 95deg)' },
40
+ { value: 'hack-the-box', label: 'Hack The Box', type: 'dark', iconKey: 'shield', bg: 'oklch(16% 0.03 255deg)', accent: 'oklch(88% 0.22 130deg)' },
41
+ { value: 'pink-cat-boo', label: 'Pink Cat Boo', type: 'dark', iconKey: 'heart', bg: 'oklch(18% 0.03 280deg)', accent: 'oklch(78% 0.12 350deg)' },
42
+ ];
43
+
44
+ export const THEMES_LIGHT: ThemeEntry[] = [
45
+ { value: 'github-light', label: 'GitHub Light', type: 'light', iconKey: 'sun', bg: 'oklch(100% 0 0deg)', accent: 'oklch(55% 0.18 255deg)' },
46
+ { value: 'red-velvet-cupcake', label: 'Red Velvet Cupcake', type: 'light', iconKey: 'cake', bg: 'oklch(99% 0.005 25deg)', accent: 'oklch(55% 0.17 25deg)' },
47
+ { value: 'orangy-one-light', label: 'Orangy One Light', type: 'light', iconKey: 'lemon', bg: 'oklch(99% 0.008 70deg)', accent: 'oklch(58% 0.16 55deg)' },
48
+ { value: 'sunflower', label: 'Sunflower', type: 'light', iconKey: 'rainbow', bg: 'oklch(98% 0.03 95deg)', accent: 'oklch(75% 0.16 95deg)' },
49
+ { value: 'green-breeze-light', label: 'Green Breeze Light', type: 'light', iconKey: 'leaf', bg: 'oklch(98% 0.008 140deg)', accent: 'oklch(48% 0.16 155deg)' },
50
+ { value: 'cute-pink', label: 'Cute Pink', type: 'light', iconKey: 'cherry', bg: 'oklch(98% 0.025 350deg)', accent: 'oklch(62% 0.22 350deg)' },
51
+ { value: 'semi-light-purple', label: 'Semi Light Purple', type: 'light', iconKey: 'brush', bg: 'oklch(96% 0.02 290deg)', accent: 'oklch(52% 0.2 290deg)' },
52
+ ];
53
+
54
+ export const ALL_THEMES = [...THEMES_DARK, ...THEMES_LIGHT];
@@ -0,0 +1,35 @@
1
+ # Rizzo CSS — React Full Template
2
+
3
+ This project was generated with the **Full** template (React + React Router). It mirrors the structure of the Rizzo docs site: home, docs (overview, getting started, components), blocks, and themes.
4
+
5
+ ## Project structure
6
+
7
+ - **Home** (`/`) — Hero, features, add-command, doc cards.
8
+ - **Docs** (`/docs`, `/docs/overview`, `/docs/getting-started`, `/docs/components`) — Docs layout with sidebar (Introduction, Components, Blocks, Themes). Uses `DocsSidebar` with `DOCS_NAV` from `src/config/docsNav.ts`.
9
+ - **Blocks** (`/blocks`) — Blocks layout with sidebar and index of block links (main site).
10
+ - **Themes** (`/themes`) — Theme picker with ThemeSwitcher, Dark/Light theme cards (Card + ThemeIcon), and code snippet.
11
+
12
+ Config used by the full template:
13
+
14
+ - `src/config/docsNav.ts` — Sidebar nav (matches main site structure).
15
+ - `src/config/componentCategories.ts` — Component categories for the components page.
16
+
17
+ ## Scripts
18
+
19
+ - `pnpm dev` — Start dev server (Vite).
20
+ - `pnpm build` — Build for production.
21
+ - `pnpm preview` — Preview production build.
22
+
23
+ ## Adding components
24
+
25
+ From the project root:
26
+
27
+ ```bash
28
+ npx rizzo-css add <ComponentName>
29
+ ```
30
+
31
+ Example: `npx rizzo-css add Modal` adds the Modal component to `src/components/rizzo`.
32
+
33
+ ## Settings panel
34
+
35
+ The Settings panel (gear in the navbar) includes Theme (ThemeSwitcher), Font Size, Font (FontSwitcher), Sound (SoundEffects), and Accessibility options. Preferences are stored in `localStorage`.
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^18.3.1",
13
+ "react-dom": "^18.3.1",
14
+ "react-router-dom": "^6.28.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^18.3.12",
18
+ "@types/react-dom": "^18.3.1",
19
+ "@vitejs/plugin-react": "^4.3.3",
20
+ "rizzo-css": "^0.0.82",
21
+ "typescript": "~5.6.2",
22
+ "vite": "^6.0.1"
23
+ }
24
+ }
@@ -0,0 +1,41 @@
1
+ import { Routes, Route } from 'react-router-dom';
2
+ import Navbar from '@/components/rizzo/Navbar';
3
+ import Footer from '@/components/rizzo/Footer';
4
+ import Settings from '@/components/rizzo/Settings';
5
+ import BackToTop from '@/components/rizzo/BackToTop';
6
+ import Home from './views/Home';
7
+ import BlocksLayout from './layouts/BlocksLayout';
8
+ import BlocksIndex from './views/BlocksIndex';
9
+ import DocsLayout from './layouts/DocsLayout';
10
+ import DocsIndex from './views/DocsIndex';
11
+ import DocsOverview from './views/DocsOverview';
12
+ import DocsGettingStarted from './views/DocsGettingStarted';
13
+ import DocsComponents from './views/DocsComponents';
14
+ import Themes from './views/Themes';
15
+
16
+ export default function App() {
17
+ return (
18
+ <>
19
+ <a href="#main-content" className="skip-link">Skip to main content</a>
20
+ <Navbar siteName="Rizzo CSS" />
21
+ <main id="main-content">
22
+ <Routes>
23
+ <Route path="/" element={<Home />} />
24
+ <Route path="/blocks" element={<BlocksLayout />}>
25
+ <Route index element={<BlocksIndex />} />
26
+ </Route>
27
+ <Route path="/docs" element={<DocsLayout />}>
28
+ <Route index element={<DocsIndex />} />
29
+ <Route path="overview" element={<DocsOverview />} />
30
+ <Route path="getting-started" element={<DocsGettingStarted />} />
31
+ <Route path="components" element={<DocsComponents />} />
32
+ </Route>
33
+ <Route path="/themes" element={<Themes />} />
34
+ </Routes>
35
+ </main>
36
+ <Footer />
37
+ <BackToTop />
38
+ <Settings />
39
+ </>
40
+ );
41
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Minimal component categories for the full template components page.
3
+ * Same structure as main site for consistent layout.
4
+ */
5
+ export interface CategoryItem {
6
+ href: string;
7
+ title: string;
8
+ description: string;
9
+ }
10
+
11
+ export interface CategoryWithItems {
12
+ id: string;
13
+ label: string;
14
+ items: CategoryItem[];
15
+ }
16
+
17
+ function slugToTitle(slug: string): string {
18
+ return slug
19
+ .split('-')
20
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
21
+ .join(' ');
22
+ }
23
+
24
+ const CATEGORIES: { id: string; label: string; slugs: string[] }[] = [
25
+ { id: 'layout', label: 'Layout', slugs: ['navbar', 'docs-sidebar', 'dashboard', 'resizable', 'footer'] },
26
+ { id: 'forms', label: 'Forms & inputs', slugs: ['button', 'button-group', 'forms', 'switch', 'slider', 'toggle', 'toggle-group', 'divider', 'separator', 'label', 'kbd'] },
27
+ { id: 'data', label: 'Data display', slugs: ['cards', 'table', 'badge', 'pagination', 'aspect-ratio', 'empty', 'scroll-area', 'alert', 'skeleton', 'spinner', 'progress-bar', 'toast'] },
28
+ { id: 'overlay', label: 'Overlay', slugs: ['modal', 'sheet', 'popover', 'dropdown', 'tooltip', 'accordion', 'collapsible', 'tabs'] },
29
+ { id: 'other', label: 'Other', slugs: ['avatar', 'copy-to-clipboard', 'theme-switcher', 'font-switcher', 'settings', 'search', 'breadcrumb', 'back-to-top'] },
30
+ ];
31
+
32
+ export function getComponentsByCategory(): CategoryWithItems[] {
33
+ const pathPrefix = '/docs/components';
34
+ return CATEGORIES.map((cat) => ({
35
+ id: cat.id,
36
+ label: cat.label,
37
+ items: cat.slugs.map((slug) => ({
38
+ href: `${pathPrefix}/${slug}`,
39
+ title: slugToTitle(slug),
40
+ description: `Accessible, themeable ${slugToTitle(slug).toLowerCase()} component.`,
41
+ })),
42
+ }));
43
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Docs nav for the full template. Matches main site structure (Introduction, Components, Blocks, Themes).
3
+ */
4
+ export interface DocsNavSection {
5
+ id: string;
6
+ label: string;
7
+ }
8
+
9
+ export interface DocsNavLink {
10
+ href: string;
11
+ label: string;
12
+ frameworkOnly?: boolean;
13
+ absolute?: boolean;
14
+ sections?: DocsNavSection[];
15
+ }
16
+
17
+ export interface DocsNavGroup {
18
+ label: string;
19
+ links: DocsNavLink[];
20
+ }
21
+
22
+ export const DOCS_NAV: DocsNavGroup[] = [
23
+ {
24
+ label: 'Introduction',
25
+ links: [
26
+ {
27
+ href: 'overview',
28
+ label: 'Overview',
29
+ frameworkOnly: true,
30
+ sections: [
31
+ { id: 'overview', label: 'Overview' },
32
+ { id: 'installation', label: 'Installation' },
33
+ { id: 'components', label: 'Components' },
34
+ { id: 'blocks', label: 'Blocks' },
35
+ ],
36
+ },
37
+ {
38
+ href: 'getting-started',
39
+ label: 'Getting Started',
40
+ sections: [
41
+ { id: 'installation', label: 'Installation' },
42
+ { id: 'project-structure', label: 'Project Structure' },
43
+ { id: 'development', label: 'Development' },
44
+ { id: 'using-components', label: 'Using Components' },
45
+ { id: 'using-themes', label: 'Using Themes' },
46
+ ],
47
+ },
48
+ ],
49
+ },
50
+ {
51
+ label: 'Components',
52
+ links: [{ href: 'components', label: 'Overview', frameworkOnly: true }],
53
+ },
54
+ {
55
+ label: 'Blocks',
56
+ links: [
57
+ { href: '/blocks', label: 'Blocks overview', absolute: true },
58
+ { href: '/blocks/landing-hero', label: 'Landing hero', absolute: true },
59
+ { href: '/blocks/pricing', label: 'Pricing cards', absolute: true },
60
+ { href: '/blocks/dashboard-01', label: 'Dashboard', absolute: true },
61
+ { href: '/blocks/docs-layout', label: 'Docs layout', absolute: true },
62
+ { href: '/blocks/login', label: 'Login', absolute: true },
63
+ { href: '/blocks/signup', label: 'Sign up', absolute: true },
64
+ ],
65
+ },
66
+ {
67
+ label: 'Themes',
68
+ links: [{ href: '/themes', label: 'Themes', absolute: true }],
69
+ },
70
+ ];