zabi-components 5.0.21 → 6.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 +42 -44
- package/THEME.md +473 -0
- package/THEMING.md +248 -0
- package/dist/atoms/Card.svelte +40 -67
- package/dist/atoms/Card.svelte.d.ts +3 -3
- package/dist/atoms/CardContent.svelte +1 -1
- package/dist/atoms/CardHeader.svelte +10 -1
- package/dist/atoms/CardHeader.svelte.d.ts +3 -0
- package/dist/atoms/Checkbox.svelte +1 -7
- package/dist/atoms/CodeBlock.svelte +7 -7
- package/dist/atoms/ColorPicker.svelte +81 -73
- package/dist/atoms/Input.svelte +16 -12
- package/dist/atoms/Input.svelte.d.ts +2 -0
- package/dist/atoms/List.svelte +47 -0
- package/dist/atoms/List.svelte.d.ts +19 -0
- package/dist/atoms/ListItem.svelte +148 -0
- package/dist/atoms/ListItem.svelte.d.ts +35 -0
- package/dist/atoms/Progress.svelte +4 -6
- package/dist/atoms/Select.svelte +65 -20
- package/dist/atoms/Select.svelte.d.ts +13 -1
- package/dist/atoms/Textarea.svelte +13 -11
- package/dist/atoms/Textarea.svelte.d.ts +1 -0
- package/dist/atoms/Toast.svelte +1 -1
- package/dist/atoms/Toggle.svelte +1 -7
- package/dist/atoms/Tooltip.svelte +8 -15
- package/dist/atoms/index.d.ts +2 -0
- package/dist/atoms/index.js +2 -0
- package/dist/components/atoms/index.d.ts +2 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +0 -1
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/navigation-menu-context.d.ts +7 -0
- package/dist/components/molecules/navigation-menu-context.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/index.d.ts +4 -1
- package/dist/index.js +5 -9
- package/dist/lib/showcase/components-catalog.d.ts +3 -0
- package/dist/lib/showcase/components-catalog.d.ts.map +1 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts +63 -0
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -0
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts +15 -0
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +1 -0
- package/dist/molecules/Alert.svelte +9 -4
- package/dist/molecules/ComponentDemo.svelte +53 -50
- package/dist/molecules/ContactForm.svelte +113 -76
- package/dist/molecules/ContactForm.svelte.d.ts +1 -4
- package/dist/molecules/Dropdown.svelte +4 -2
- package/dist/molecules/ImageUpload.svelte +27 -4
- package/dist/molecules/ImageUpload.svelte.d.ts +1 -0
- package/dist/molecules/Modal.svelte +46 -45
- package/dist/molecules/NavigationMenu.svelte +7 -10
- package/dist/molecules/NavigationMenuContent.svelte +9 -9
- package/dist/molecules/NavigationMenuItem.svelte +7 -6
- package/dist/molecules/NavigationMenuLink.svelte +8 -6
- package/dist/molecules/NavigationMenuList.svelte +0 -7
- package/dist/molecules/NavigationMenuTrigger.svelte +8 -11
- package/dist/molecules/Section.svelte +12 -12
- package/dist/molecules/SlideUp.svelte +32 -2
- package/dist/molecules/Tabs.svelte +74 -7
- package/dist/molecules/index.d.ts +0 -1
- package/dist/molecules/index.js +0 -1
- package/dist/molecules/navigation-menu-context.d.ts +6 -0
- package/dist/molecules/navigation-menu-context.js +1 -0
- package/dist/organisms/Navbar.svelte +170 -53
- package/dist/organisms/Navbar.svelte.d.ts +31 -4
- package/dist/organisms/SidebarNavigation.svelte +464 -0
- package/dist/organisms/SidebarNavigation.svelte.d.ts +49 -0
- package/dist/organisms/SidebarPanel.svelte +175 -0
- package/dist/organisms/SidebarPanel.svelte.d.ts +32 -0
- package/dist/organisms/index.d.ts +2 -1
- package/dist/organisms/index.js +2 -1
- package/dist/types/page.types.d.ts +32 -0
- package/dist/types/page.types.d.ts.map +1 -0
- package/dist/types/variants.ts +76 -0
- package/dist/zabi-components-colors.css +347 -324
- package/dist/zabi-components-theme-dark-only.css +159 -151
- package/dist/zabi-components-theme-dark.css +159 -151
- package/dist/zabi-components-theme-only.css +245 -230
- package/dist/zabi-components-theme.css +245 -230
- package/dist/zabi-components.css +722 -84
- package/docs/theme-imports.md +112 -0
- package/package.json +20 -9
- package/dist/molecules/Sidebar.svelte +0 -324
- package/dist/molecules/Sidebar.svelte.d.ts +0 -30
- package/dist/organisms/Navigation.svelte +0 -116
- package/dist/organisms/Navigation.svelte.d.ts +0 -20
package/THEMING.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Theming Guide
|
|
2
|
+
|
|
3
|
+
## 🎨 Single Source of Truth
|
|
4
|
+
|
|
5
|
+
**All theming is done in one file: `src/app.css`**
|
|
6
|
+
|
|
7
|
+
This file contains:
|
|
8
|
+
- All color scales (brand, citron, pine, iris, base)
|
|
9
|
+
- Semantic color tokens (primary, secondary, success, error, etc.)
|
|
10
|
+
- Action colors (for buttons and interactive elements)
|
|
11
|
+
- Dark mode overrides
|
|
12
|
+
- Typography settings
|
|
13
|
+
- Spacing, border radius, and z-index values
|
|
14
|
+
|
|
15
|
+
## 📍 Where to Make Changes
|
|
16
|
+
|
|
17
|
+
### Main Theme (Light Mode)
|
|
18
|
+
Edit the `@theme` block in `src/app.css`.
|
|
19
|
+
|
|
20
|
+
### Dark Mode Theme
|
|
21
|
+
Edit the `.dark` block in `src/app.css`.
|
|
22
|
+
|
|
23
|
+
## ⚠️ Important: Don't Edit These Files
|
|
24
|
+
|
|
25
|
+
### Generated Files (in `dist/` folder)
|
|
26
|
+
These files are **automatically generated** from `src/app.css` by the build script:
|
|
27
|
+
- ❌ `dist/zabi-components.css` - Full compiled CSS (generated)
|
|
28
|
+
- ❌ `dist/zabi-components-theme.css` - Theme with Tailwind import (generated)
|
|
29
|
+
- ❌ `dist/zabi-components-theme-only.css` - Theme only, no Tailwind (generated)
|
|
30
|
+
- ❌ `dist/zabi-components-theme-dark.css` - Dark mode theme (generated)
|
|
31
|
+
- ❌ `dist/zabi-components-theme-dark-only.css` - Dark mode theme only (generated)
|
|
32
|
+
- ❌ `dist/zabi-components-colors.css` - Standalone colors (generated from app.css)
|
|
33
|
+
|
|
34
|
+
**These files are overwritten every time you run `npm run build:css`**
|
|
35
|
+
|
|
36
|
+
### Example Files
|
|
37
|
+
- ❌ `examples/theme-extensions/02-custom-brand-colors.css` - Just an example, not your actual theme
|
|
38
|
+
- ❌ `examples/theme-extensions/03-custom-semantic-colors.css` - Just an example, not your actual theme
|
|
39
|
+
|
|
40
|
+
### Source Files (Edit These)
|
|
41
|
+
- ✅ `src/app.css` - **THIS IS THE ONLY FILE YOU NEED TO EDIT**
|
|
42
|
+
- ✅ Removed deprecated files (`src/styles/colors.css`, `src/styles/base.css`, `src/styles/simple.css`, `src/app-simple.css`)
|
|
43
|
+
|
|
44
|
+
## 📦 What Are the Dist Files For?
|
|
45
|
+
|
|
46
|
+
The `dist/` files are **for consumers** of your library who want to import the theme separately.
|
|
47
|
+
|
|
48
|
+
**Full import matrix, export paths, and examples:** [docs/theme-imports.md](./docs/theme-imports.md)
|
|
49
|
+
|
|
50
|
+
### For Library Developers (You)
|
|
51
|
+
- **Edit**: `src/app.css` only
|
|
52
|
+
- **Build**: Run `npm run build:css` to regenerate all dist files (or `npm run build:lib` before publish)
|
|
53
|
+
- **After token edits**: run `node scripts/verify-build.js` to confirm outputs (also runs as part of `build:lib`)
|
|
54
|
+
- **Base scale source**: use `tokens/base-scale.js`; run `npm run sync:tokens` (or `npm run build:css`) to render token blocks back into `src/app.css`
|
|
55
|
+
|
|
56
|
+
### For Library Consumers
|
|
57
|
+
|
|
58
|
+
Use **package exports** (preferred):
|
|
59
|
+
|
|
60
|
+
| Need | Import |
|
|
61
|
+
|------|--------|
|
|
62
|
+
| Tailwind + full `@theme` (app has no Tailwind yet) | `zabi-components/theme` |
|
|
63
|
+
| Tailwind already in app — merge `@theme` only | `zabi-components/theme-only` |
|
|
64
|
+
| Dark overrides (+ Tailwind import in file) | `zabi-components/theme-dark` |
|
|
65
|
+
| Dark overrides only (Tailwind already loaded) | `zabi-components/theme-dark-only` |
|
|
66
|
+
| CSS variables only, no Tailwind | `zabi-components/colors` |
|
|
67
|
+
| Full compiled CSS (utilities + everything) | `zabi-components/css` |
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
|
|
71
|
+
```css
|
|
72
|
+
@import "zabi-components/theme-only";
|
|
73
|
+
@import "zabi-components/theme-dark-only";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
@import "zabi-components/colors";
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```css
|
|
81
|
+
@import "zabi-components/css";
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Canonical recommendation for most apps remains:
|
|
85
|
+
|
|
86
|
+
```css
|
|
87
|
+
@import "tailwindcss";
|
|
88
|
+
@import "zabi-components/theme-only";
|
|
89
|
+
@import "zabi-components/theme-dark-only";
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Dark mapping rule reference:
|
|
93
|
+
- Physical ramp tokens are `--zabi-base-50 ... --zabi-base-950`.
|
|
94
|
+
- Semantic dark mapping is generated with `S -> 1000 - S` (for example `50 <-> 950`, `75 <-> 925`, `500` unchanged).
|
|
95
|
+
|
|
96
|
+
## 🎯 Common Customizations
|
|
97
|
+
|
|
98
|
+
### Change Brand Colors
|
|
99
|
+
|
|
100
|
+
```css
|
|
101
|
+
@theme {
|
|
102
|
+
/* Replace the brand color scale */
|
|
103
|
+
--color-brand-50: #f0f9ff;
|
|
104
|
+
--color-brand-100: #e0f2fe;
|
|
105
|
+
--color-brand-200: #bae6fd;
|
|
106
|
+
--color-brand-300: #7dd3fc;
|
|
107
|
+
--color-brand-400: #38bdf8;
|
|
108
|
+
--color-brand-500: #0ea5e9;
|
|
109
|
+
--color-brand-600: #0284c7;
|
|
110
|
+
--color-brand-700: #0369a1;
|
|
111
|
+
--color-brand-800: #075985;
|
|
112
|
+
--color-brand-900: #0c4a6e;
|
|
113
|
+
--color-brand-950: #082f49;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Change Primary Button Color
|
|
118
|
+
|
|
119
|
+
The primary button uses `--color-action-primary` which defaults to `brand-800`. To change it:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
@theme {
|
|
123
|
+
/* Use a different brand shade */
|
|
124
|
+
--color-action-primary: theme(colors.brand.600);
|
|
125
|
+
|
|
126
|
+
/* Or use a completely custom color */
|
|
127
|
+
--color-action-primary: #your-color-here;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Brand Scale Usage
|
|
132
|
+
|
|
133
|
+
The brand scale is used throughout the system:
|
|
134
|
+
|
|
135
|
+
- **brand-50 to brand-100**: Subtle backgrounds, hover states
|
|
136
|
+
- **brand-300 to brand-400**: Light accents, disabled states
|
|
137
|
+
- **brand-500**: Focus rings, medium emphasis
|
|
138
|
+
- **brand-600**: Primary color, links
|
|
139
|
+
- **brand-700**: Link hover, primary hover
|
|
140
|
+
- **brand-800**: **Primary buttons** (`action-primary`)
|
|
141
|
+
- **brand-900**: Primary button hover
|
|
142
|
+
- **brand-950**: Darkest brand shade
|
|
143
|
+
|
|
144
|
+
### Dark Mode
|
|
145
|
+
|
|
146
|
+
Dark mode automatically inverts the brand scale:
|
|
147
|
+
- `brand-50` becomes `brand-950` (darkest)
|
|
148
|
+
- `brand-950` becomes `brand-50` (lightest)
|
|
149
|
+
|
|
150
|
+
This happens automatically in the `.dark` block, so you typically don't need to override action colors in dark mode.
|
|
151
|
+
|
|
152
|
+
## 🌙 Dark Mode Action Colors
|
|
153
|
+
|
|
154
|
+
Dark mode action colors are handled in `src/app.css` in the `.dark` block (lines 393-404):
|
|
155
|
+
|
|
156
|
+
### Primary Actions
|
|
157
|
+
- **No explicit dark mode override needed** - Brand colors are automatically inverted
|
|
158
|
+
- `brand-800` (light mode) → `brand-200` (dark mode) automatically
|
|
159
|
+
|
|
160
|
+
### Secondary Actions
|
|
161
|
+
- **No explicit dark mode override needed** - Base colors are automatically inverted
|
|
162
|
+
- `base-600` (light mode) → `base-400` (dark mode) automatically
|
|
163
|
+
|
|
164
|
+
### Danger Actions
|
|
165
|
+
- **Has explicit dark mode overrides** (lines 399-404) because red colors aren't automatically inverted:
|
|
166
|
+
```css
|
|
167
|
+
.dark {
|
|
168
|
+
--color-action-danger: theme(colors.red.500);
|
|
169
|
+
--color-action-danger-hover: theme(colors.red.600);
|
|
170
|
+
--color-action-danger-active: theme(colors.red.700);
|
|
171
|
+
/* ... */
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### In Generated Files
|
|
176
|
+
|
|
177
|
+
- **`dist/zabi-components.css`**: Has dark mode at line 2178, danger actions at lines 2264-2269
|
|
178
|
+
- **`dist/zabi-components-theme-dark-only.css`**: Has dark mode action colors at lines 119-130 (only danger has explicit overrides)
|
|
179
|
+
- **`dist/zabi-components-theme-only.css`**: Only has light mode (@theme block), no dark mode
|
|
180
|
+
- **`dist/zabi-components-theme.css`**: Only has light mode (@theme block), no dark mode
|
|
181
|
+
|
|
182
|
+
## 📁 File Structure
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
src/
|
|
186
|
+
└── app.css ← 🎯 EDIT THIS FILE FOR ALL THEMING (single source of truth)
|
|
187
|
+
|
|
188
|
+
dist/ ← ⚠️ GENERATED FILES - DON'T EDIT
|
|
189
|
+
├── zabi-components.css (full CSS with dark mode)
|
|
190
|
+
├── zabi-components-theme.css (light mode only)
|
|
191
|
+
├── zabi-components-theme-only.css (light mode only)
|
|
192
|
+
├── zabi-components-theme-dark.css (dark mode only)
|
|
193
|
+
├── zabi-components-theme-dark-only.css (dark mode only)
|
|
194
|
+
└── zabi-components-colors.css (standalone, both modes)
|
|
195
|
+
|
|
196
|
+
examples/ ← ⚠️ EXAMPLES ONLY - DON'T EDIT
|
|
197
|
+
└── theme-extensions/
|
|
198
|
+
├── 02-custom-brand-colors.css
|
|
199
|
+
└── 03-custom-semantic-colors.css
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## ⚠️ Important Notes
|
|
203
|
+
|
|
204
|
+
1. **Never edit files in `dist/`** - These are automatically generated from `src/app.css` by `scripts/build-css.js`
|
|
205
|
+
2. **All theming is in `src/app.css`** - This is the single source of truth
|
|
206
|
+
3. **Always edit `src/app.css`** - This is your single source of truth
|
|
207
|
+
4. The `examples/` folder contains examples, not your actual theme
|
|
208
|
+
5. **Dist files are overwritten** every time you run `npm run build:css`
|
|
209
|
+
|
|
210
|
+
## 🔄 Build Process
|
|
211
|
+
|
|
212
|
+
When you edit `src/app.css`:
|
|
213
|
+
|
|
214
|
+
1. **Development**: Changes are automatically reflected (no build needed)
|
|
215
|
+
2. **Production**: Run `npm run build:css` to regenerate dist files
|
|
216
|
+
3. The build script (`scripts/build-css.js`) reads `src/app.css` and generates:
|
|
217
|
+
- `zabi-components.css` - Full compiled CSS with Tailwind processed (includes dark mode)
|
|
218
|
+
- `zabi-components-theme.css` - Theme block with Tailwind import (light mode only)
|
|
219
|
+
- `zabi-components-theme-only.css` - Theme block only (light mode only)
|
|
220
|
+
- `zabi-components-theme-dark.css` - Dark mode theme with Tailwind import
|
|
221
|
+
- `zabi-components-theme-dark-only.css` - Dark mode theme only
|
|
222
|
+
- `zabi-components-colors.css` - Standalone CSS custom properties (both light and dark)
|
|
223
|
+
|
|
224
|
+
## 🔄 After Making Changes
|
|
225
|
+
|
|
226
|
+
### For Development
|
|
227
|
+
After editing `src/app.css`, changes are automatically reflected in your app. No build needed!
|
|
228
|
+
|
|
229
|
+
### For Production/Library Build
|
|
230
|
+
If you're building the library for distribution:
|
|
231
|
+
```bash
|
|
232
|
+
npm run build:css # Regenerates all dist CSS files from src/app.css
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Optional perceptual midpoint generation (OKLCH)
|
|
236
|
+
|
|
237
|
+
Base midpoints are frozen hex by default (`fixed` mode).
|
|
238
|
+
For experimentation, you can generate midpoint steps in OKLCH interpolation and commit the resulting frozen values:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
ZABI_BASE_SCALE_MODE=oklch npm run sync:tokens
|
|
242
|
+
npm run build:css
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Notes:
|
|
246
|
+
- Primary anchors (`50, 100, 200 ... 950`) stay fixed.
|
|
247
|
+
- Midpoints (`75, 150, 250 ... 925`) are generated from adjacent anchors.
|
|
248
|
+
- Commit updated `src/app.css` and regenerated `dist/` outputs together.
|
package/dist/atoms/Card.svelte
CHANGED
|
@@ -1,70 +1,64 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import type { CardVariant, SizeVariant } from "../../types/variants.js";
|
|
4
|
-
import CardHeader from "./CardHeader.svelte";
|
|
5
|
-
import CardContent from "./CardContent.svelte";
|
|
6
4
|
|
|
7
5
|
interface Props {
|
|
8
|
-
title?: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
image?: string;
|
|
11
6
|
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
12
7
|
size?: SizeVariant;
|
|
13
8
|
variant?: CardVariant;
|
|
14
9
|
fullWidth?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Required when card is interactive (has onclick) for accessibility */
|
|
12
|
+
ariaLabel?: string;
|
|
15
13
|
children?: Snippet;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
let {
|
|
19
|
-
title = "",
|
|
20
|
-
description = "",
|
|
21
|
-
image = "",
|
|
22
17
|
onclick,
|
|
23
18
|
size = "md",
|
|
24
19
|
variant = "default",
|
|
25
20
|
fullWidth = true,
|
|
21
|
+
className = "",
|
|
22
|
+
ariaLabel,
|
|
26
23
|
children,
|
|
27
24
|
...restProps
|
|
28
25
|
}: Props = $props();
|
|
29
26
|
|
|
30
|
-
const sizeClass = $derived(
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const variantClasses = $derived(() => {
|
|
35
|
-
switch (variant) {
|
|
36
|
-
case "elevated":
|
|
37
|
-
return "bg-card shadow-lg";
|
|
38
|
-
case "outlined":
|
|
39
|
-
return "bg-card border-2 border-base-200 shadow-none";
|
|
40
|
-
case "flat":
|
|
41
|
-
return "bg-card shadow-none border-none";
|
|
42
|
-
default:
|
|
43
|
-
return "bg-card shadow-sm";
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const useOldAPI = $derived(
|
|
48
|
-
title !== "" || description !== "" || image !== "",
|
|
27
|
+
const sizeClass = $derived(
|
|
28
|
+
size === "sm" ? "p-4" : size === "lg" ? "p-8" : "p-6",
|
|
49
29
|
);
|
|
50
30
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
: "cursor-pointer hover:shadow-lg hover:bg-card-hover"
|
|
61
|
-
: "";
|
|
62
|
-
const widthClasses = fullWidth ? "w-full" : "";
|
|
63
|
-
const minWidthClass = useOldAPI ? "min-w-64" : "";
|
|
64
|
-
const paddingClass = useOldAPI ? sizeClass() : "";
|
|
31
|
+
const variantClasses = $derived(
|
|
32
|
+
variant === "elevated"
|
|
33
|
+
? "bg-card shadow-lg"
|
|
34
|
+
: variant === "outlined"
|
|
35
|
+
? "bg-card border-2 border-base-200 shadow-none"
|
|
36
|
+
: variant === "flat"
|
|
37
|
+
? "bg-card shadow-none border-none"
|
|
38
|
+
: "bg-card shadow-sm",
|
|
39
|
+
);
|
|
65
40
|
|
|
66
|
-
|
|
67
|
-
|
|
41
|
+
const cardClasses = $derived(
|
|
42
|
+
[
|
|
43
|
+
"rounded-lg transition-all duration-200",
|
|
44
|
+
variantClasses,
|
|
45
|
+
onclick
|
|
46
|
+
? variant === "elevated"
|
|
47
|
+
? "cursor-pointer hover:shadow-xl hover:bg-card-hover"
|
|
48
|
+
: variant === "outlined"
|
|
49
|
+
? "cursor-pointer hover:border-brand-500 hover:bg-card-hover"
|
|
50
|
+
: variant === "flat"
|
|
51
|
+
? "cursor-pointer hover:bg-card-hover"
|
|
52
|
+
: "cursor-pointer hover:shadow-lg hover:bg-card-hover"
|
|
53
|
+
: "",
|
|
54
|
+
fullWidth ? "w-full" : "",
|
|
55
|
+
sizeClass,
|
|
56
|
+
className,
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join(" ")
|
|
60
|
+
.trim(),
|
|
61
|
+
);
|
|
68
62
|
|
|
69
63
|
function handleKeydown(event: KeyboardEvent) {
|
|
70
64
|
if (onclick && (event.key === "Enter" || event.key === " ")) {
|
|
@@ -74,37 +68,16 @@
|
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
const cardRole = $derived(onclick ? "button" : undefined);
|
|
77
|
-
const cardAriaLabel = $derived(onclick && title ? title : undefined);
|
|
78
71
|
</script>
|
|
79
72
|
|
|
80
73
|
<div
|
|
81
|
-
class={cardClasses
|
|
74
|
+
class={cardClasses}
|
|
82
75
|
{onclick}
|
|
83
76
|
role={cardRole}
|
|
84
77
|
{...onclick ? { tabindex: 0 } : {}}
|
|
85
|
-
aria-label={
|
|
78
|
+
aria-label={ariaLabel}
|
|
86
79
|
onkeydown={onclick ? handleKeydown : undefined}
|
|
87
80
|
{...restProps}
|
|
88
81
|
>
|
|
89
|
-
{
|
|
90
|
-
<!-- Old API: Using title/description/image props -->
|
|
91
|
-
{#if title || description}
|
|
92
|
-
<CardHeader {title} {description} />
|
|
93
|
-
{/if}
|
|
94
|
-
|
|
95
|
-
{#if image || children}
|
|
96
|
-
<CardContent {image} imageAlt={title}>
|
|
97
|
-
{#if children}
|
|
98
|
-
{@render children()}
|
|
99
|
-
{/if}
|
|
100
|
-
</CardContent>
|
|
101
|
-
{/if}
|
|
102
|
-
{:else}
|
|
103
|
-
<!-- New API: Using compound components (CardHeader, CardContent, CardFooter) -->
|
|
104
|
-
{#if children}
|
|
105
|
-
<CardContent className="pt-6">
|
|
106
|
-
{@render children()}
|
|
107
|
-
</CardContent>
|
|
108
|
-
{/if}
|
|
109
|
-
{/if}
|
|
82
|
+
{@render children?.()}
|
|
110
83
|
</div>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
2
|
import type { CardVariant, SizeVariant } from "../../types/variants.js";
|
|
3
3
|
interface Props {
|
|
4
|
-
title?: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
image?: string;
|
|
7
4
|
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
8
5
|
size?: SizeVariant;
|
|
9
6
|
variant?: CardVariant;
|
|
10
7
|
fullWidth?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Required when card is interactive (has onclick) for accessibility */
|
|
10
|
+
ariaLabel?: string;
|
|
11
11
|
children?: Snippet;
|
|
12
12
|
}
|
|
13
13
|
declare const Card: import("svelte").Component<Props, {}, "">;
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
title?: string;
|
|
6
|
+
/** Short secondary text displayed directly under the title */
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
/** Longer supporting text displayed below the subtitle */
|
|
6
9
|
description?: string;
|
|
7
10
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
8
11
|
className?: string;
|
|
@@ -11,6 +14,7 @@
|
|
|
11
14
|
|
|
12
15
|
let {
|
|
13
16
|
title = "",
|
|
17
|
+
subtitle = "",
|
|
14
18
|
description = "",
|
|
15
19
|
level = 3,
|
|
16
20
|
className = "",
|
|
@@ -24,7 +28,7 @@
|
|
|
24
28
|
});
|
|
25
29
|
</script>
|
|
26
30
|
|
|
27
|
-
<header class=
|
|
31
|
+
<header class={`flex flex-col space-y-1.5 pb-4 ${className}`.trim()} {...restProps}>
|
|
28
32
|
{#if title}
|
|
29
33
|
<svelte:element this={headingTag} class={headingClasses()}>
|
|
30
34
|
{title}
|
|
@@ -33,6 +37,11 @@
|
|
|
33
37
|
{#if children}
|
|
34
38
|
{@render children()}
|
|
35
39
|
{/if}
|
|
40
|
+
{#if subtitle}
|
|
41
|
+
<p class="text-sm text-description">
|
|
42
|
+
{subtitle}
|
|
43
|
+
</p>
|
|
44
|
+
{/if}
|
|
36
45
|
{#if description}
|
|
37
46
|
<p class="text-sm text-description">
|
|
38
47
|
{description}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
2
|
interface Props {
|
|
3
3
|
title?: string;
|
|
4
|
+
/** Short secondary text displayed directly under the title */
|
|
5
|
+
subtitle?: string;
|
|
6
|
+
/** Longer supporting text displayed below the subtitle */
|
|
4
7
|
description?: string;
|
|
5
8
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
6
9
|
className?: string;
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
if (typeof window !== "undefined") {
|
|
4
|
-
return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
|
|
5
|
-
} else {
|
|
6
|
-
return `${prefix}-ssr-${Date.now()}`;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
2
|
+
import { generateId } from "../../routes/lib/ssr-safe.js";
|
|
9
3
|
|
|
10
4
|
interface Props {
|
|
11
5
|
checked?: boolean;
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
{#if showCopyButton}
|
|
45
45
|
<button
|
|
46
46
|
onclick={copyToClipboard}
|
|
47
|
-
class="flex items-center gap-2 px-3 py-1 text-xs text-base-300
|
|
47
|
+
class="flex cursor-pointer items-center gap-2 rounded px-3 py-1 text-xs text-base-300 transition-colors duration-200 hover:bg-base-700 hover:text-white"
|
|
48
48
|
aria-label="Copy code to clipboard"
|
|
49
49
|
>
|
|
50
50
|
{#if copied}
|
|
@@ -92,31 +92,31 @@
|
|
|
92
92
|
|
|
93
93
|
:global(.language-svelte .token.tag),
|
|
94
94
|
:global(.language-html .token.tag) {
|
|
95
|
-
color:
|
|
95
|
+
color: var(--color-error-text);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
:global(.language-svelte .token.attr-name),
|
|
99
99
|
:global(.language-html .token.attr-name) {
|
|
100
|
-
color:
|
|
100
|
+
color: var(--color-success-text);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
:global(.language-svelte .token.attr-value),
|
|
104
104
|
:global(.language-html .token.attr-value) {
|
|
105
|
-
color:
|
|
105
|
+
color: var(--color-warning-text);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
:global(.language-javascript .token.keyword),
|
|
109
109
|
:global(.language-typescript .token.keyword) {
|
|
110
|
-
color:
|
|
110
|
+
color: var(--color-info-text);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
:global(.language-javascript .token.string),
|
|
114
114
|
:global(.language-typescript .token.string) {
|
|
115
|
-
color:
|
|
115
|
+
color: var(--color-warning-text);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
:global(.language-javascript .token.function),
|
|
119
119
|
:global(.language-typescript .token.function) {
|
|
120
|
-
color:
|
|
120
|
+
color: var(--color-success-text);
|
|
121
121
|
}
|
|
122
122
|
</style>
|