rizzo-css 0.0.62 → 0.0.64

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 (195) hide show
  1. package/README.md +9 -5
  2. package/bin/rizzo-css.js +350 -31
  3. package/dist/rizzo.min.css +5 -3
  4. package/package.json +14 -7
  5. package/scaffold/astro/Footer.astro +8 -0
  6. package/scaffold/astro/Settings.astro +8 -2
  7. package/scaffold/astro/Tabs.astro +2 -2
  8. package/scaffold/react/Accordion.tsx +143 -0
  9. package/scaffold/react/Alert.tsx +90 -0
  10. package/scaffold/react/AlertDialog.tsx +80 -0
  11. package/scaffold/react/AspectRatio.tsx +32 -0
  12. package/scaffold/react/Avatar.tsx +53 -0
  13. package/scaffold/react/BackToTop.tsx +62 -0
  14. package/scaffold/react/Badge.tsx +39 -0
  15. package/scaffold/react/Breadcrumb.tsx +81 -0
  16. package/scaffold/react/Button.tsx +40 -0
  17. package/scaffold/react/ButtonGroup.tsx +24 -0
  18. package/scaffold/react/Card.tsx +26 -0
  19. package/scaffold/react/Checkbox.tsx +40 -0
  20. package/scaffold/react/Collapsible.tsx +58 -0
  21. package/scaffold/react/ContextMenu.tsx +67 -0
  22. package/scaffold/react/CopyToClipboard.tsx +128 -0
  23. package/scaffold/react/Dashboard.tsx +23 -0
  24. package/scaffold/react/Divider.tsx +47 -0
  25. package/scaffold/react/DocsSidebar.tsx +48 -0
  26. package/scaffold/react/Dropdown.tsx +256 -0
  27. package/scaffold/react/Empty.tsx +29 -0
  28. package/scaffold/react/FontSwitcher.tsx +68 -0
  29. package/scaffold/react/Footer.tsx +55 -0
  30. package/scaffold/react/FormGroup.tsx +57 -0
  31. package/scaffold/react/HoverCard.tsx +61 -0
  32. package/scaffold/react/Icons.tsx +22 -0
  33. package/scaffold/react/Input.tsx +69 -0
  34. package/scaffold/react/Kbd.tsx +16 -0
  35. package/scaffold/react/Label.tsx +16 -0
  36. package/scaffold/react/Modal.tsx +149 -0
  37. package/scaffold/react/Navbar.tsx +72 -0
  38. package/scaffold/react/Pagination.tsx +155 -0
  39. package/scaffold/react/Popover.tsx +66 -0
  40. package/scaffold/react/ProgressBar.tsx +66 -0
  41. package/scaffold/react/Radio.tsx +38 -0
  42. package/scaffold/react/ResizableHandle.tsx +24 -0
  43. package/scaffold/react/ResizablePane.tsx +29 -0
  44. package/scaffold/react/ResizablePaneGroup.tsx +29 -0
  45. package/scaffold/react/ScrollArea.tsx +29 -0
  46. package/scaffold/react/Search.tsx +62 -0
  47. package/scaffold/react/Select.tsx +65 -0
  48. package/scaffold/react/Separator.tsx +33 -0
  49. package/scaffold/react/Settings.tsx +60 -0
  50. package/scaffold/react/Sheet.tsx +86 -0
  51. package/scaffold/react/Skeleton.tsx +32 -0
  52. package/scaffold/react/Slider.tsx +66 -0
  53. package/scaffold/react/SoundEffects.tsx +15 -0
  54. package/scaffold/react/Spinner.tsx +36 -0
  55. package/scaffold/react/Switch.tsx +52 -0
  56. package/scaffold/react/Table.tsx +178 -0
  57. package/scaffold/react/Tabs.tsx +143 -0
  58. package/scaffold/react/Textarea.tsx +69 -0
  59. package/scaffold/react/ThemeSwitcher.tsx +89 -0
  60. package/scaffold/react/Toast.tsx +43 -0
  61. package/scaffold/react/Toggle.tsx +45 -0
  62. package/scaffold/react/ToggleGroup.tsx +34 -0
  63. package/scaffold/react/Tooltip.tsx +40 -0
  64. package/scaffold/react/base/README-RIZZO.md +50 -0
  65. package/scaffold/react/base/gitignore +8 -0
  66. package/scaffold/react/base/index.html +16 -0
  67. package/scaffold/react/base/package.json +22 -0
  68. package/scaffold/react/base/public/.gitkeep +0 -0
  69. package/scaffold/react/base/src/App.tsx +11 -0
  70. package/scaffold/react/base/src/index.css +1 -0
  71. package/scaffold/react/base/src/main.tsx +10 -0
  72. package/scaffold/react/base/src/vite-env.d.ts +1 -0
  73. package/scaffold/react/base/tsconfig.json +21 -0
  74. package/scaffold/react/base/tsconfig.node.json +9 -0
  75. package/scaffold/react/base/vite.config.ts +6 -0
  76. package/scaffold/vanilla/README-RIZZO.md +1 -1
  77. package/scaffold/vanilla/components/accordion.html +36 -0
  78. package/scaffold/vanilla/components/alert-dialog.html +36 -0
  79. package/scaffold/vanilla/components/alert.html +36 -0
  80. package/scaffold/vanilla/components/aspect-ratio.html +36 -0
  81. package/scaffold/vanilla/components/avatar.html +36 -0
  82. package/scaffold/vanilla/components/back-to-top.html +36 -0
  83. package/scaffold/vanilla/components/badge.html +36 -0
  84. package/scaffold/vanilla/components/breadcrumb.html +36 -0
  85. package/scaffold/vanilla/components/button-group.html +36 -0
  86. package/scaffold/vanilla/components/button.html +36 -0
  87. package/scaffold/vanilla/components/cards.html +36 -0
  88. package/scaffold/vanilla/components/collapsible.html +36 -0
  89. package/scaffold/vanilla/components/context-menu.html +36 -0
  90. package/scaffold/vanilla/components/copy-to-clipboard.html +36 -0
  91. package/scaffold/vanilla/components/dashboard.html +36 -0
  92. package/scaffold/vanilla/components/divider.html +36 -0
  93. package/scaffold/vanilla/components/docs-sidebar.html +36 -0
  94. package/scaffold/vanilla/components/dropdown.html +36 -0
  95. package/scaffold/vanilla/components/empty.html +36 -0
  96. package/scaffold/vanilla/components/font-switcher.html +36 -0
  97. package/scaffold/vanilla/components/footer.html +36 -0
  98. package/scaffold/vanilla/components/forms.html +36 -0
  99. package/scaffold/vanilla/components/hover-card.html +36 -0
  100. package/scaffold/vanilla/components/icons.html +36 -0
  101. package/scaffold/vanilla/components/index.html +36 -0
  102. package/scaffold/vanilla/components/kbd.html +36 -0
  103. package/scaffold/vanilla/components/label.html +36 -0
  104. package/scaffold/vanilla/components/modal.html +36 -0
  105. package/scaffold/vanilla/components/navbar.html +36 -0
  106. package/scaffold/vanilla/components/pagination.html +36 -0
  107. package/scaffold/vanilla/components/popover.html +36 -0
  108. package/scaffold/vanilla/components/progress-bar.html +36 -0
  109. package/scaffold/vanilla/components/resizable.html +36 -0
  110. package/scaffold/vanilla/components/scroll-area.html +36 -0
  111. package/scaffold/vanilla/components/search.html +36 -0
  112. package/scaffold/vanilla/components/separator.html +36 -0
  113. package/scaffold/vanilla/components/settings.html +36 -0
  114. package/scaffold/vanilla/components/sheet.html +36 -0
  115. package/scaffold/vanilla/components/skeleton.html +36 -0
  116. package/scaffold/vanilla/components/slider.html +36 -0
  117. package/scaffold/vanilla/components/sound-effects.html +36 -0
  118. package/scaffold/vanilla/components/spinner.html +36 -0
  119. package/scaffold/vanilla/components/switch.html +36 -0
  120. package/scaffold/vanilla/components/table.html +36 -0
  121. package/scaffold/vanilla/components/tabs.html +36 -0
  122. package/scaffold/vanilla/components/theme-switcher.html +36 -0
  123. package/scaffold/vanilla/components/toast.html +36 -0
  124. package/scaffold/vanilla/components/toggle-group.html +36 -0
  125. package/scaffold/vanilla/components/toggle.html +36 -0
  126. package/scaffold/vanilla/components/tooltip.html +36 -0
  127. package/scaffold/vanilla/index.html +36 -0
  128. package/scaffold/vue/Accordion.vue +9 -0
  129. package/scaffold/vue/Alert.vue +9 -0
  130. package/scaffold/vue/AlertDialog.vue +9 -0
  131. package/scaffold/vue/AspectRatio.vue +9 -0
  132. package/scaffold/vue/Avatar.vue +9 -0
  133. package/scaffold/vue/BackToTop.vue +9 -0
  134. package/scaffold/vue/Badge.vue +28 -0
  135. package/scaffold/vue/Breadcrumb.vue +9 -0
  136. package/scaffold/vue/Button.vue +23 -0
  137. package/scaffold/vue/ButtonGroup.vue +9 -0
  138. package/scaffold/vue/Card.vue +21 -0
  139. package/scaffold/vue/Checkbox.vue +31 -0
  140. package/scaffold/vue/Collapsible.vue +9 -0
  141. package/scaffold/vue/ContextMenu.vue +9 -0
  142. package/scaffold/vue/CopyToClipboard.vue +9 -0
  143. package/scaffold/vue/Dashboard.vue +9 -0
  144. package/scaffold/vue/Divider.vue +23 -0
  145. package/scaffold/vue/DocsSidebar.vue +9 -0
  146. package/scaffold/vue/Dropdown.vue +9 -0
  147. package/scaffold/vue/Empty.vue +9 -0
  148. package/scaffold/vue/FontSwitcher.vue +9 -0
  149. package/scaffold/vue/Footer.vue +9 -0
  150. package/scaffold/vue/FormGroup.vue +45 -0
  151. package/scaffold/vue/HoverCard.vue +9 -0
  152. package/scaffold/vue/Icons.vue +9 -0
  153. package/scaffold/vue/Input.vue +59 -0
  154. package/scaffold/vue/Kbd.vue +9 -0
  155. package/scaffold/vue/Label.vue +23 -0
  156. package/scaffold/vue/Modal.vue +9 -0
  157. package/scaffold/vue/Navbar.vue +9 -0
  158. package/scaffold/vue/Pagination.vue +9 -0
  159. package/scaffold/vue/Popover.vue +9 -0
  160. package/scaffold/vue/ProgressBar.vue +9 -0
  161. package/scaffold/vue/Radio.vue +29 -0
  162. package/scaffold/vue/ResizableHandle.vue +9 -0
  163. package/scaffold/vue/ResizablePane.vue +9 -0
  164. package/scaffold/vue/ResizablePaneGroup.vue +9 -0
  165. package/scaffold/vue/ScrollArea.vue +9 -0
  166. package/scaffold/vue/Search.vue +9 -0
  167. package/scaffold/vue/Select.vue +52 -0
  168. package/scaffold/vue/Separator.vue +9 -0
  169. package/scaffold/vue/Settings.vue +9 -0
  170. package/scaffold/vue/Sheet.vue +9 -0
  171. package/scaffold/vue/Skeleton.vue +9 -0
  172. package/scaffold/vue/Slider.vue +9 -0
  173. package/scaffold/vue/SoundEffects.vue +9 -0
  174. package/scaffold/vue/Spinner.vue +21 -0
  175. package/scaffold/vue/Switch.vue +9 -0
  176. package/scaffold/vue/Table.vue +9 -0
  177. package/scaffold/vue/Tabs.vue +9 -0
  178. package/scaffold/vue/Textarea.vue +60 -0
  179. package/scaffold/vue/ThemeSwitcher.vue +9 -0
  180. package/scaffold/vue/Toast.vue +9 -0
  181. package/scaffold/vue/Toggle.vue +9 -0
  182. package/scaffold/vue/ToggleGroup.vue +9 -0
  183. package/scaffold/vue/Tooltip.vue +9 -0
  184. package/scaffold/vue/base/README-RIZZO.md +50 -0
  185. package/scaffold/vue/base/gitignore +8 -0
  186. package/scaffold/vue/base/index.html +16 -0
  187. package/scaffold/vue/base/package.json +20 -0
  188. package/scaffold/vue/base/public/.gitkeep +0 -0
  189. package/scaffold/vue/base/src/App.vue +13 -0
  190. package/scaffold/vue/base/src/index.css +1 -0
  191. package/scaffold/vue/base/src/main.ts +5 -0
  192. package/scaffold/vue/base/src/vite-env.d.ts +7 -0
  193. package/scaffold/vue/base/tsconfig.json +22 -0
  194. package/scaffold/vue/base/tsconfig.node.json +10 -0
  195. package/scaffold/vue/base/vite.config.ts +6 -0
@@ -0,0 +1,143 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ import { useState, useCallback, useRef } from 'react';
3
+
4
+ export interface Tab {
5
+ id: string;
6
+ label: string;
7
+ icon?: string;
8
+ content?: string;
9
+ }
10
+
11
+ export interface TabsProps extends HTMLAttributes<HTMLDivElement> {
12
+ tabs: Tab[];
13
+ id?: string;
14
+ defaultTab?: string;
15
+ variant?: 'default' | 'pills' | 'underline';
16
+ children?: (activeTabId: string) => ReactNode;
17
+ className?: string;
18
+ }
19
+
20
+ export function Tabs({
21
+ tabs,
22
+ id: idProp,
23
+ defaultTab,
24
+ variant = 'default',
25
+ children,
26
+ className = '',
27
+ ...rest
28
+ }: TabsProps) {
29
+ const defaultActiveId = defaultTab ?? tabs[0]?.id ?? '';
30
+ const [selectedId, setSelectedId] = useState<string | null>(null);
31
+ const activeTabId = selectedId ?? defaultActiveId;
32
+ const tabsId = idProp ?? `tabs-${Math.random().toString(36).slice(2, 11)}`;
33
+ const variantClass = variant !== 'default' ? `tabs--${variant}` : '';
34
+ const classes = ['tabs', variantClass, className].filter(Boolean).join(' ').trim();
35
+ const tabListRef = useRef<HTMLDivElement>(null);
36
+
37
+ const activateTab = useCallback(
38
+ (index: number) => {
39
+ if (index < 0 || index >= tabs.length) return;
40
+ setSelectedId(tabs[index].id);
41
+ },
42
+ [tabs]
43
+ );
44
+
45
+ const handleKeydown = useCallback(
46
+ (e: React.KeyboardEvent, index: number) => {
47
+ let targetIndex = index;
48
+ switch (e.key) {
49
+ case 'ArrowRight':
50
+ case 'ArrowDown':
51
+ e.preventDefault();
52
+ targetIndex = (index + 1) % tabs.length;
53
+ break;
54
+ case 'ArrowLeft':
55
+ case 'ArrowUp':
56
+ e.preventDefault();
57
+ targetIndex = index === 0 ? tabs.length - 1 : index - 1;
58
+ break;
59
+ case 'Home':
60
+ e.preventDefault();
61
+ targetIndex = 0;
62
+ break;
63
+ case 'End':
64
+ e.preventDefault();
65
+ targetIndex = tabs.length - 1;
66
+ break;
67
+ case 'Enter':
68
+ case ' ':
69
+ e.preventDefault();
70
+ activateTab(index);
71
+ return;
72
+ default:
73
+ return;
74
+ }
75
+ activateTab(targetIndex);
76
+ const buttons = tabListRef.current?.querySelectorAll('[role="tab"]');
77
+ if (buttons && buttons[targetIndex]) (buttons[targetIndex] as HTMLElement).focus();
78
+ },
79
+ [tabs.length, activateTab]
80
+ );
81
+
82
+ return (
83
+ <div className={classes} data-tabs={tabsId} {...rest}>
84
+ <div className="tabs__list" role="tablist" aria-label="Tabs" ref={tabListRef}>
85
+ {tabs.map((tab, index) => {
86
+ const isActive = tab.id === activeTabId;
87
+ return (
88
+ <span
89
+ key={tab.id}
90
+ className={`tabs__tab ${isActive ? 'tabs__tab--active' : ''}`.trim()}
91
+ id={`${tabsId}-tab-${tab.id}`}
92
+ role="tab"
93
+ tabIndex={isActive ? 0 : -1}
94
+ aria-selected={isActive ? 'true' : 'false'}
95
+ aria-controls={`${tabsId}-panel-${tab.id}`}
96
+ data-tab-id={tab.id}
97
+ data-tab-index={index}
98
+ onClick={() => activateTab(index)}
99
+ onKeyDown={(e) => handleKeydown(e, index)}
100
+ >
101
+ {tab.icon && (
102
+ <img
103
+ src={tab.icon}
104
+ alt=""
105
+ className="tabs__tab-icon"
106
+ width={20}
107
+ height={20}
108
+ loading="lazy"
109
+ aria-hidden="true"
110
+ />
111
+ )}
112
+ {tab.label}
113
+ </span>
114
+ );
115
+ })}
116
+ </div>
117
+ <div className="tabs__panels-wrapper">
118
+ {tabs.map((tab) => {
119
+ const isActive = tab.id === activeTabId;
120
+ return (
121
+ <div
122
+ key={tab.id}
123
+ className={`tabs__panel ${isActive ? 'tabs__panel--active' : ''}`.trim()}
124
+ id={`${tabsId}-panel-${tab.id}`}
125
+ role="tabpanel"
126
+ aria-labelledby={`${tabsId}-tab-${tab.id}`}
127
+ aria-hidden={isActive ? 'false' : 'true'}
128
+ data-panel-id={tab.id}
129
+ >
130
+ {tab.content ? (
131
+ <div className="tabs__panel-content" dangerouslySetInnerHTML={{ __html: tab.content }} />
132
+ ) : isActive && children ? (
133
+ children(activeTabId)
134
+ ) : null}
135
+ </div>
136
+ );
137
+ })}
138
+ </div>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ export default Tabs;
@@ -0,0 +1,69 @@
1
+ import type { TextareaHTMLAttributes } from 'react';
2
+
3
+ export type TextareaSize = 'sm' | 'md' | 'lg';
4
+
5
+ export interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> {
6
+ size?: TextareaSize;
7
+ error?: boolean;
8
+ success?: boolean;
9
+ ariaDescribedby?: string;
10
+ ariaInvalid?: boolean | 'true' | 'false';
11
+ onValueChange?: (value: string) => void;
12
+ className?: string;
13
+ }
14
+
15
+ export function Textarea({
16
+ id,
17
+ name,
18
+ value = '',
19
+ placeholder,
20
+ required = false,
21
+ disabled = false,
22
+ readOnly = false,
23
+ rows = 4,
24
+ cols,
25
+ size = 'md',
26
+ error = false,
27
+ success = false,
28
+ className = '',
29
+ ariaDescribedby,
30
+ ariaInvalid,
31
+ onChange,
32
+ onValueChange,
33
+ ...rest
34
+ }: TextareaProps) {
35
+ const sizeClass = size !== 'md' ? `form-input--${size}` : '';
36
+ const errorClass = error ? 'form-input--error' : '';
37
+ const successClass = success ? 'form-input--success' : '';
38
+ const classes = ['form-input', sizeClass, errorClass, successClass, className]
39
+ .filter(Boolean)
40
+ .join(' ')
41
+ .trim();
42
+ const invalid = error || ariaInvalid === true || ariaInvalid === 'true';
43
+
44
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
45
+ onChange?.(e);
46
+ onValueChange?.(e.target.value);
47
+ };
48
+
49
+ return (
50
+ <textarea
51
+ id={id}
52
+ name={name}
53
+ value={value}
54
+ placeholder={placeholder}
55
+ required={required}
56
+ disabled={disabled}
57
+ readOnly={readOnly}
58
+ rows={rows}
59
+ cols={cols}
60
+ className={classes}
61
+ aria-invalid={invalid ? 'true' : 'false'}
62
+ aria-describedby={ariaDescribedby}
63
+ onChange={handleChange}
64
+ {...rest}
65
+ />
66
+ );
67
+ }
68
+
69
+ export default Textarea;
@@ -0,0 +1,89 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ import { useState, useEffect } from 'react';
3
+
4
+ export interface ThemeOption {
5
+ value: string;
6
+ label: string;
7
+ }
8
+
9
+ export interface ThemeSwitcherProps extends HTMLAttributes<HTMLDivElement> {
10
+ idPrefix?: string;
11
+ themes?: ThemeOption[];
12
+ className?: string;
13
+ }
14
+
15
+ const DEFAULT_THEMES: ThemeOption[] = [
16
+ { value: 'github-dark-classic', label: 'GitHub Dark' },
17
+ { value: 'github-light', label: 'GitHub Light' },
18
+ { value: 'system', label: 'System' },
19
+ ];
20
+
21
+ export function ThemeSwitcher({
22
+ idPrefix = '',
23
+ themes = DEFAULT_THEMES,
24
+ className = '',
25
+ ...rest
26
+ }: ThemeSwitcherProps) {
27
+ const [open, setOpen] = useState(false);
28
+ const [current, setCurrent] = useState(themes[0]?.value ?? 'system');
29
+
30
+ useEffect(() => {
31
+ const stored = typeof localStorage !== 'undefined' ? localStorage.getItem('theme') : null;
32
+ if (stored) setCurrent(stored);
33
+ }, []);
34
+
35
+ const menuId = idPrefix ? `theme-switcher-${idPrefix}-menu` : 'theme-switcher-menu';
36
+ const triggerId = idPrefix ? `theme-switcher-${idPrefix}-trigger` : 'theme-switcher-trigger';
37
+
38
+ const apply = (value: string) => {
39
+ if (value === 'system') {
40
+ document.documentElement.removeAttribute('data-theme');
41
+ localStorage.removeItem('theme');
42
+ } else {
43
+ document.documentElement.setAttribute('data-theme', value);
44
+ localStorage.setItem('theme', value);
45
+ }
46
+ setCurrent(value);
47
+ setOpen(false);
48
+ };
49
+
50
+ return (
51
+ <div className={`theme-switcher ${className}`.trim()} data-theme-switcher {...rest}>
52
+ <button
53
+ type="button"
54
+ id={triggerId}
55
+ className="theme-switcher__trigger"
56
+ aria-expanded={open}
57
+ aria-haspopup="true"
58
+ aria-controls={menuId}
59
+ aria-label="Theme"
60
+ onClick={() => setOpen((o) => !o)}
61
+ >
62
+ <span className="theme-switcher__trigger-icon" aria-hidden="true">◐</span>
63
+ <span className="theme-switcher__trigger-label">{themes.find((t) => t.value === current)?.label ?? 'Theme'}</span>
64
+ </button>
65
+ <div
66
+ className={`theme-switcher__menu ${open ? 'theme-switcher__menu--open' : ''}`.trim()}
67
+ id={menuId}
68
+ role="menu"
69
+ aria-labelledby={triggerId}
70
+ aria-hidden={!open}
71
+ hidden={!open}
72
+ >
73
+ {themes.map((t) => (
74
+ <button
75
+ key={t.value}
76
+ type="button"
77
+ role="menuitem"
78
+ className={`theme-switcher__item ${current === t.value ? 'theme-switcher__item--active' : ''}`.trim()}
79
+ onClick={() => apply(t.value)}
80
+ >
81
+ {t.label}
82
+ </button>
83
+ ))}
84
+ </div>
85
+ </div>
86
+ );
87
+ }
88
+
89
+ export default ThemeSwitcher;
@@ -0,0 +1,43 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ import { Alert } from './Alert';
3
+
4
+ export type ToastPosition =
5
+ | 'top-right'
6
+ | 'top-left'
7
+ | 'bottom-right'
8
+ | 'bottom-left'
9
+ | 'top-center'
10
+ | 'bottom-center';
11
+
12
+ export interface ToastProps extends HTMLAttributes<HTMLDivElement> {
13
+ variant?: 'success' | 'error' | 'warning' | 'info';
14
+ dismissible?: boolean;
15
+ autoDismiss?: number;
16
+ position?: ToastPosition;
17
+ children?: ReactNode;
18
+ className?: string;
19
+ id?: string;
20
+ }
21
+
22
+ export function Toast({
23
+ variant = 'info',
24
+ dismissible = true,
25
+ autoDismiss = 5000,
26
+ position = 'top-right',
27
+ children,
28
+ className = '',
29
+ id,
30
+ ...rest
31
+ }: ToastProps) {
32
+ const positionClass = `toast--${position}`;
33
+ const classes = ['toast', positionClass, className].filter(Boolean).join(' ').trim();
34
+ return (
35
+ <div className={classes} data-toast-container {...rest}>
36
+ <Alert variant={variant} dismissible={dismissible} autoDismiss={autoDismiss} id={id}>
37
+ {children}
38
+ </Alert>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ export default Toast;
@@ -0,0 +1,45 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+
3
+ export interface ToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
4
+ pressed?: boolean;
5
+ onPressedChange?: (pressed: boolean) => void;
6
+ children?: ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ export function Toggle({
11
+ pressed = false,
12
+ onPressedChange,
13
+ disabled = false,
14
+ type = 'button',
15
+ value,
16
+ 'aria-label': ariaLabel,
17
+ className = '',
18
+ children,
19
+ onClick,
20
+ ...rest
21
+ }: ToggleProps) {
22
+ const pressedClass = pressed ? 'toggle--pressed' : '';
23
+ const classes = ['toggle', pressedClass, className].filter(Boolean).join(' ').trim();
24
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
25
+ onClick?.(e);
26
+ if (!disabled) onPressedChange?.(!pressed);
27
+ };
28
+ return (
29
+ <button
30
+ type={type}
31
+ className={classes}
32
+ aria-pressed={pressed}
33
+ disabled={disabled}
34
+ value={value}
35
+ aria-label={ariaLabel}
36
+ data-toggle
37
+ onClick={handleClick}
38
+ {...rest}
39
+ >
40
+ {children}
41
+ </button>
42
+ );
43
+ }
44
+
45
+ export default Toggle;
@@ -0,0 +1,34 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+
3
+ export interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {
4
+ type?: 'single' | 'multiple';
5
+ orientation?: 'horizontal' | 'vertical';
6
+ children?: ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ export function ToggleGroup({
11
+ type = 'single',
12
+ orientation = 'horizontal',
13
+ className = '',
14
+ children,
15
+ ...rest
16
+ }: ToggleGroupProps) {
17
+ const orientationClass = orientation === 'vertical' ? 'toggle-group--vertical' : '';
18
+ const role = type === 'single' ? 'radiogroup' : 'group';
19
+ const classes = ['toggle-group', orientationClass, className].filter(Boolean).join(' ').trim();
20
+ return (
21
+ <div
22
+ className={classes}
23
+ role={role}
24
+ aria-label="Toggle group"
25
+ data-toggle-group
26
+ data-toggle-type={type}
27
+ {...rest}
28
+ >
29
+ {children}
30
+ </div>
31
+ );
32
+ }
33
+
34
+ export default ToggleGroup;
@@ -0,0 +1,40 @@
1
+ import type { HTMLAttributes } from 'react';
2
+
3
+ export type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';
4
+
5
+ export interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
6
+ text: string;
7
+ position?: TooltipPosition;
8
+ delay?: number;
9
+ className?: string;
10
+ id?: string;
11
+ }
12
+
13
+ export function Tooltip({
14
+ text,
15
+ position = 'top',
16
+ delay = 0,
17
+ className = '',
18
+ id: idProp,
19
+ style,
20
+ ...rest
21
+ }: TooltipProps) {
22
+ const tooltipId = idProp ?? `tooltip-${Math.random().toString(36).slice(2, 11)}`;
23
+ const positionClass = `tooltip--${position}`;
24
+ const classes = ['tooltip', positionClass, className].filter(Boolean).join(' ').trim();
25
+ const delayStyle = delay > 0 ? { ['--tooltip-delay' as string]: `${delay}ms`, ...(style as object) } : style;
26
+ return (
27
+ <span
28
+ className={classes}
29
+ role="tooltip"
30
+ id={tooltipId}
31
+ aria-hidden="true"
32
+ style={delayStyle}
33
+ {...rest}
34
+ >
35
+ {text}
36
+ </span>
37
+ );
38
+ }
39
+
40
+ export default Tooltip;
@@ -0,0 +1,50 @@
1
+ # React + Rizzo CSS
2
+
3
+ <pre style="font-family: ui-monospace, monospace; font-size: 0.85em; line-height: 1.2;">
4
+ /\___/\
5
+ __( o o )__
6
+ ( =^= )
7
+ _/ ~ \_
8
+ / \_____/ \
9
+ <span style="color:#c44536"> ____ </span><span style="color:#e07c3e"> ___ _</span><span style="color:#d4a800">______</span><span style="color:#2d9d78">______</span><span style="color:#0052bd"> _</span><span style="color:#7c3aed">___ __</span><span style="color:#d946ef">__ ____</span>
10
+ <span style="color:#c44536">| _ \</span><span style="color:#e07c3e">|_ _|_</span><span style="color:#d4a800">_ /__</span><span style="color:#2d9d78"> / _ </span><span style="color:#0052bd">\ / </span><span style="color:#7c3aed">___/ _</span><span style="color:#d946ef">__/ ___|</span>
11
+ <span style="color:#c44536">| |_) </span><span style="color:#e07c3e">|| | </span><span style="color:#d4a800">/ / /</span><span style="color:#2d9d78"> / | |</span><span style="color:#0052bd"> | | |</span><span style="color:#7c3aed"> \__</span><span style="color:#d946ef">_ \___ \</span>
12
+ <span style="color:#c44536">| _ &lt;</span><span style="color:#e07c3e"> | | /</span><span style="color:#d4a800"> /_ / </span><span style="color:#2d9d78">/| |_|</span><span style="color:#0052bd"> | | |</span><span style="color:#7c3aed">___ __</span><span style="color:#d946ef">_) |__) |</span>
13
+ <span style="color:#c44536">|_| \_</span><span style="color:#e07c3e">\___/_</span><span style="color:#d4a800">___/__</span><span style="color:#2d9d78">__\___</span><span style="color:#0052bd">/ \_</span><span style="color:#7c3aed">___|__</span><span style="color:#d946ef">__/____/</span>
14
+
15
+ Design system · Vanilla · Astro · Svelte · React · Vue
16
+ </pre>
17
+
18
+ Vite + React project with Rizzo CSS. Scaffolded with `npx rizzo-css init` when you chose **Create new** and React, then **Landing**, **Docs**, **Dashboard**, or **Full**. Same template choice for **Add to existing** (`npx rizzo-css add`).
19
+
20
+ ## Setup
21
+
22
+ 1. Install dependencies:
23
+ ```bash
24
+ pnpm install
25
+ # or: npm install
26
+ ```
27
+
28
+ 2. Start the dev server:
29
+ ```bash
30
+ pnpm dev
31
+ ```
32
+
33
+ ## Project structure
34
+
35
+ - `vite.config.ts` — Vite configuration
36
+ - `index.html` — HTML shell with Rizzo CSS and theme (edit `data-theme` for default)
37
+ - `src/main.tsx` — Entry point
38
+ - `src/App.tsx` — Root component
39
+ - `public/css/rizzo.min.css` — Rizzo CSS bundle (added by CLI; font URLs point to `/assets/fonts/`)
40
+ - `public/assets/fonts/` — Rizzo font files (added by CLI)
41
+ - `README-RIZZO.md` — This file (scaffold docs; does not replace your project README)
42
+ - `LICENSE-RIZZO` — Rizzo CSS license (does not replace your project LICENSE)
43
+
44
+ ## Commands
45
+
46
+ - `pnpm dev` — Start dev server
47
+ - `pnpm build` — Build for production
48
+ - `pnpm preview` — Preview production build
49
+
50
+ Docs: [rizzo-css.vercel.app](https://rizzo-css.vercel.app)
@@ -0,0 +1,8 @@
1
+ node_modules
2
+ dist
3
+ dist-ssr
4
+ .DS_Store
5
+ .env
6
+ .env.*
7
+ !.env.example
8
+ *.local
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="{{DATA_THEME}}"><!-- {{THEME_LIST_COMMENT}} -->
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <script>
7
+ (function(){try{var s=localStorage.getItem('theme');var d='{{DEFAULT_DARK}}';var l='{{DEFAULT_LIGHT}}';var initial=document.documentElement.getAttribute('data-theme');var r=!s?((initial&&initial!=='system')?initial:(matchMedia('(prefers-color-scheme: dark)').matches?d:l)):s==='system'?(matchMedia('(prefers-color-scheme: dark)').matches?d:l):s;document.documentElement.setAttribute('data-theme',r);}catch(e){}})();
8
+ </script>
9
+ <link rel="stylesheet" href="/css/rizzo.min.css" />
10
+ <title>{{TITLE}}</title>
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ <script type="module" src="/src/main.tsx"></script>
15
+ </body>
16
+ </html>
@@ -0,0 +1,22 @@
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
+ },
15
+ "devDependencies": {
16
+ "@types/react": "^18.3.12",
17
+ "@types/react-dom": "^18.3.1",
18
+ "@vitejs/plugin-react": "^4.3.3",
19
+ "typescript": "~5.6.2",
20
+ "vite": "^6.0.1"
21
+ }
22
+ }
File without changes
@@ -0,0 +1,11 @@
1
+ export default function App() {
2
+ return (
3
+ <>
4
+ <a href="#main-content" className="skip-link">Skip to main content</a>
5
+ <main id="main-content" className="container">
6
+ <h1>Hello, Rizzo CSS</h1>
7
+ <p>Edit <code>src/App.tsx</code> and add components. Docs: <a href="https://rizzo-css.vercel.app">rizzo-css.vercel.app</a></p>
8
+ </main>
9
+ </>
10
+ );
11
+ }
@@ -0,0 +1 @@
1
+ /* App styles; Rizzo is loaded from /css/rizzo.min.css in index.html */
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "isolatedModules": true,
11
+ "moduleDetection": "force",
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedSideEffectImports": true
19
+ },
20
+ "include": ["src"]
21
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true
7
+ },
8
+ "include": ["vite.config.ts"]
9
+ }
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ });
@@ -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.62`, in production.)
31
+ (Replace `@latest` with a specific version, e.g. `@0.0.64`, 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