zabi-components 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -1473
- package/dist/atoms/ActionPanel.svelte +116 -0
- package/dist/atoms/ActionPanel.svelte.d.ts +19 -0
- package/dist/atoms/Badge.svelte +1 -1
- package/dist/atoms/Badge.svelte.d.ts +1 -1
- package/dist/atoms/Button.svelte +35 -16
- package/dist/atoms/Button.svelte.d.ts +3 -1
- package/dist/atoms/Card.svelte +12 -3
- package/dist/atoms/Card.svelte.d.ts +2 -2
- package/dist/atoms/CardHeader.svelte +8 -4
- package/dist/atoms/CardHeader.svelte.d.ts +4 -2
- package/dist/atoms/Checkbox.svelte +50 -76
- package/dist/atoms/Checkbox.svelte.d.ts +7 -2
- package/dist/atoms/CodeBlock.svelte +20 -34
- package/dist/atoms/CodeBlock.svelte.d.ts +1 -0
- package/dist/atoms/ColorPicker.svelte +2 -6
- package/dist/atoms/Container.svelte +41 -0
- package/dist/atoms/Container.svelte.d.ts +12 -0
- package/dist/atoms/Divider.svelte +68 -0
- package/dist/atoms/Divider.svelte.d.ts +13 -0
- package/dist/atoms/FeatureCard.svelte +35 -50
- package/dist/atoms/FeatureCard.svelte.d.ts +9 -4
- package/dist/atoms/IconButton.svelte +33 -24
- package/dist/atoms/IconButton.svelte.d.ts +4 -8
- package/dist/atoms/Input.svelte +58 -26
- package/dist/atoms/Input.svelte.d.ts +6 -1
- package/dist/atoms/List.svelte +2 -7
- package/dist/atoms/List.svelte.d.ts +1 -6
- package/dist/atoms/ListItem.svelte +54 -65
- package/dist/atoms/ListItem.svelte.d.ts +7 -13
- package/dist/atoms/ListItemLeading.svelte +19 -0
- package/dist/atoms/ListItemLeading.svelte.d.ts +9 -0
- package/dist/atoms/Progress.svelte +1 -1
- package/dist/atoms/Radio.svelte +51 -0
- package/dist/atoms/Radio.svelte.d.ts +14 -0
- package/dist/atoms/Select.svelte +23 -30
- package/dist/atoms/Select.svelte.d.ts +0 -10
- package/dist/atoms/SelectionControl.svelte +98 -0
- package/dist/atoms/SelectionControl.svelte.d.ts +26 -0
- package/dist/atoms/Skeleton.svelte +39 -15
- package/dist/atoms/Skeleton.svelte.d.ts +7 -3
- package/dist/atoms/Table.svelte +27 -0
- package/dist/atoms/Table.svelte.d.ts +10 -0
- package/dist/atoms/Text.svelte +46 -0
- package/dist/atoms/Text.svelte.d.ts +13 -0
- package/dist/atoms/Textarea.svelte +74 -53
- package/dist/atoms/Textarea.svelte.d.ts +6 -2
- package/dist/atoms/ThemeToggle.svelte +35 -24
- package/dist/atoms/ThemeToggle.svelte.d.ts +1 -0
- package/dist/atoms/Toast.svelte +55 -31
- package/dist/atoms/Toast.svelte.d.ts +4 -1
- package/dist/atoms/Toggle.svelte +37 -22
- package/dist/atoms/Toggle.svelte.d.ts +2 -1
- package/dist/atoms/Tooltip.svelte +90 -11
- package/dist/atoms/index.d.ts +6 -0
- package/dist/atoms/index.js +6 -0
- package/dist/atoms/selection-control.styles.d.ts +25 -0
- package/dist/atoms/selection-control.styles.js +30 -0
- package/dist/components/atoms/index.d.ts +6 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +26 -0
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -0
- package/dist/components/index.d.ts +41 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +10 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +39 -0
- package/dist/components/molecules/toast-store.d.ts.map +1 -0
- package/dist/components/organisms/index.d.ts +2 -1
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/page.types.d.ts +69 -0
- package/dist/components/types/page.types.d.ts.map +1 -0
- package/dist/components/types/page.types.js +1 -0
- package/dist/components/types/variants.d.ts +64 -0
- package/dist/components/types/variants.d.ts.map +1 -0
- package/dist/components/types/variants.js +44 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts +23 -0
- package/dist/components/util/fixed-sidebar-flyout.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +12 -0
- package/dist/components/util/focus-utils.d.ts.map +1 -0
- package/dist/components/util/ssr-safe.d.ts +17 -0
- package/dist/components/util/ssr-safe.d.ts.map +1 -0
- package/dist/index.d.ts +41 -5
- package/dist/index.js +44 -8
- package/dist/lib/layout-width-tokens.d.ts +17 -0
- package/dist/lib/layout-width-tokens.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Button.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Button.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Input.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Input.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/List.d.ts +3 -0
- package/dist/lib/showcase/component-docs/List.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Modal.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Radio.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +3 -0
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts +3 -0
- package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts +12 -0
- package/dist/lib/showcase/component-docs/_shared.d.ts.map +1 -0
- package/dist/lib/showcase/component-docs/index.d.ts +4 -0
- package/dist/lib/showcase/component-docs/index.d.ts.map +1 -0
- package/dist/lib/showcase/components-catalog.d.ts.map +1 -1
- package/dist/lib/showcase/components-showcase-constants.d.ts +26 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -1
- package/dist/molecules/Alert.svelte +50 -23
- package/dist/molecules/Alert.svelte.d.ts +1 -1
- package/dist/molecules/ComponentDemo.svelte +2 -2
- package/dist/molecules/ContactForm.svelte +1 -1
- package/dist/molecules/Dropdown.svelte +99 -109
- package/dist/molecules/Dropdown.svelte.d.ts +12 -6
- package/dist/molecules/EmptyState.svelte +44 -0
- package/dist/molecules/EmptyState.svelte.d.ts +11 -0
- package/dist/molecules/FormField.svelte +89 -0
- package/dist/molecules/FormField.svelte.d.ts +23 -0
- package/dist/molecules/Header.svelte +41 -0
- package/dist/molecules/Header.svelte.d.ts +10 -0
- package/dist/molecules/Modal.svelte +37 -24
- package/dist/molecules/Modal.svelte.d.ts +4 -2
- package/dist/molecules/NavigationMenu.svelte +16 -0
- package/dist/molecules/NavigationMenu.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuContent.svelte +9 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuTrigger.svelte +9 -1
- package/dist/molecules/Page.svelte +16 -0
- package/dist/molecules/Page.svelte.d.ts +8 -0
- package/dist/molecules/PropsTable.svelte +61 -0
- package/dist/molecules/PropsTable.svelte.d.ts +8 -0
- package/dist/molecules/RadioGroup.svelte +197 -0
- package/dist/molecules/RadioGroup.svelte.d.ts +20 -0
- package/dist/molecules/Section.svelte +2 -11
- package/dist/molecules/Section.svelte.d.ts +2 -11
- package/dist/molecules/SidebarBrandHeader.svelte +68 -0
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +10 -0
- package/dist/molecules/SidebarFooter.svelte +119 -0
- package/dist/molecules/SidebarFooter.svelte.d.ts +23 -0
- package/dist/molecules/SidebarNavSection.svelte +49 -0
- package/dist/molecules/SidebarNavSection.svelte.d.ts +14 -0
- package/dist/molecules/SlideUp.svelte +19 -14
- package/dist/molecules/SlideUp.svelte.d.ts +1 -1
- package/dist/molecules/Tabs.svelte +4 -9
- package/dist/molecules/Toaster.svelte +21 -0
- package/dist/molecules/Toaster.svelte.d.ts +6 -0
- package/dist/molecules/ToasterToast.svelte +217 -0
- package/dist/molecules/ToasterToast.svelte.d.ts +7 -0
- package/dist/molecules/index.d.ts +10 -0
- package/dist/molecules/index.js +10 -0
- package/dist/molecules/navigation-menu-context.d.ts +11 -0
- package/dist/molecules/navigation-menu-context.js +18 -0
- package/dist/molecules/toast-store.d.ts +38 -0
- package/dist/molecules/toast-store.js +34 -0
- package/dist/organisms/SidebarAccountPanel.svelte +118 -0
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +20 -0
- package/dist/organisms/SidebarNavigation.svelte +267 -237
- package/dist/organisms/SidebarNavigation.svelte.d.ts +21 -5
- package/dist/organisms/SidebarPanel.svelte +54 -22
- package/dist/organisms/SidebarPanel.svelte.d.ts +3 -0
- package/dist/organisms/{Navbar.svelte → TopNavbar.svelte} +22 -17
- package/dist/organisms/TopNavbar.svelte.d.ts +35 -0
- package/dist/organisms/index.d.ts +2 -1
- package/dist/organisms/index.js +2 -1
- package/dist/routes/lib/focus-utils.d.ts +7 -18
- package/dist/routes/lib/focus-utils.ts +32 -34
- package/dist/routes/lib/ssr-safe.ts +5 -4
- package/dist/routes/lib/variant-utils.ts +48 -39
- package/dist/types/components.d.ts +187 -0
- package/dist/types/components.d.ts.map +1 -0
- package/dist/types/events.d.ts +200 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/index.d.ts +171 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/page.types.d.ts +2 -31
- package/dist/types/page.types.d.ts.map +1 -1
- package/dist/types/page.types.js +1 -0
- package/dist/types/page.types.ts +79 -0
- package/dist/types/variants.d.ts +3 -0
- package/dist/types/variants.d.ts.map +1 -0
- package/dist/types/variants.js +1 -0
- package/dist/types/variants.ts +5 -6
- package/dist/util/fixed-sidebar-flyout.d.ts +22 -0
- package/dist/util/fixed-sidebar-flyout.js +148 -0
- package/dist/util/focus-utils.d.ts +11 -0
- package/dist/util/focus-utils.js +94 -0
- package/dist/util/ssr-safe.d.ts +16 -0
- package/dist/util/ssr-safe.js +48 -0
- package/dist/zabi-components-colors.css +14 -1
- package/dist/zabi-components-theme-dark-only.css +5 -0
- package/dist/zabi-components-theme-dark.css +5 -0
- package/dist/zabi-components-theme-only.css +9 -1
- package/dist/zabi-components-theme.css +9 -1
- package/dist/zabi-components.css +877 -192
- package/package.json +19 -7
- package/dist/organisms/Navbar.svelte.d.ts +0 -41
package/README.md
CHANGED
|
@@ -1,1580 +1,244 @@
|
|
|
1
1
|
# Zabi Components
|
|
2
2
|
|
|
3
|
-
Svelte 5 UI
|
|
3
|
+
Svelte **5** UI primitives (runes, `onclick` / `oninput`, …), Tailwind **v4** design tokens, SSR-safe. Requires **Svelte ≥ 5.43.8**.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Approach
|
|
8
|
-
|
|
9
|
-
Semantic HTML, sensible defaults, and props that stay predictable—especially when you use SSR.
|
|
10
|
-
|
|
11
|
-
## Features
|
|
5
|
+
---
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
- 🎨 **Semantic Color System** - Built-in semantic colors with automatic dark mode support
|
|
15
|
-
- 🌙 **Dark Mode Ready** - Automatic dark mode switching with CSS custom properties
|
|
16
|
-
- ♿ **Accessibility First** - ARIA compliant components with keyboard navigation support
|
|
17
|
-
- 📱 **Responsive Design** - Mobile-first approach with responsive utilities
|
|
18
|
-
- 🧩 **Clean API** - Simple, intuitive component APIs with minimal props
|
|
19
|
-
- 🔧 **Utility Functions** - Reusable variant utilities for consistent styling
|
|
20
|
-
- 📦 **Tree Shakeable** - Import only what you need
|
|
21
|
-
- ✅ **Production Ready** - Fully tested and optimized for production use
|
|
22
|
-
- 🚀 **Modern CSS** - CSS-only positioning, animations, and interactions
|
|
23
|
-
- ⚡ **Lightweight** - 60-80% less code than traditional component libraries
|
|
24
|
-
- 🎭 **Emoji Icons** - Simple emoji-based icons instead of complex SVG libraries
|
|
25
|
-
- 🛡️ **SSR Safe** - 100% server-side rendering compatible with zero runtime errors
|
|
26
|
-
- 🔄 **SvelteKit Ready** - Full SvelteKit integration with proper hydration
|
|
7
|
+
## Quick start
|
|
27
8
|
|
|
28
|
-
|
|
9
|
+
**1. Install**
|
|
29
10
|
|
|
30
11
|
```bash
|
|
31
12
|
npm install zabi-components
|
|
32
13
|
```
|
|
33
14
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Make sure you have the required peer dependencies installed:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install svelte@^5.43.8
|
|
40
|
-
npm install @sveltejs/kit@^2.0.0 # Optional, for SvelteKit projects
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Theme Setup
|
|
15
|
+
Ensure `svelte@^5.43.8` is installed — SvelteKit apps usually already match; add `@sveltejs/kit@^2` only for Kit. Optional: `react@^18` if you use `zabi-components/react`.
|
|
44
16
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### Quick Setup
|
|
48
|
-
|
|
49
|
-
**Canonical (recommended for Tailwind v4 apps):**
|
|
17
|
+
**2. Global CSS** (e.g. SvelteKit `src/app.css` — adjust for your bundler entry)
|
|
50
18
|
|
|
51
19
|
```css
|
|
52
|
-
/* app.css */
|
|
53
20
|
@import "tailwindcss";
|
|
54
21
|
@import "zabi-components/theme-only";
|
|
55
22
|
@import "zabi-components/theme-dark-only";
|
|
56
23
|
```
|
|
57
24
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```css
|
|
61
|
-
/* app.css */
|
|
62
|
-
@import "zabi-components/colors";
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### With Dark Mode
|
|
66
|
-
|
|
67
|
-
```css
|
|
68
|
-
@import "tailwindcss";
|
|
69
|
-
@import "zabi-components/theme-only";
|
|
70
|
-
@import "zabi-components/theme-dark-only"; /* Add dark mode support */
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Extending the Theme
|
|
25
|
+
No Tailwind? See [docs/theme-imports.md](./docs/theme-imports.md) (`colors` or full `css`).
|
|
74
26
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```css
|
|
78
|
-
@import "tailwindcss";
|
|
79
|
-
@import 'zabi-components/theme-only';
|
|
80
|
-
|
|
81
|
-
/* Your custom theme extensions */
|
|
82
|
-
@theme {
|
|
83
|
-
--font-family-title: 'Your Font', sans-serif;
|
|
84
|
-
--color-custom-primary: #ff0000;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@import "zabi-components/theme-only";
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
> 📖 **Complete Theme Guide**: See [THEME.md](./THEME.md) for comprehensive documentation on theme usage, customization, dark mode, and advanced patterns.
|
|
91
|
-
> **Which CSS file to import** (`theme`, `theme-only`, `colors`, etc.): [docs/theme-imports.md](./docs/theme-imports.md)
|
|
92
|
-
|
|
93
|
-
## Import Methods
|
|
94
|
-
|
|
95
|
-
Zabi Components supports multiple import patterns:
|
|
96
|
-
|
|
97
|
-
### Main Import (All Components)
|
|
98
|
-
```typescript
|
|
99
|
-
import {
|
|
100
|
-
Button,
|
|
101
|
-
IconButton,
|
|
102
|
-
Input,
|
|
103
|
-
Card,
|
|
104
|
-
CardHeader,
|
|
105
|
-
CardContent,
|
|
106
|
-
CardFooter,
|
|
107
|
-
Alert,
|
|
108
|
-
Badge,
|
|
109
|
-
Modal,
|
|
110
|
-
NavigationMenu,
|
|
111
|
-
Section
|
|
112
|
-
} from 'zabi-components';
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Subpath Imports (Recommended for Tree Shaking)
|
|
116
|
-
```typescript
|
|
117
|
-
// Import from specific categories
|
|
118
|
-
import {
|
|
119
|
-
Button,
|
|
120
|
-
IconButton,
|
|
121
|
-
Input,
|
|
122
|
-
Badge,
|
|
123
|
-
Card,
|
|
124
|
-
CardHeader,
|
|
125
|
-
CardContent,
|
|
126
|
-
CardFooter
|
|
127
|
-
} from 'zabi-components/atoms';
|
|
128
|
-
import {
|
|
129
|
-
Alert,
|
|
130
|
-
Modal,
|
|
131
|
-
Dropdown,
|
|
132
|
-
NavigationMenu,
|
|
133
|
-
NavigationMenuList,
|
|
134
|
-
NavigationMenuItem,
|
|
135
|
-
NavigationMenuTrigger,
|
|
136
|
-
NavigationMenuContent,
|
|
137
|
-
NavigationMenuLink,
|
|
138
|
-
Section,
|
|
139
|
-
Tabs
|
|
140
|
-
} from 'zabi-components/molecules';
|
|
141
|
-
import { Navbar, SidebarNavigation, SidebarPanel } from 'zabi-components/organisms';
|
|
142
|
-
|
|
143
|
-
// Import types separately
|
|
144
|
-
import type { ButtonEvents, InputEvents } from 'zabi-components/types';
|
|
145
|
-
|
|
146
|
-
// Import utility functions
|
|
147
|
-
import {
|
|
148
|
-
createId,
|
|
149
|
-
cn,
|
|
150
|
-
getFormData,
|
|
151
|
-
validateEmail,
|
|
152
|
-
safeLocalStorage,
|
|
153
|
-
safeDocument
|
|
154
|
-
} from 'zabi-components';
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Quick Start
|
|
27
|
+
**3. Use a component** (`+page.svelte` or any Svelte 5 component)
|
|
158
28
|
|
|
159
29
|
```svelte
|
|
160
30
|
<script lang="ts">
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
CardContent,
|
|
165
|
-
CardFooter,
|
|
166
|
-
Form,
|
|
167
|
-
Navbar,
|
|
168
|
-
Button,
|
|
169
|
-
IconButton,
|
|
170
|
-
Input,
|
|
171
|
-
Textarea,
|
|
172
|
-
Badge,
|
|
173
|
-
Alert
|
|
174
|
-
} from 'zabi-components';
|
|
175
|
-
|
|
176
|
-
let formData = $state({
|
|
177
|
-
name: '',
|
|
178
|
-
email: '',
|
|
179
|
-
message: '',
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
function handleFormSubmit(event: SubmitEvent) {
|
|
183
|
-
const formData = new FormData(event.target as HTMLFormElement);
|
|
184
|
-
const data = Object.fromEntries(formData.entries());
|
|
185
|
-
console.log('Form submitted:', data);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function handleCardClick(event: MouseEvent) {
|
|
189
|
-
console.log('Card clicked:', event);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const navItems = [
|
|
193
|
-
{ label: "Home", href: "/" },
|
|
194
|
-
{ label: "About", href: "/about" },
|
|
195
|
-
{ label: "Contact", href: "/contact" },
|
|
196
|
-
];
|
|
31
|
+
import { Button, Input } from "zabi-components";
|
|
32
|
+
|
|
33
|
+
let name = $state("");
|
|
197
34
|
</script>
|
|
198
35
|
|
|
199
|
-
<div class="
|
|
200
|
-
|
|
201
|
-
<
|
|
202
|
-
<Navbar embedded ariaLabel="Primary" navVariant="header" items={navItems} />
|
|
203
|
-
</header>
|
|
204
|
-
|
|
205
|
-
<main class="container mx-auto p-6">
|
|
206
|
-
<!-- Card with Compound Components -->
|
|
207
|
-
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
|
|
208
|
-
<Card variant="elevated" onclick={handleCardClick}>
|
|
209
|
-
<CardHeader>
|
|
210
|
-
<h3 class="text-lg font-semibold">Default Card</h3>
|
|
211
|
-
</CardHeader>
|
|
212
|
-
<CardContent>
|
|
213
|
-
<p>This is a card using compound components.</p>
|
|
214
|
-
</CardContent>
|
|
215
|
-
<CardFooter>
|
|
216
|
-
<Button variant="primary" size="sm">Learn More</Button>
|
|
217
|
-
</CardFooter>
|
|
218
|
-
</Card>
|
|
219
|
-
|
|
220
|
-
<Card variant="success" onclick={handleCardClick}>
|
|
221
|
-
<CardHeader>
|
|
222
|
-
<h3 class="text-lg font-semibold">Success Card</h3>
|
|
223
|
-
</CardHeader>
|
|
224
|
-
<CardContent>
|
|
225
|
-
<p>This card indicates a successful action.</p>
|
|
226
|
-
</CardContent>
|
|
227
|
-
</Card>
|
|
228
|
-
|
|
229
|
-
<Card variant="warning" onclick={handleCardClick}>
|
|
230
|
-
<CardHeader>
|
|
231
|
-
<h3 class="text-lg font-semibold">Warning Card</h3>
|
|
232
|
-
</CardHeader>
|
|
233
|
-
<CardContent>
|
|
234
|
-
<p>This card shows a warning state.</p>
|
|
235
|
-
</CardContent>
|
|
236
|
-
</Card>
|
|
237
|
-
</div>
|
|
238
|
-
|
|
239
|
-
<!-- Form with Semantic Variants -->
|
|
240
|
-
<Card variant="outlined">
|
|
241
|
-
<CardHeader>
|
|
242
|
-
<h2 class="text-xl font-bold">Contact Form</h2>
|
|
243
|
-
</CardHeader>
|
|
244
|
-
<CardContent>
|
|
245
|
-
<Form onsubmit={handleFormSubmit}>
|
|
246
|
-
<div class="form-field">
|
|
247
|
-
<Input
|
|
248
|
-
id="name"
|
|
249
|
-
name="name"
|
|
250
|
-
value={formData.name}
|
|
251
|
-
oninput={(e) => formData.name = (e.target as HTMLInputElement).value}
|
|
252
|
-
label="Name"
|
|
253
|
-
placeholder="Enter your name"
|
|
254
|
-
variant="default"
|
|
255
|
-
/>
|
|
256
|
-
</div>
|
|
257
|
-
|
|
258
|
-
<div class="form-field">
|
|
259
|
-
<Input
|
|
260
|
-
id="email"
|
|
261
|
-
name="email"
|
|
262
|
-
type="email"
|
|
263
|
-
value={formData.email}
|
|
264
|
-
oninput={(e) => formData.email = (e.target as HTMLInputElement).value}
|
|
265
|
-
label="Email"
|
|
266
|
-
placeholder="Enter your email"
|
|
267
|
-
variant="success"
|
|
268
|
-
/>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
<div class="form-field">
|
|
272
|
-
<Textarea
|
|
273
|
-
id="message"
|
|
274
|
-
name="message"
|
|
275
|
-
value={formData.message}
|
|
276
|
-
oninput={(e) => formData.message = (e.target as HTMLTextAreaElement).value}
|
|
277
|
-
label="Message"
|
|
278
|
-
placeholder="Enter your message"
|
|
279
|
-
variant="default"
|
|
280
|
-
/>
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
<div class="form-actions flex gap-2">
|
|
284
|
-
<Button type="submit" variant="primary" className="flex-1">
|
|
285
|
-
Submit
|
|
286
|
-
</Button>
|
|
287
|
-
<IconButton variant="outline" label="Help">
|
|
288
|
-
❓
|
|
289
|
-
</IconButton>
|
|
290
|
-
</div>
|
|
291
|
-
</Form>
|
|
292
|
-
</CardContent>
|
|
293
|
-
</Card>
|
|
294
|
-
|
|
295
|
-
<!-- Badges and Alerts -->
|
|
296
|
-
<div class="mt-6 space-y-4">
|
|
297
|
-
<div class="flex gap-2">
|
|
298
|
-
<Badge variant="success">Success</Badge>
|
|
299
|
-
<Badge variant="warning">Warning</Badge>
|
|
300
|
-
<Badge variant="error">Error</Badge>
|
|
301
|
-
<Badge variant="info">Info</Badge>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
<Alert variant="info" title="Information" message="This is an informational alert." />
|
|
305
|
-
</div>
|
|
306
|
-
</main>
|
|
36
|
+
<div class="flex max-w-md flex-col gap-4 p-6">
|
|
37
|
+
<Input label="Name" name="name" bind:value={name} placeholder="Ada Lovelace" />
|
|
38
|
+
<Button onclick={() => console.log(name)}>Save</Button>
|
|
307
39
|
</div>
|
|
308
40
|
```
|
|
309
41
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
### Core components
|
|
313
|
-
|
|
314
|
-
| Component | Category | Description | Key Features |
|
|
315
|
-
|-----------|----------|-------------|--------------|
|
|
316
|
-
| **Card** | Atom | Clean card container | Simple, interactive, image support, compound components, 4 variants |
|
|
317
|
-
| **CardHeader** | Atom | Card header section | Compound component for card structure |
|
|
318
|
-
| **CardContent** | Atom | Card content section | Compound component for card structure |
|
|
319
|
-
| **CardFooter** | Atom | Card footer section | Compound component for card structure |
|
|
320
|
-
| **Form** | Molecule | Simple form wrapper | FormData handling, clean API |
|
|
321
|
-
| **Button** | Atom | Action button | 6 variants (primary, secondary, danger, ghost, outline, link) |
|
|
322
|
-
| **IconButton** | Atom | Icon-only button | Icon-only, variants, sizes, accessible label |
|
|
323
|
-
| **Input** | Atom | Form input | Essential props, accessibility, semantic variants |
|
|
324
|
-
| **Badge** | Atom | Status indicator | 5 color variants, closable, icon support |
|
|
325
|
-
| **Modal** | Molecule | Overlay dialog | Focus trap, keyboard navigation, focus return |
|
|
326
|
-
| **Tabs** | Molecule | Tab navigation | Keyboard navigation, 2 variants |
|
|
327
|
-
| **Dropdown** | Molecule | Dropdown menu | CSS-only positioning, keyboard navigation, ARIA support |
|
|
328
|
-
| **ImageUpload** | Molecule | File upload | Direct selection, preview |
|
|
329
|
-
| **Navbar** | Organism | Navigation bar | Built-in links or snippets, `embedded` link strip, mobile menu |
|
|
330
|
-
| **NavigationMenu** | Molecule | Advanced navigation menu | Compound components, keyboard navigation |
|
|
331
|
-
| **Section** | Molecule | Content section wrapper | Responsive layout, variant support |
|
|
332
|
-
| **Sidebar** | Molecule | Sidebar navigation | Collapsible, responsive |
|
|
333
|
-
|
|
334
|
-
### Simplified Components
|
|
335
|
-
|
|
336
|
-
| Component | Category | Description | Key Features |
|
|
337
|
-
|-----------|----------|-------------|--------------|
|
|
338
|
-
| **Checkbox** | Atom | Checkbox input | Simple on/off state, semantic variants |
|
|
339
|
-
| **Select** | Atom | Dropdown select | Basic options support, semantic variants |
|
|
340
|
-
| **Textarea** | Atom | Multi-line input | Essential configuration, semantic variants |
|
|
341
|
-
| **Toggle** | Atom | Toggle switch | Fixed size, simple state |
|
|
342
|
-
| **Progress** | Atom | Progress bar | Percentage display |
|
|
343
|
-
| **Heading** | Atom | Text headings | 6 levels, clean styling |
|
|
344
|
-
| **ThemeToggle** | Atom | Theme switcher | Emoji-based, simple |
|
|
345
|
-
| **Toast** | Atom | Notification toast | Fixed position, basic |
|
|
346
|
-
| **Tooltip** | Atom | Hover tooltip | CSS-only positioning |
|
|
347
|
-
| **Skeleton** | Atom | Loading placeholder | Simple animation |
|
|
348
|
-
| **ColorPicker** | Atom | Color selection | Simple color grid |
|
|
349
|
-
| **SlideUp** | Molecule | Slide-up panel | CSS-only animations |
|
|
350
|
-
| **CodeBlock** | Atom | Code display | Syntax highlighting support |
|
|
351
|
-
| **FeatureCard** | Atom | Feature showcase card | Image, title, description |
|
|
352
|
-
| **OptimizedImage** | Atom | Optimized image | Lazy loading, responsive |
|
|
353
|
-
| **Alert** | Molecule | Alert notification | Variants, closable, custom content |
|
|
354
|
-
| **ContactForm** | Molecule | Contact form | Pre-built form with validation |
|
|
355
|
-
| **ComponentDemo** | Molecule | Component showcase | Demo wrapper for examples |
|
|
356
|
-
|
|
357
|
-
## Semantic Color System
|
|
358
|
-
|
|
359
|
-
Zabi Components includes a comprehensive semantic color system that automatically supports dark mode and provides consistent styling across all components.
|
|
360
|
-
|
|
361
|
-
### Available Variants
|
|
362
|
-
|
|
363
|
-
All components support these semantic color variants:
|
|
364
|
-
|
|
365
|
-
- **`default`** - Standard appearance with primary colors
|
|
366
|
-
- **`success`** - Green colors for positive states
|
|
367
|
-
- **`warning`** - Yellow/amber colors for caution states
|
|
368
|
-
- **`error`** - Red colors for error states
|
|
369
|
-
- **`info`** - Blue colors for informational states
|
|
370
|
-
|
|
371
|
-
### Usage Examples
|
|
42
|
+
Without the theme CSS, token classes (`bg-input`, `text-body`, …) look unstyled — always import the theme.
|
|
372
43
|
|
|
373
|
-
|
|
374
|
-
<!-- Input variants -->
|
|
375
|
-
<Input variant="default" label="Default Input" />
|
|
376
|
-
<Input variant="success" label="Success Input" />
|
|
377
|
-
<Input variant="warning" label="Warning Input" />
|
|
378
|
-
<Input variant="error" label="Error Input" />
|
|
379
|
-
|
|
380
|
-
<!-- Textarea variants -->
|
|
381
|
-
<Textarea variant="default" label="Default Textarea" />
|
|
382
|
-
<Textarea variant="success" label="Success Textarea" />
|
|
383
|
-
|
|
384
|
-
<!-- Card variants -->
|
|
385
|
-
<Card variant="default" title="Default Card" />
|
|
386
|
-
<Card variant="success" title="Success Card" />
|
|
387
|
-
<Card variant="warning" title="Warning Card" />
|
|
388
|
-
<Card variant="error" title="Error Card" />
|
|
389
|
-
<Card variant="info" title="Info Card" />
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Utility Functions
|
|
393
|
-
|
|
394
|
-
Zabi Components exports several utility functions for common tasks:
|
|
395
|
-
|
|
396
|
-
```typescript
|
|
397
|
-
// Main exports (most utilities)
|
|
398
|
-
import {
|
|
399
|
-
createId,
|
|
400
|
-
cn,
|
|
401
|
-
getFormData,
|
|
402
|
-
validateEmail,
|
|
403
|
-
validateRequired,
|
|
404
|
-
isBrowser,
|
|
405
|
-
safeWindow,
|
|
406
|
-
safeDocument,
|
|
407
|
-
safeLocalStorage,
|
|
408
|
-
generateId
|
|
409
|
-
} from 'zabi-components';
|
|
410
|
-
|
|
411
|
-
// Or import SSR-safe utilities directly
|
|
412
|
-
import {
|
|
413
|
-
safeLocalStorage,
|
|
414
|
-
safeDocument,
|
|
415
|
-
safeWindow,
|
|
416
|
-
isBrowser,
|
|
417
|
-
generateId
|
|
418
|
-
} from 'zabi-components/lib/ssr-safe';
|
|
419
|
-
|
|
420
|
-
// ID Generation (SSR-safe)
|
|
421
|
-
const uniqueId = createId('input'); // Returns "input-abc123xyz" (client) or "input-ssr-1234567890" (server)
|
|
422
|
-
|
|
423
|
-
// Class Name Utility
|
|
424
|
-
const className = cn('base-class', condition && 'conditional-class', undefined);
|
|
425
|
-
// Returns "base-class conditional-class" (filters out falsy values)
|
|
426
|
-
|
|
427
|
-
// Form Utilities
|
|
428
|
-
const form = document.querySelector('form') as HTMLFormElement;
|
|
429
|
-
const data = getFormData(form); // Returns object with form data
|
|
430
|
-
|
|
431
|
-
// Validation
|
|
432
|
-
const isValidEmail = validateEmail('user@example.com'); // Returns true/false
|
|
433
|
-
const isRequired = validateRequired('value'); // Returns true/false
|
|
434
|
-
|
|
435
|
-
// Browser Detection (SSR-safe)
|
|
436
|
-
if (isBrowser()) {
|
|
437
|
-
// Safe to use window, document, localStorage, etc.
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Safe Browser APIs
|
|
441
|
-
const window = safeWindow(); // Returns Window | undefined
|
|
442
|
-
const document = safeDocument(); // Returns Document | undefined
|
|
443
|
-
const storage = safeLocalStorage(); // Returns Storage | undefined
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### Variant Utility Functions
|
|
447
|
-
|
|
448
|
-
For custom components or advanced usage, you can use the built-in variant utility functions:
|
|
449
|
-
|
|
450
|
-
```typescript
|
|
451
|
-
import {
|
|
452
|
-
getInputVariantClasses,
|
|
453
|
-
getCardVariantClasses,
|
|
454
|
-
getVariantClasses
|
|
455
|
-
} from 'zabi-components/lib/variant-utils';
|
|
456
|
-
|
|
457
|
-
// For input components (Input, Textarea)
|
|
458
|
-
const inputClass = getInputVariantClasses('success'); // Returns "input-variant-success"
|
|
459
|
-
|
|
460
|
-
// For card components
|
|
461
|
-
const cardClass = getCardVariantClasses('error'); // Returns "card-variant-error"
|
|
462
|
-
|
|
463
|
-
// For custom components
|
|
464
|
-
const borderClass = getVariantClasses('warning', 'border'); // Returns "variant-border-warning"
|
|
465
|
-
const textClass = getVariantClasses('success', 'text'); // Returns "variant-text-success"
|
|
466
|
-
const bgClass = getVariantClasses('info', 'bg'); // Returns "variant-bg-info"
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### Dark Mode Support
|
|
470
|
-
|
|
471
|
-
The semantic color system automatically adapts to dark mode through CSS custom properties. Simply add the `.dark` class to your document:
|
|
472
|
-
|
|
473
|
-
```javascript
|
|
474
|
-
// Toggle dark mode
|
|
475
|
-
document.documentElement.classList.toggle('dark');
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
All components will automatically switch to their dark mode variants without any additional configuration.
|
|
44
|
+
---
|
|
479
45
|
|
|
480
|
-
##
|
|
46
|
+
## Import strategy
|
|
481
47
|
|
|
482
|
-
|
|
48
|
+
| Approach | When to use |
|
|
49
|
+
|----------|-------------|
|
|
50
|
+
| **Barrel** `import { … } from "zabi-components"` | **Default.** Smallest mental load; fine for most apps. |
|
|
51
|
+
| **Category** `zabi-components/atoms`, `/molecules`, `/organisms` | Larger bundles where you want imports to mirror structure, or you only pull one layer in a given file. |
|
|
52
|
+
| **Types** `zabi-components/types` | Shared prop unions / enums in your app code. |
|
|
53
|
+
| **Helpers** `zabi-components`, `zabi-components/lib/ssr-safe`, `lib/variant-utils` | Utilities the package publishes — not app internals. |
|
|
483
54
|
|
|
484
|
-
|
|
485
|
-
<Card
|
|
486
|
-
title={string}
|
|
487
|
-
image={string}
|
|
488
|
-
interactive={boolean}
|
|
489
|
-
variant="default" | "success" | "warning" | "error" | "info"
|
|
490
|
-
size="sm" | "md" | "lg"
|
|
491
|
-
className={string}
|
|
492
|
-
on:click={(e) => console.log(e.detail.event)}
|
|
493
|
-
>
|
|
494
|
-
Custom Content
|
|
495
|
-
</Card>
|
|
496
|
-
```
|
|
55
|
+
Stick to **documented package subpaths** from `package.json` `exports`. Do not reach into `node_modules/zabi-components/dist/...` by hand.
|
|
497
56
|
|
|
498
|
-
|
|
499
|
-
- `title`: Card title
|
|
500
|
-
- `image`: Card image URL
|
|
501
|
-
- `interactive`: Make card clickable (default: false)
|
|
502
|
-
- `variant`: Card variant with semantic colors (default: "default")
|
|
503
|
-
- `size`: Card size (default: "md")
|
|
504
|
-
- `className`: Additional CSS classes
|
|
57
|
+
---
|
|
505
58
|
|
|
506
|
-
|
|
507
|
-
- `click`: Fired when interactive card is clicked - `{ detail: { event: MouseEvent } }`
|
|
59
|
+
## Mental model
|
|
508
60
|
|
|
509
|
-
|
|
61
|
+
**Data-driven (start here)** — You pass **arrays or option lists**; the component renders rows and wires behavior (keyboard, selection, labels).
|
|
510
62
|
|
|
511
63
|
```svelte
|
|
512
|
-
<
|
|
513
|
-
|
|
514
|
-
action={string}
|
|
515
|
-
className={string}
|
|
516
|
-
on:submit={(e) => console.log('Form submitted')}
|
|
517
|
-
>
|
|
518
|
-
<div class="form-field">
|
|
519
|
-
<label for="field" class="form-label">Label</label>
|
|
520
|
-
<Input id="field" name="field" class="form-input" />
|
|
521
|
-
</div>
|
|
522
|
-
<div class="form-actions">
|
|
523
|
-
<Button type="submit">Submit</Button>
|
|
524
|
-
</div>
|
|
525
|
-
</Form>
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
**Props:**
|
|
529
|
-
- `method`: Form method (default: "post")
|
|
530
|
-
- `action`: Form action URL
|
|
531
|
-
- `className`: Additional CSS classes
|
|
532
|
-
|
|
533
|
-
**Events:**
|
|
534
|
-
- `submit`: Native form submit event - use `FormData` to get form data
|
|
535
|
-
|
|
536
|
-
**CSS Classes:**
|
|
537
|
-
- `.form-field`: Field container
|
|
538
|
-
- `.form-label`: Field label
|
|
539
|
-
- `.form-input`: Field input
|
|
540
|
-
- `.form-actions`: Action buttons container
|
|
64
|
+
<script lang="ts">
|
|
65
|
+
import { List, RadioGroup } from "zabi-components";
|
|
541
66
|
|
|
542
|
-
|
|
67
|
+
let choice = $state<string | undefined>();
|
|
68
|
+
</script>
|
|
543
69
|
|
|
544
|
-
|
|
545
|
-
<
|
|
546
|
-
variant="main" | "sidebar" | "grid"
|
|
547
|
-
className={string}
|
|
548
|
-
>
|
|
549
|
-
<div slot="header">Header Content</div>
|
|
550
|
-
Main Content
|
|
551
|
-
<div slot="footer">Footer Content</div>
|
|
552
|
-
</Layout>
|
|
70
|
+
<List items={[{ id: "a", label: "Alpha" }]} ariaLabel="Example" />
|
|
71
|
+
<RadioGroup legend="Pick one" options={[{ value: "x", label: "X" }]} bind:value={choice} />
|
|
553
72
|
```
|
|
554
73
|
|
|
555
|
-
**
|
|
556
|
-
- `variant`: Layout type (default: "main")
|
|
557
|
-
- `className`: Additional CSS classes
|
|
558
|
-
|
|
559
|
-
**Slots:**
|
|
560
|
-
- `header`: Header content
|
|
561
|
-
- `default`: Main content
|
|
562
|
-
- `footer`: Footer content
|
|
563
|
-
|
|
564
|
-
### Navbar (built-in links)
|
|
565
|
-
|
|
566
|
-
Use `items` for a typed link list, or pass a `nav` snippet for custom markup. Set `embedded` when you only need the link row inside your own header.
|
|
567
|
-
|
|
568
|
-
```svelte
|
|
569
|
-
<Navbar
|
|
570
|
-
brand="My App"
|
|
571
|
-
brandHref="/"
|
|
572
|
-
items={navItems}
|
|
573
|
-
navVariant="header"
|
|
574
|
-
currentPath="/about"
|
|
575
|
-
onclick={(e) => console.log('Nav link', e)}
|
|
576
|
-
/>
|
|
577
|
-
|
|
578
|
-
<Navbar embedded ariaLabel="Primary" navVariant="header" items={navItems} />
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
**Props (selected):**
|
|
582
|
-
- `items`: `NavbarNavItem[]` — `label`, `href`, optional `icon` / `iconFilled`
|
|
583
|
-
- `navVariant`: `"header"` | `"sidebar"` — layout for built-in links
|
|
584
|
-
- `embedded`: render link list (or `nav` snippet) without full chrome
|
|
585
|
-
- `currentPath`: active item for built-in links
|
|
586
|
-
- `preventNavigation`: call `preventDefault` on link clicks
|
|
587
|
-
- `onclick`: invoked when a built-in link is clicked
|
|
588
|
-
|
|
589
|
-
### NavigationMenu Component
|
|
590
|
-
|
|
591
|
-
Advanced navigation menu system with compound components:
|
|
74
|
+
**Composable (advanced)** — You assemble **nested components** when layout and snippets matter more than a single props object.
|
|
592
75
|
|
|
593
76
|
```svelte
|
|
594
77
|
<script lang="ts">
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
78
|
+
import {
|
|
79
|
+
Card,
|
|
80
|
+
CardHeader,
|
|
81
|
+
CardContent,
|
|
82
|
+
NavigationMenu,
|
|
83
|
+
NavigationMenuList,
|
|
84
|
+
NavigationMenuItem,
|
|
85
|
+
NavigationMenuTrigger,
|
|
86
|
+
} from "zabi-components";
|
|
603
87
|
</script>
|
|
604
88
|
|
|
605
|
-
<
|
|
606
|
-
|
|
607
|
-
<
|
|
608
|
-
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
|
|
609
|
-
<NavigationMenuContent>
|
|
610
|
-
<NavigationMenuLink href="/products/web">Web</NavigationMenuLink>
|
|
611
|
-
<NavigationMenuLink href="/products/mobile">Mobile</NavigationMenuLink>
|
|
612
|
-
</NavigationMenuContent>
|
|
613
|
-
</NavigationMenuItem>
|
|
614
|
-
<NavigationMenuItem>
|
|
615
|
-
<NavigationMenuLink href="/about">About</NavigationMenuLink>
|
|
616
|
-
</NavigationMenuItem>
|
|
617
|
-
</NavigationMenuList>
|
|
618
|
-
</NavigationMenu>
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
**Compound Components:**
|
|
622
|
-
- `NavigationMenu`: Root container
|
|
623
|
-
- `NavigationMenuList`: List wrapper
|
|
624
|
-
- `NavigationMenuItem`: Individual menu item
|
|
625
|
-
- `NavigationMenuTrigger`: Trigger for dropdown items
|
|
626
|
-
- `NavigationMenuContent`: Dropdown content container
|
|
627
|
-
- `NavigationMenuLink`: Navigation link component
|
|
628
|
-
|
|
629
|
-
**Features:**
|
|
630
|
-
- Keyboard navigation support
|
|
631
|
-
- ARIA compliant
|
|
632
|
-
- CSS-only positioning
|
|
633
|
-
- Responsive design
|
|
634
|
-
|
|
635
|
-
### Section Component
|
|
636
|
-
|
|
637
|
-
```svelte
|
|
638
|
-
<Section
|
|
639
|
-
variant="default" | "primary" | "secondary"
|
|
640
|
-
size="sm" | "md" | "lg" | "xl"
|
|
641
|
-
className={string}
|
|
642
|
-
>
|
|
643
|
-
Section content
|
|
644
|
-
</Section>
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
**Props:**
|
|
648
|
-
- `variant`: Section style variant (default: "default")
|
|
649
|
-
- `size`: Section size (default: "md")
|
|
650
|
-
- `className`: Additional CSS classes
|
|
651
|
-
|
|
652
|
-
### Button Component
|
|
653
|
-
|
|
654
|
-
```svelte
|
|
655
|
-
<Button
|
|
656
|
-
variant="primary" | "secondary" | "danger" | "ghost" | "outline" | "link"
|
|
657
|
-
size="sm" | "md" | "lg"
|
|
658
|
-
disabled={boolean}
|
|
659
|
-
type="button" | "submit" | "reset"
|
|
660
|
-
className={string}
|
|
661
|
-
onclick={(e) => console.log('Button clicked')}
|
|
662
|
-
>
|
|
663
|
-
Button Content
|
|
664
|
-
</Button>
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
**Props:**
|
|
668
|
-
- `variant`: Button style variant (default: "primary")
|
|
669
|
-
- `primary`: Primary action button with solid background
|
|
670
|
-
- `secondary`: Secondary action button
|
|
671
|
-
- `danger`: Destructive action button (red)
|
|
672
|
-
- `ghost`: Transparent button with hover effect
|
|
673
|
-
- `outline`: Outlined button with border
|
|
674
|
-
- `link`: Text button styled as a link
|
|
675
|
-
- `size`: Button size (default: "md")
|
|
676
|
-
- `disabled`: Disable the button (default: false)
|
|
677
|
-
- `type`: HTML button type (default: "button")
|
|
678
|
-
- `className`: Additional CSS classes
|
|
679
|
-
|
|
680
|
-
**Events:**
|
|
681
|
-
- `onclick`: Native click event - `MouseEvent`
|
|
682
|
-
|
|
683
|
-
### IconButton Component
|
|
684
|
-
|
|
685
|
-
```svelte
|
|
686
|
-
<IconButton
|
|
687
|
-
variant="primary" | "secondary" | "danger" | "ghost" | "outline" | "link"
|
|
688
|
-
size="sm" | "md" | "lg"
|
|
689
|
-
disabled={boolean}
|
|
690
|
-
type="button" | "submit" | "reset"
|
|
691
|
-
label={string}
|
|
692
|
-
onclick={(e) => console.log('Icon clicked', e)}
|
|
693
|
-
>
|
|
694
|
-
<!-- Icon content -->
|
|
695
|
-
</IconButton>
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
**Props:**
|
|
699
|
-
- `variant`: Icon button style (default: "primary")
|
|
700
|
-
- `size`: Icon button size (default: "md")
|
|
701
|
-
- `disabled`: Disable the button (default: false)
|
|
702
|
-
- `type`: HTML button type (default: "button")
|
|
703
|
-
- `label`: Accessible label for screen readers
|
|
704
|
-
|
|
705
|
-
**Events:**
|
|
706
|
-
- `onclick`: Native click event - `MouseEvent`
|
|
707
|
-
|
|
708
|
-
### Input Component
|
|
709
|
-
|
|
710
|
-
```svelte
|
|
711
|
-
<Input
|
|
712
|
-
value={string}
|
|
713
|
-
type={string}
|
|
714
|
-
label={string}
|
|
715
|
-
placeholder={string}
|
|
716
|
-
disabled={boolean}
|
|
717
|
-
size="sm" | "md" | "lg"
|
|
718
|
-
variant="default" | "success" | "warning" | "error"
|
|
719
|
-
className={string}
|
|
720
|
-
on:input={(e) => console.log('Input changed:', e.target.value)}
|
|
721
|
-
on:change={(e) => console.log('Input changed:', e.target.value)}
|
|
722
|
-
/>
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
**Props:**
|
|
726
|
-
- `value`: Input value (controlled)
|
|
727
|
-
- `type`: Input type (default: "text")
|
|
728
|
-
- `label`: Input label
|
|
729
|
-
- `placeholder`: Input placeholder
|
|
730
|
-
- `disabled`: Disable the input (default: false)
|
|
731
|
-
- `size`: Input size (default: "md")
|
|
732
|
-
- `variant`: Input variant with semantic colors (default: "default")
|
|
733
|
-
- `className`: Additional CSS classes
|
|
734
|
-
|
|
735
|
-
**Events:**
|
|
736
|
-
- `input`: Native input event - `InputEvent`
|
|
737
|
-
- `change`: Native change event - `Event`
|
|
738
|
-
|
|
739
|
-
### Textarea Component
|
|
740
|
-
|
|
741
|
-
```svelte
|
|
742
|
-
<Textarea
|
|
743
|
-
value={string}
|
|
744
|
-
label={string}
|
|
745
|
-
placeholder={string}
|
|
746
|
-
disabled={boolean}
|
|
747
|
-
rows={number}
|
|
748
|
-
size="sm" | "md" | "lg"
|
|
749
|
-
variant="default" | "success" | "warning" | "error"
|
|
750
|
-
className={string}
|
|
751
|
-
on:input={(e) => console.log('Textarea changed:', e.target.value)}
|
|
752
|
-
on:change={(e) => console.log('Textarea changed:', e.target.value)}
|
|
753
|
-
/>
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
**Props:**
|
|
757
|
-
- `value`: Textarea value (controlled)
|
|
758
|
-
- `label`: Textarea label
|
|
759
|
-
- `placeholder`: Textarea placeholder
|
|
760
|
-
- `disabled`: Disable the textarea (default: false)
|
|
761
|
-
- `rows`: Number of visible text lines (default: 4)
|
|
762
|
-
- `size`: Textarea size (default: "md")
|
|
763
|
-
- `variant`: Textarea variant with semantic colors (default: "default")
|
|
764
|
-
- `className`: Additional CSS classes
|
|
765
|
-
|
|
766
|
-
**Events:**
|
|
767
|
-
- `input`: Native input event - `InputEvent`
|
|
768
|
-
- `change`: Native change event - `Event`
|
|
769
|
-
|
|
770
|
-
### Card Component
|
|
771
|
-
|
|
772
|
-
Card component supports two APIs: prop-based (backward compatible) and compound components (recommended).
|
|
773
|
-
|
|
774
|
-
#### Prop-based API (Backward Compatible)
|
|
775
|
-
|
|
776
|
-
```svelte
|
|
777
|
-
<Card
|
|
778
|
-
title="Card Title"
|
|
779
|
-
description="Card description"
|
|
780
|
-
image="image-url.jpg"
|
|
781
|
-
variant="default" | "elevated" | "outlined" | "flat"
|
|
782
|
-
size="sm" | "md" | "lg"
|
|
783
|
-
fullWidth={boolean}
|
|
784
|
-
onclick={(e) => console.log('Card clicked', e)}
|
|
785
|
-
>
|
|
786
|
-
Additional Content
|
|
787
|
-
</Card>
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
#### Compound Component API (Recommended)
|
|
791
|
-
|
|
792
|
-
```svelte
|
|
793
|
-
<Card variant="elevated">
|
|
794
|
-
<CardHeader>
|
|
795
|
-
<CardTitle>Card Title</CardTitle>
|
|
796
|
-
<CardDescription>Card description</CardDescription>
|
|
797
|
-
</CardHeader>
|
|
798
|
-
<CardContent>
|
|
799
|
-
Main content goes here
|
|
800
|
-
</CardContent>
|
|
801
|
-
<CardFooter>
|
|
802
|
-
<Button variant="primary">Action</Button>
|
|
803
|
-
</CardFooter>
|
|
89
|
+
<Card>
|
|
90
|
+
<CardHeader title="Title" />
|
|
91
|
+
<CardContent><p class="text-body">Main content.</p></CardContent>
|
|
804
92
|
</Card>
|
|
805
|
-
```
|
|
806
|
-
|
|
807
|
-
**Props:**
|
|
808
|
-
- `variant`: Card style variant (default: "default")
|
|
809
|
-
- `default`: Standard card with subtle shadow
|
|
810
|
-
- `elevated`: Card with stronger shadow (elevated appearance)
|
|
811
|
-
- `outlined`: Card with border, no shadow
|
|
812
|
-
- `flat`: Card with no shadow, no border (minimal)
|
|
813
|
-
- `size`: Card size (default: "md")
|
|
814
|
-
- `fullWidth`: Whether card takes full width (default: true)
|
|
815
|
-
- `title`: Card title (old API)
|
|
816
|
-
- `description`: Card description (old API)
|
|
817
|
-
- `image`: Card image URL (old API)
|
|
818
|
-
- `onclick`: Click handler for interactive cards
|
|
819
|
-
|
|
820
|
-
**Compound Components:**
|
|
821
|
-
- `CardHeader`: Header section container
|
|
822
|
-
- `CardContent`: Main content area
|
|
823
|
-
- `CardFooter`: Footer section container
|
|
824
|
-
- `CardTitle`: Title component (supports heading levels 1-6)
|
|
825
|
-
- `CardDescription`: Description text component
|
|
826
|
-
|
|
827
|
-
**Events:**
|
|
828
|
-
- `onclick`: Native click event - `MouseEvent`
|
|
829
|
-
|
|
830
|
-
**Keyboard Navigation (Interactive Cards):**
|
|
831
|
-
- **Tab**: Focus the card
|
|
832
|
-
- **Enter / Space**: Activate card click handler
|
|
833
|
-
|
|
834
|
-
### Alert Component
|
|
835
|
-
|
|
836
|
-
```svelte
|
|
837
|
-
<Alert
|
|
838
|
-
variant="info" | "success" | "warning" | "error"
|
|
839
|
-
title={string}
|
|
840
|
-
message={string}
|
|
841
|
-
closable={boolean}
|
|
842
|
-
className={string}
|
|
843
|
-
onclick={(e) => console.log('Alert closed', e)}
|
|
844
|
-
>
|
|
845
|
-
Custom Alert Content
|
|
846
|
-
</Alert>
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
**Props:**
|
|
850
|
-
- `variant`: Alert type (default: "info")
|
|
851
|
-
- `title`: Alert title
|
|
852
|
-
- `message`: Alert message
|
|
853
|
-
- `closable`: Show close button (default: false)
|
|
854
|
-
- `className`: Additional CSS classes
|
|
855
|
-
|
|
856
|
-
**Slots:**
|
|
857
|
-
- `default`: Custom alert content (overrides message prop)
|
|
858
|
-
|
|
859
|
-
**Events:**
|
|
860
|
-
- `close`: Fired when alert is closed - `{ detail: { value: true, event: Event } }`
|
|
861
|
-
|
|
862
|
-
## Modern CSS Features
|
|
863
|
-
|
|
864
|
-
Zabi Components leverages modern CSS features for better performance and developer experience:
|
|
865
|
-
|
|
866
|
-
### Container Queries
|
|
867
|
-
```css
|
|
868
|
-
.card {
|
|
869
|
-
container-type: inline-size;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
@container (min-width: 300px) {
|
|
873
|
-
.card-content {
|
|
874
|
-
display: flex;
|
|
875
|
-
flex-direction: row;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
```
|
|
879
|
-
|
|
880
|
-
### CSS Grid & Subgrid
|
|
881
|
-
```css
|
|
882
|
-
.layout-grid {
|
|
883
|
-
display: grid;
|
|
884
|
-
grid-template-columns: subgrid;
|
|
885
|
-
gap: 1rem;
|
|
886
|
-
}
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
### CSS Logical Properties
|
|
890
|
-
```css
|
|
891
|
-
.component {
|
|
892
|
-
margin-inline-start: 1rem;
|
|
893
|
-
padding-block: 0.5rem;
|
|
894
|
-
border-inline-end: 1px solid;
|
|
895
|
-
}
|
|
896
|
-
```
|
|
897
93
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
```
|
|
908
|
-
|
|
909
|
-
## Theming
|
|
910
|
-
|
|
911
|
-
Zabi Components uses CSS custom properties and Tailwind CSS for easy theming. You can customize the appearance by overriding these variables:
|
|
912
|
-
|
|
913
|
-
```css
|
|
914
|
-
:root {
|
|
915
|
-
/* Primary Colors */
|
|
916
|
-
--zabi-primary: theme('colors.blue.600');
|
|
917
|
-
--zabi-primary-hover: theme('colors.blue.700');
|
|
918
|
-
--zabi-primary-active: theme('colors.blue.800');
|
|
919
|
-
|
|
920
|
-
/* Secondary Colors */
|
|
921
|
-
--zabi-secondary: theme('colors.stone.600');
|
|
922
|
-
--zabi-secondary-hover: theme('colors.stone.700');
|
|
923
|
-
|
|
924
|
-
/* Success Colors */
|
|
925
|
-
--zabi-success: theme('colors.green.600');
|
|
926
|
-
--zabi-success-hover: theme('colors.green.700');
|
|
927
|
-
|
|
928
|
-
/* Warning Colors */
|
|
929
|
-
--zabi-warning: theme('colors.yellow.600');
|
|
930
|
-
--zabi-warning-hover: theme('colors.yellow.700');
|
|
931
|
-
|
|
932
|
-
/* Error Colors */
|
|
933
|
-
--zabi-error: theme('colors.red.600');
|
|
934
|
-
--zabi-error-hover: theme('colors.red.700');
|
|
935
|
-
|
|
936
|
-
/* Info Colors */
|
|
937
|
-
--zabi-info: theme('colors.blue.600');
|
|
938
|
-
--zabi-info-hover: theme('colors.blue.700');
|
|
939
|
-
|
|
940
|
-
/* Surface Colors */
|
|
941
|
-
--zabi-surface: theme('colors.white');
|
|
942
|
-
--zabi-surface-hover: theme('colors.stone.50');
|
|
943
|
-
|
|
944
|
-
/* Border Colors */
|
|
945
|
-
--zabi-border: theme('colors.stone.300');
|
|
946
|
-
--zabi-border-hover: theme('colors.stone.400');
|
|
947
|
-
--zabi-border-focus: theme('colors.blue.500');
|
|
948
|
-
|
|
949
|
-
/* Text Colors */
|
|
950
|
-
--zabi-text: theme('colors.stone.900');
|
|
951
|
-
--zabi-text-muted: theme('colors.stone.600');
|
|
952
|
-
--zabi-text-placeholder: theme('colors.stone.400');
|
|
953
|
-
--zabi-text-inverse: theme('colors.white');
|
|
954
|
-
}
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
### Dark Mode
|
|
958
|
-
|
|
959
|
-
Dark mode is automatically supported through CSS custom properties:
|
|
960
|
-
|
|
961
|
-
```css
|
|
962
|
-
.dark {
|
|
963
|
-
--zabi-surface: theme('colors.stone.800');
|
|
964
|
-
--zabi-surface-hover: theme('colors.stone.700');
|
|
965
|
-
--zabi-text: theme('colors.stone.200');
|
|
966
|
-
--zabi-text-muted: theme('colors.stone.500');
|
|
967
|
-
--zabi-border: theme('colors.stone.600');
|
|
968
|
-
}
|
|
94
|
+
<NavigationMenu>
|
|
95
|
+
<NavigationMenuList>
|
|
96
|
+
<NavigationMenuItem>
|
|
97
|
+
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
|
|
98
|
+
</NavigationMenuItem>
|
|
99
|
+
</NavigationMenuList>
|
|
100
|
+
</NavigationMenu>
|
|
969
101
|
```
|
|
970
102
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
Zabi Components now use **native DOM events** with **event forwarding** for maximum compatibility across frameworks.
|
|
103
|
+
Wrong mental model: treating composable roots as optional — see **Common pitfalls**.
|
|
974
104
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
All components use `{...$$restProps}` to forward native DOM events, making them compatible with React, Vue, Svelte, and vanilla JavaScript.
|
|
105
|
+
---
|
|
978
106
|
|
|
979
|
-
|
|
980
|
-
<!-- All these work the same way -->
|
|
981
|
-
<Button on:click={handleClick}>Click me</Button>
|
|
982
|
-
<Input on:input={handleInput} on:change={handleChange} />
|
|
983
|
-
<Modal on:click={handleClose} on:keydown={handleKeydown} />
|
|
984
|
-
```
|
|
107
|
+
## Pattern examples
|
|
985
108
|
|
|
986
|
-
|
|
109
|
+
Components use **DOM-style props** (`onclick`, `oninput`), not legacy `on:click`.
|
|
987
110
|
|
|
988
|
-
|
|
111
|
+
**List** — data + optional selection:
|
|
989
112
|
|
|
990
113
|
```svelte
|
|
991
114
|
<script lang="ts">
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
function handleInput(event: Event) {
|
|
996
|
-
inputValue = (event.target as HTMLInputElement).value;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
function handleCheckbox(event: Event) {
|
|
1000
|
-
checkboxValue = (event.target as HTMLInputElement).checked;
|
|
1001
|
-
}
|
|
1002
|
-
</script>
|
|
1003
|
-
|
|
1004
|
-
<Input
|
|
1005
|
-
value={inputValue}
|
|
1006
|
-
on:input={handleInput}
|
|
1007
|
-
label="Name"
|
|
1008
|
-
/>
|
|
1009
|
-
<Checkbox
|
|
1010
|
-
checked={checkboxValue}
|
|
1011
|
-
on:change={handleCheckbox}
|
|
1012
|
-
label="Subscribe"
|
|
1013
|
-
/>
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
### Migration from Previous Versions
|
|
1017
|
-
|
|
1018
|
-
**Before (v2.0.x):**
|
|
1019
|
-
```svelte
|
|
1020
|
-
<Input bind:value={inputValue} />
|
|
1021
|
-
<Button on:click={handleClick}>Click</Button>
|
|
1022
|
-
<Modal bind:isOpen onclick={handleClose} />
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
**After (v2.1.x):**
|
|
1026
|
-
```svelte
|
|
1027
|
-
<Input value={inputValue} on:input={(e) => inputValue = e.target.value} />
|
|
1028
|
-
<Button on:click={handleClick}>Click</Button>
|
|
1029
|
-
<Modal bind:isOpen on:click={handleClose} />
|
|
1030
|
-
```
|
|
1031
|
-
|
|
1032
|
-
### Benefits
|
|
1033
|
-
|
|
1034
|
-
- ✅ **SSR Safe**: No more hydration errors in production
|
|
1035
|
-
- ✅ **Cross-Framework**: Works in React, Vue, Svelte, vanilla JS
|
|
1036
|
-
- ✅ **Standards Compliant**: Uses native DOM events
|
|
1037
|
-
- ✅ **Better Performance**: Reduced JavaScript overhead
|
|
1038
|
-
|
|
1039
|
-
## TypeScript Support
|
|
1040
|
-
|
|
1041
|
-
Full TypeScript definitions are included with comprehensive type safety:
|
|
1042
|
-
|
|
1043
|
-
### Event Types
|
|
115
|
+
import { List } from "zabi-components"; // or …/atoms
|
|
116
|
+
import { Home } from "@lucide/svelte";
|
|
1044
117
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
console.log('Button clicked:', event);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
function handleInputChange(event: Event) {
|
|
1054
|
-
const target = event.target as HTMLInputElement;
|
|
1055
|
-
console.log('Input value:', target.value);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
function handleFormSubmit(event: SubmitEvent) {
|
|
1059
|
-
const formData = new FormData(event.target as HTMLFormElement);
|
|
1060
|
-
const data = Object.fromEntries(formData.entries());
|
|
1061
|
-
console.log('Form data:', data);
|
|
1062
|
-
}
|
|
1063
|
-
```
|
|
118
|
+
const items = [
|
|
119
|
+
{ id: "1", label: "Home", icon: Home },
|
|
120
|
+
{ id: "2", label: "Settings", href: "/settings" },
|
|
121
|
+
];
|
|
122
|
+
</script>
|
|
1064
123
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
All components have proper prop typing with event forwarding:
|
|
1068
|
-
|
|
1069
|
-
```typescript
|
|
1070
|
-
// All components support event forwarding
|
|
1071
|
-
interface ButtonProps {
|
|
1072
|
-
variant?: "primary" | "secondary" | "danger" | "ghost" | "outline" | "link";
|
|
1073
|
-
size?: "sm" | "md" | "lg";
|
|
1074
|
-
disabled?: boolean;
|
|
1075
|
-
type?: "button" | "submit" | "reset";
|
|
1076
|
-
className?: string;
|
|
1077
|
-
// All native button events are forwarded via {...$$restProps}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
interface InputProps {
|
|
1081
|
-
value?: string;
|
|
1082
|
-
type?: string;
|
|
1083
|
-
label?: string;
|
|
1084
|
-
placeholder?: string;
|
|
1085
|
-
disabled?: boolean;
|
|
1086
|
-
size?: "sm" | "md" | "lg";
|
|
1087
|
-
variant?: "default" | "success" | "warning" | "error";
|
|
1088
|
-
className?: string;
|
|
1089
|
-
// All native input events are forwarded via {...$$restProps}
|
|
1090
|
-
}
|
|
124
|
+
<List items={items} selectedId="1" ariaLabel="Navigation" />
|
|
1091
125
|
```
|
|
1092
126
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
Zabi Components is **100% SSR-safe** and fully compatible with SvelteKit's server-side rendering. All components have been thoroughly tested to eliminate runtime errors and ensure proper hydration.
|
|
1096
|
-
|
|
1097
|
-
### ✅ **Zero Runtime Errors**
|
|
1098
|
-
|
|
1099
|
-
All components are designed to work seamlessly in both server and client environments:
|
|
1100
|
-
|
|
1101
|
-
- **No "Cannot read properties of null" errors**
|
|
1102
|
-
- **Proper hydration without mismatches**
|
|
1103
|
-
- **Safe browser API access**
|
|
1104
|
-
- **Consistent behavior across SSR and client**
|
|
1105
|
-
|
|
1106
|
-
### 🛡️ **SSR-Safe Patterns**
|
|
1107
|
-
|
|
1108
|
-
Components use consistent SSR-safe patterns:
|
|
127
|
+
**Modal** — `bind:isOpen`, body in the default slot, optional `{#snippet footer()}`:
|
|
1109
128
|
|
|
1110
129
|
```svelte
|
|
1111
130
|
<script lang="ts">
|
|
1112
|
-
|
|
1113
|
-
import { safeLocalStorage, safeDocument } from "zabi-components/lib/ssr-safe";
|
|
1114
|
-
|
|
1115
|
-
let mounted = $state(false);
|
|
1116
|
-
|
|
1117
|
-
onMount(() => {
|
|
1118
|
-
mounted = true;
|
|
1119
|
-
// Safe browser API access after mount
|
|
1120
|
-
const storage = safeLocalStorage();
|
|
1121
|
-
if (storage) {
|
|
1122
|
-
// Use localStorage safely
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1125
|
-
</script>
|
|
1126
|
-
```
|
|
131
|
+
import { Modal, Button } from "zabi-components";
|
|
1127
132
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
Perfect integration with SvelteKit features:
|
|
1131
|
-
|
|
1132
|
-
```svelte
|
|
1133
|
-
<script lang="ts">
|
|
1134
|
-
import { goto } from '$app/navigation';
|
|
1135
|
-
import { Button } from 'zabi-components';
|
|
133
|
+
let open = $state(false);
|
|
1136
134
|
</script>
|
|
1137
135
|
|
|
1138
|
-
|
|
1139
|
-
<Button onclick={() => goto('/about')}>
|
|
1140
|
-
Go to About
|
|
1141
|
-
</Button>
|
|
1142
|
-
```
|
|
1143
|
-
|
|
1144
|
-
### 🎯 **Key SSR Features**
|
|
1145
|
-
|
|
1146
|
-
- **Safe ID Generation**: Prevents hydration mismatches
|
|
1147
|
-
- **Browser API Guards**: All browser APIs are safely accessed
|
|
1148
|
-
- **Consistent State**: Server and client state remain synchronized
|
|
1149
|
-
- **Theme Persistence**: Dark mode works correctly on first load
|
|
1150
|
-
- **Form Stability**: Input IDs remain consistent across hydration
|
|
1151
|
-
|
|
1152
|
-
### 📋 **SSR Best Practices**
|
|
1153
|
-
|
|
1154
|
-
1. **Use SvelteKit Navigation**: Always use `goto()` instead of `window.location.href`
|
|
1155
|
-
2. **Safe Browser Access**: Use provided SSR-safe utilities
|
|
1156
|
-
3. **Consistent IDs**: Let components handle ID generation internally
|
|
1157
|
-
4. **Theme Loading**: Components handle theme loading automatically
|
|
136
|
+
<Button onclick={() => (open = true)}>Open</Button>
|
|
1158
137
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
size="lg"
|
|
1167
|
-
className="w-full shadow-lg hover:shadow-xl"
|
|
1168
|
-
on:click={(e) => console.log('Clicked!', e)}
|
|
1169
|
-
>
|
|
1170
|
-
Custom Button
|
|
1171
|
-
</Button>
|
|
138
|
+
<Modal bind:isOpen={open} title="Confirm">
|
|
139
|
+
<p class="text-description">Continue?</p>
|
|
140
|
+
{#snippet footer()}
|
|
141
|
+
<Button variant="secondary" onclick={() => (open = false)}>Cancel</Button>
|
|
142
|
+
<Button onclick={() => (open = false)}>OK</Button>
|
|
143
|
+
{/snippet}
|
|
144
|
+
</Modal>
|
|
1172
145
|
```
|
|
1173
146
|
|
|
1174
|
-
|
|
147
|
+
**RadioGroup** — `bind:value` or `defaultValue`:
|
|
1175
148
|
|
|
1176
149
|
```svelte
|
|
1177
150
|
<script lang="ts">
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
interface FormData {
|
|
1181
|
-
name: string;
|
|
1182
|
-
email: string;
|
|
1183
|
-
message: string;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
interface FormErrors {
|
|
1187
|
-
name?: string;
|
|
1188
|
-
email?: string;
|
|
1189
|
-
message?: string;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
let formData: FormData = {
|
|
1193
|
-
name: '',
|
|
1194
|
-
email: '',
|
|
1195
|
-
message: ''
|
|
1196
|
-
};
|
|
1197
|
-
|
|
1198
|
-
let errors: FormErrors = {};
|
|
1199
|
-
let showSuccess = false;
|
|
1200
|
-
|
|
1201
|
-
function validateForm(): boolean {
|
|
1202
|
-
errors = {};
|
|
1203
|
-
if (!formData.name) errors.name = 'Name is required';
|
|
1204
|
-
if (!formData.email) errors.email = 'Email is required';
|
|
1205
|
-
return Object.keys(errors).length === 0;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
function handleSubmit(event: SubmitEvent) {
|
|
1209
|
-
if (validateForm()) {
|
|
1210
|
-
showSuccess = true;
|
|
1211
|
-
// Submit form
|
|
1212
|
-
console.log('Form submitted:', formData);
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
</script>
|
|
1216
|
-
|
|
1217
|
-
<Card>
|
|
1218
|
-
<div slot="header">
|
|
1219
|
-
<h2>Contact Form</h2>
|
|
1220
|
-
</div>
|
|
1221
|
-
|
|
1222
|
-
<form on:submit|preventDefault={handleSubmit}>
|
|
1223
|
-
<Input
|
|
1224
|
-
value={formData.name}
|
|
1225
|
-
on:input={(e) => formData.name = e.target.value}
|
|
1226
|
-
label="Name"
|
|
1227
|
-
placeholder="Enter your name"
|
|
1228
|
-
required
|
|
1229
|
-
/>
|
|
1230
|
-
|
|
1231
|
-
<Input
|
|
1232
|
-
value={formData.email}
|
|
1233
|
-
type="email"
|
|
1234
|
-
on:input={(e) => formData.email = e.target.value}
|
|
1235
|
-
label="Email"
|
|
1236
|
-
placeholder="Enter your email"
|
|
1237
|
-
required
|
|
1238
|
-
/>
|
|
1239
|
-
|
|
1240
|
-
<Input
|
|
1241
|
-
value={formData.message}
|
|
1242
|
-
on:input={(e) => formData.message = e.target.value}
|
|
1243
|
-
label="Message"
|
|
1244
|
-
placeholder="Enter your message"
|
|
1245
|
-
/>
|
|
1246
|
-
|
|
1247
|
-
<Button type="submit" variant="primary">
|
|
1248
|
-
Send Message
|
|
1249
|
-
</Button>
|
|
1250
|
-
</form>
|
|
1251
|
-
|
|
1252
|
-
<div slot="footer">
|
|
1253
|
-
{#if showSuccess}
|
|
1254
|
-
<Alert variant="success" closable>
|
|
1255
|
-
Message sent successfully!
|
|
1256
|
-
</Alert>
|
|
1257
|
-
{/if}
|
|
1258
|
-
</div>
|
|
1259
|
-
</Card>
|
|
1260
|
-
```
|
|
1261
|
-
|
|
1262
|
-
## Contributing
|
|
1263
|
-
|
|
1264
|
-
1. Fork the repository
|
|
1265
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
1266
|
-
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
1267
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1268
|
-
5. Open a Pull Request
|
|
1269
|
-
|
|
1270
|
-
## License
|
|
151
|
+
import { RadioGroup } from "zabi-components";
|
|
1271
152
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
## Changelog
|
|
1275
|
-
|
|
1276
|
-
### v5.0.21 (Latest) - Current Version
|
|
1277
|
-
|
|
1278
|
-
See [CHANGELOG.md](./CHANGELOG.md) for the complete changelog.
|
|
1279
|
-
|
|
1280
|
-
### v5.0.18 - "Enhanced Components" Edition
|
|
1281
|
-
|
|
1282
|
-
#### ✨ **Component Enhancements**
|
|
1283
|
-
- **Card Compound Components**: Added CardHeader, CardContent, CardFooter, CardTitle, and CardDescription
|
|
1284
|
-
- **Button Variants**: Added ghost, outline, and link variants
|
|
1285
|
-
- **Badge Updates**: Added icon support and enhanced size variants
|
|
1286
|
-
- **NavigationMenu**: Complete navigation menu system with compound components
|
|
1287
|
-
- **Section Component**: New responsive section wrapper component
|
|
1288
|
-
- **Accessibility**: Enhanced focus management and keyboard navigation across all components
|
|
1289
|
-
|
|
1290
|
-
### v4.0.0 - "Svelte 5 Runes" Edition
|
|
1291
|
-
|
|
1292
|
-
#### 🚀 **MAJOR UPDATE** - Svelte 5 Migration
|
|
1293
|
-
|
|
1294
|
-
This version migrates to Svelte 5 with runes syntax for better performance and developer experience.
|
|
1295
|
-
|
|
1296
|
-
#### ✅ **What's New**
|
|
1297
|
-
- **Svelte 5 Runes**: All components now use `$props`, `$derived`, and `$state`
|
|
1298
|
-
- **Better Performance**: Improved reactivity and reduced bundle size
|
|
1299
|
-
- **Enhanced TypeScript**: Better type inference and safety
|
|
1300
|
-
- **Modern Syntax**: Cleaner, more intuitive component APIs
|
|
1301
|
-
|
|
1302
|
-
#### 🔄 **Migration Required**
|
|
1303
|
-
- **Svelte Version**: Requires Svelte 5.43.8 or later
|
|
1304
|
-
- **Syntax Updates**: Components now use runes syntax
|
|
1305
|
-
- **Event Handling**: Updated to use `oninput`, `onclick` instead of `on:input`, `on:click`
|
|
1306
|
-
|
|
1307
|
-
#### 📋 **Breaking Changes**
|
|
1308
|
-
1. **Svelte Version**: Must upgrade to Svelte 5.43.8+
|
|
1309
|
-
2. **Event Syntax**: Use `oninput`, `onclick` instead of `on:input`, `on:click`
|
|
1310
|
-
3. **Component Props**: All components use Svelte 5 runes syntax
|
|
1311
|
-
|
|
1312
|
-
### v2.1.0 - "Cross-Framework Compatible" Edition
|
|
1313
|
-
|
|
1314
|
-
#### 🚀 **MAJOR BREAKING CHANGES** - Event Handling Overhaul
|
|
1315
|
-
|
|
1316
|
-
This is a **major breaking change** that affects how all components handle events. The refactoring eliminates SSR/production errors and makes components compatible with React, Vue, and vanilla JavaScript applications.
|
|
1317
|
-
|
|
1318
|
-
#### ✅ **What's Fixed**
|
|
1319
|
-
- **SSR/Production Errors**: Eliminated all `createEventDispatcher` hydration errors
|
|
1320
|
-
- **Cross-Framework Compatibility**: Components now work in React, Vue, Svelte, and vanilla JS
|
|
1321
|
-
- **Event Forwarding**: All components use `{...$$restProps}` for native DOM events
|
|
1322
|
-
- **Standards Compliance**: Uses web standards instead of framework-specific events
|
|
1323
|
-
|
|
1324
|
-
#### 🔄 **Migration Required**
|
|
1325
|
-
- **Form Components**: Replace `bind:value` with `value` prop + event handlers
|
|
1326
|
-
- **Event Names**: Some custom events replaced with native DOM events
|
|
1327
|
-
- **Event Structure**: Simplified event structures across all components
|
|
1328
|
-
|
|
1329
|
-
#### 📋 **Breaking Changes**
|
|
1330
|
-
1. **Form Components**: Must use `value` prop + event handlers instead of `bind:value`
|
|
1331
|
-
2. **Event Names**: Some custom events replaced with native DOM events
|
|
1332
|
-
3. **Event Structure**: Simplified event structures across all components
|
|
1333
|
-
4. **SSR Compatibility**: Components now work correctly in SSR environments
|
|
1334
|
-
|
|
1335
|
-
See the [Migration Guide](#migration-from-previous-versions) above for detailed examples.
|
|
1336
|
-
|
|
1337
|
-
### v2.0.2 — streamlined components
|
|
1338
|
-
|
|
1339
|
-
## Development Setup
|
|
1340
|
-
|
|
1341
|
-
### Prerequisites
|
|
1342
|
-
- Node.js 18+
|
|
1343
|
-
- npm or yarn
|
|
1344
|
-
- Svelte 5.43.8+ or SvelteKit 2+
|
|
1345
|
-
|
|
1346
|
-
### Local Development
|
|
1347
|
-
```bash
|
|
1348
|
-
# Clone the repository
|
|
1349
|
-
git clone https://github.com/sabnor/zabi-components.git
|
|
1350
|
-
cd zabi-components
|
|
1351
|
-
|
|
1352
|
-
# Install dependencies
|
|
1353
|
-
npm install
|
|
1354
|
-
|
|
1355
|
-
# Start development server
|
|
1356
|
-
npm run dev
|
|
1357
|
-
|
|
1358
|
-
# Build library
|
|
1359
|
-
npm run build
|
|
1360
|
-
|
|
1361
|
-
# Run type checking
|
|
1362
|
-
npm run check
|
|
1363
|
-
|
|
1364
|
-
# Run tests
|
|
1365
|
-
npm run test
|
|
1366
|
-
```
|
|
1367
|
-
|
|
1368
|
-
### Project Structure
|
|
1369
|
-
```
|
|
1370
|
-
src/
|
|
1371
|
-
├── components/
|
|
1372
|
-
│ ├── atoms/ # Basic UI components
|
|
1373
|
-
│ ├── molecules/ # Composite components
|
|
1374
|
-
│ └── organisms/ # Complex components
|
|
1375
|
-
├── types/ # TypeScript definitions
|
|
1376
|
-
├── styles/ # CSS and styling
|
|
1377
|
-
└── lib/ # Library exports
|
|
1378
|
-
```
|
|
1379
|
-
|
|
1380
|
-
## Troubleshooting
|
|
1381
|
-
|
|
1382
|
-
### Common Issues
|
|
1383
|
-
|
|
1384
|
-
#### TypeScript Errors
|
|
1385
|
-
If you see TypeScript errors with event handlers, ensure proper typing:
|
|
1386
|
-
|
|
1387
|
-
```typescript
|
|
1388
|
-
// ✅ Correct - properly typed event handlers
|
|
1389
|
-
function handleClick(event: CustomEvent<{ event: MouseEvent }>) {
|
|
1390
|
-
console.log('Clicked:', event.detail.event);
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
function handleFormSubmit(event: CustomEvent<{
|
|
1394
|
-
data: Record<string, FormDataEntryValue>;
|
|
1395
|
-
formData: FormData
|
|
1396
|
-
}>) {
|
|
1397
|
-
console.log('Form data:', event.detail.data);
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
// ❌ Incorrect - missing proper typing
|
|
1401
|
-
function handleClick(event: CustomEvent) {
|
|
1402
|
-
console.log(event.detail.value); // May cause type errors
|
|
1403
|
-
}
|
|
1404
|
-
```
|
|
153
|
+
let plan = $state<string | undefined>("pro");
|
|
154
|
+
</script>
|
|
1405
155
|
|
|
1406
|
-
|
|
1407
|
-
If you see "Unknown at rule @apply" warnings in your IDE:
|
|
1408
|
-
|
|
1409
|
-
1. Install Tailwind CSS IntelliSense extension
|
|
1410
|
-
2. Add to your VS Code settings:
|
|
1411
|
-
```json
|
|
1412
|
-
{
|
|
1413
|
-
"css.customData": [".vscode/tailwind.json"],
|
|
1414
|
-
"tailwindCSS.includeLanguages": {
|
|
1415
|
-
"svelte": "html"
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
156
|
+
<RadioGroup legend="Plan" options={[{ value: "pro", label: "Pro" }]} bind:value={plan} />
|
|
1418
157
|
```
|
|
1419
158
|
|
|
1420
|
-
|
|
1421
|
-
If you encounter import errors, ensure you're using the latest version:
|
|
1422
|
-
|
|
1423
|
-
```bash
|
|
1424
|
-
npm install zabi-components@latest
|
|
1425
|
-
```
|
|
1426
|
-
|
|
1427
|
-
#### Runtime Errors
|
|
1428
|
-
If you encounter runtime errors, ensure you have the correct peer dependencies:
|
|
1429
|
-
|
|
1430
|
-
```bash
|
|
1431
|
-
npm install svelte@^5.43.8 @sveltejs/kit@^2.0.0
|
|
1432
|
-
```
|
|
159
|
+
---
|
|
1433
160
|
|
|
1434
|
-
|
|
161
|
+
## Design tokens (short)
|
|
1435
162
|
|
|
1436
|
-
|
|
163
|
+
| Token / class | Role |
|
|
164
|
+
|---------------|------|
|
|
165
|
+
| `surface-1` / `surface-2` | Layered backgrounds — prefer over raw neutrals so light/dark stay aligned. |
|
|
166
|
+
| `.focus-ring` / `focus-ring--nav` | Keyboard focus rings — don’t replace with `outline-none` unless you substitute an equivalent visible focus style. |
|
|
1437
167
|
|
|
1438
|
-
|
|
1439
|
-
2. **Event Structure**: All events follow standardized structure
|
|
1440
|
-
3. **TypeScript**: Enhanced type definitions with proper event typing
|
|
1441
|
-
4. **Modern CSS**: Uses Container Queries, CSS Grid, and logical properties
|
|
168
|
+
Details: [THEME.md](./THEME.md).
|
|
1442
169
|
|
|
1443
|
-
|
|
170
|
+
---
|
|
1444
171
|
|
|
1445
|
-
|
|
1446
|
-
- **Tree Shaking**: Import only what you need
|
|
1447
|
-
- **CSS Containment**: Components use CSS containment for better performance
|
|
1448
|
-
- **Efficient Animations**: Uses `will-change` and `transform` for smooth animations
|
|
1449
|
-
- **Lazy Loading**: Components support lazy loading when needed
|
|
172
|
+
## Constraints (do / don’t)
|
|
1450
173
|
|
|
1451
|
-
|
|
174
|
+
**Consumers — imports**
|
|
1452
175
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
<p>User information</p>
|
|
1458
|
-
</Card>
|
|
176
|
+
```ts
|
|
177
|
+
// ✅ Stable public API
|
|
178
|
+
import { Button, Modal } from "zabi-components";
|
|
179
|
+
import type { ButtonVariant } from "zabi-components/types";
|
|
1459
180
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
<h3>User Profile</h3>
|
|
1463
|
-
<p>User information</p>
|
|
1464
|
-
</div>
|
|
1465
|
-
```
|
|
1466
|
-
|
|
1467
|
-
#### Event Handling
|
|
1468
|
-
```typescript
|
|
1469
|
-
// ✅ Good - Proper event typing
|
|
1470
|
-
function handleFormSubmit(event: CustomEvent<{
|
|
1471
|
-
data: Record<string, FormDataEntryValue>;
|
|
1472
|
-
formData: FormData
|
|
1473
|
-
}>) {
|
|
1474
|
-
// Handle form submission
|
|
1475
|
-
console.log('Form data:', event.detail.data);
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
// ❌ Avoid - Untyped events
|
|
1479
|
-
function handleFormSubmit(event: any) {
|
|
1480
|
-
console.log(event.detail); // No type safety
|
|
1481
|
-
}
|
|
181
|
+
// ❌ Fragile / unsupported — may break on upgrades
|
|
182
|
+
import Button from "some-path/node_modules/zabi-components/dist/atoms/Button.svelte";
|
|
1482
183
|
```
|
|
1483
184
|
|
|
1484
|
-
|
|
1485
|
-
```svelte
|
|
1486
|
-
<!-- ✅ Good - Use component props and Tailwind -->
|
|
1487
|
-
<Button variant="primary" size="lg" className="w-full">
|
|
1488
|
-
Submit
|
|
1489
|
-
</Button>
|
|
1490
|
-
|
|
1491
|
-
<!-- ❌ Avoid - Inline styles -->
|
|
1492
|
-
<button style="background: blue; width: 100%;">
|
|
1493
|
-
Submit
|
|
1494
|
-
</button>
|
|
1495
|
-
```
|
|
185
|
+
**Consumers — styling**
|
|
1496
186
|
|
|
1497
|
-
|
|
1498
|
-
- Always provide proper labels and ARIA attributes
|
|
1499
|
-
- Ensure keyboard navigation works correctly
|
|
1500
|
-
- Use semantic HTML elements
|
|
1501
|
-
- Test with screen readers
|
|
1502
|
-
- Maintain proper color contrast ratios
|
|
187
|
+
Load theme CSS (Quick start). Unstyled components usually mean missing `@import "zabi-components/theme-only"` (and dark overrides if you use `.dark`).
|
|
1503
188
|
|
|
1504
|
-
|
|
189
|
+
**Library code (contributors)**
|
|
1505
190
|
|
|
1506
|
-
|
|
191
|
+
```ts
|
|
192
|
+
// ❌ App routes are not part of the package graph
|
|
193
|
+
import { analytics } from "../../routes/lib/analytics";
|
|
1507
194
|
|
|
1508
|
-
|
|
195
|
+
// ✅ Shared helpers live next to components
|
|
196
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
1509
197
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
import { Card, CardHeader, CardContent, CardFooter, CardTitle, CardDescription, Button } from 'zabi-components';
|
|
1513
|
-
</script>
|
|
198
|
+
// ❌ Packaging: escaping `dist/` layout (CI fails `npm run check`)
|
|
199
|
+
import type { Foo } from "../../types/variants";
|
|
1514
200
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
<CardTitle>Card Title</CardTitle>
|
|
1518
|
-
<CardDescription>
|
|
1519
|
-
This is a description using compound components.
|
|
1520
|
-
</CardDescription>
|
|
1521
|
-
</CardHeader>
|
|
1522
|
-
<CardContent>
|
|
1523
|
-
<p>Main content area with flexible composition.</p>
|
|
1524
|
-
</CardContent>
|
|
1525
|
-
<CardFooter>
|
|
1526
|
-
<Button variant="primary">Save</Button>
|
|
1527
|
-
<Button variant="ghost">Cancel</Button>
|
|
1528
|
-
</CardFooter>
|
|
1529
|
-
</Card>
|
|
201
|
+
// ✅ Depth matches publish layout
|
|
202
|
+
import type { Foo } from "../types/variants.js";
|
|
1530
203
|
```
|
|
1531
204
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
Zabi Components is built with accessibility in mind:
|
|
1535
|
-
|
|
1536
|
-
### WCAG 2.1 AA Compliance
|
|
1537
|
-
- Comprehensive ARIA attributes
|
|
1538
|
-
- Proper semantic HTML
|
|
1539
|
-
- Keyboard navigation support
|
|
1540
|
-
- Focus management
|
|
1541
|
-
- Screen reader compatibility
|
|
205
|
+
---
|
|
1542
206
|
|
|
1543
|
-
|
|
1544
|
-
- **Tab**: Navigate between interactive elements
|
|
1545
|
-
- **Enter/Space**: Activate buttons and links
|
|
1546
|
-
- **Arrow Keys**: Navigate within components (Tabs, Navigation, Dropdown)
|
|
1547
|
-
- **Escape**: Close modals and overlays
|
|
1548
|
-
- **Home/End**: Jump to first/last item in lists
|
|
207
|
+
## Common pitfalls
|
|
1549
208
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
-
|
|
1553
|
-
|
|
1554
|
-
-
|
|
209
|
+
| Issue | What goes wrong | Fix |
|
|
210
|
+
|-------|-----------------|-----|
|
|
211
|
+
| **Invalid import paths** | Build fails or types missing — deep `dist/` imports, wrong relative depth to `types/`. | Use `package.json` exports only; types from `zabi-components/types` or correct `../types/` inside `src/components`. |
|
|
212
|
+
| **Composable misuse** | Missing `NavigationMenu` / `NavigationMenuList` wrapper, or orphan `CardHeader` without `Card`. | Follow the documented parent chain; copy structure from Storybook or package demos. |
|
|
213
|
+
| **`outline-none` on custom controls** | WCAG failure — keyboard users see no focus. | Use `.focus-ring`, theme rings, or mirror the focus styles inputs/buttons use. |
|
|
214
|
+
| **Legacy Svelte events** | Handlers never run — this library uses `onclick`, not `on:click`. | Use `onclick={handler}` on components that expose it; use `oninput` / native props per component. |
|
|
1555
215
|
|
|
1556
|
-
|
|
1557
|
-
See [DESIGN_SYSTEM_REMEDIATION_TRACKER.md](./docs/DESIGN_SYSTEM_REMEDIATION_TRACKER.md), [AUDIT_RUNBOOK.md](./docs/AUDIT_RUNBOOK.md), and [AUDIT_SCORECARD_TEMPLATE.md](./docs/AUDIT_SCORECARD_TEMPLATE.md) for audit and remediation workflows.
|
|
216
|
+
---
|
|
1558
217
|
|
|
1559
|
-
##
|
|
218
|
+
## Development (contributors)
|
|
1560
219
|
|
|
1561
|
-
|
|
220
|
+
| Topic | Rule |
|
|
221
|
+
|-------|------|
|
|
222
|
+
| Utilities | `src/components/util/` |
|
|
223
|
+
| New components | `atoms` / `molecules` / `organisms` by scope |
|
|
224
|
+
| Imports | Shallow relatives inside `components`; no `routes/` |
|
|
225
|
+
| CSS | Prefer Tailwind + tokens; avoid unnecessary `:global` |
|
|
1562
226
|
|
|
1563
|
-
|
|
1564
|
-
Used for state indication: `default`, `success`, `warning`, `error`, `info`
|
|
227
|
+
---
|
|
1565
228
|
|
|
1566
|
-
|
|
1567
|
-
Used for visual appearance: Button (`primary`, `secondary`, `outline`, `link`, etc.), Card (`elevated`, `outlined`, `flat`)
|
|
229
|
+
## Testing & quality
|
|
1568
230
|
|
|
1569
|
-
|
|
1570
|
-
|
|
231
|
+
| Layer | Command | Purpose |
|
|
232
|
+
|-------|---------|---------|
|
|
233
|
+
| Unit / component | `npm run test` | Vitest + Testing Library — logic and regressions |
|
|
234
|
+
| Interaction | `npm run test:e2e` | Playwright — overlays, focus, keyboard flows |
|
|
1571
235
|
|
|
1572
|
-
|
|
236
|
+
---
|
|
1573
237
|
|
|
1574
|
-
##
|
|
238
|
+
## More documentation
|
|
1575
239
|
|
|
1576
|
-
|
|
240
|
+
[THEME.md](./THEME.md) · [THEMING.md](./THEMING.md) · [docs/theme-imports.md](./docs/theme-imports.md) · [CHANGELOG.md](./CHANGELOG.md)
|
|
1577
241
|
|
|
1578
|
-
|
|
242
|
+
## License
|
|
1579
243
|
|
|
1580
|
-
MIT
|
|
244
|
+
MIT — see [LICENSE](./LICENSE).
|