@dryui/ui 0.1.6 → 0.1.7
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/dist/backdrop/backdrop.svelte +1 -1
- package/dist/date-field/date-field-root.svelte +0 -1
- package/dist/date-field/date-field-segment.svelte +1 -1
- package/dist/date-picker/datepicker-calendar.svelte +0 -1
- package/dist/date-range-picker/date-range-picker-calendar.svelte +44 -26
- package/dist/date-time-input/date-time-input.svelte +12 -33
- package/dist/notification-center/notification-center-group.svelte +2 -2
- package/dist/notification-center/notification-center-item.svelte +2 -1
- package/dist/notification-center/notification-center-panel.svelte +21 -2
- package/dist/notification-center/notification-center-root.svelte +1 -1
- package/dist/notification-center/notification-center-trigger.svelte +3 -5
- package/dist/select/select-root.svelte +0 -1
- package/dist/select/select-trigger.svelte +2 -0
- package/dist/time-input/index.d.ts +8 -1
- package/dist/time-input/time-input.svelte +118 -74
- package/package.json +2 -2
- package/skills/dryui/SKILL.md +107 -220
- package/skills/dryui/rules/composition.md +280 -291
|
@@ -1,143 +1,109 @@
|
|
|
1
1
|
# Composition
|
|
2
2
|
|
|
3
|
-
## Layout
|
|
3
|
+
## Layout with CSS Grid
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
All layout uses raw `display: grid` in scoped `<style>` blocks with `--dry-space-*` tokens. No flexbox, no inline styles, no layout components (Stack, Flex, Grid, Spacer).
|
|
6
6
|
|
|
7
|
-
###
|
|
8
|
-
|
|
9
|
-
Stacks children vertically with consistent gap.
|
|
7
|
+
### Vertical stack
|
|
10
8
|
|
|
11
9
|
```svelte
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
<p>Second</p>
|
|
10
|
+
<div class="stack">
|
|
11
|
+
<p>First</p>
|
|
12
|
+
<p>Second</p>
|
|
16
13
|
</div>
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<p>Second</p>
|
|
22
|
-
</Stack>
|
|
15
|
+
<style>
|
|
16
|
+
.stack { display: grid; gap: var(--dry-space-4); }
|
|
17
|
+
</style>
|
|
23
18
|
```
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
### Flex (horizontal layout)
|
|
28
|
-
|
|
29
|
-
Flexible row layout with alignment controls.
|
|
20
|
+
### Horizontal row
|
|
30
21
|
|
|
31
22
|
```svelte
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
<Button>Action</Button>
|
|
23
|
+
<div class="row">
|
|
24
|
+
<span>Label</span>
|
|
25
|
+
<Button>Action</Button>
|
|
36
26
|
</div>
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
<style>
|
|
29
|
+
.row {
|
|
30
|
+
display: grid;
|
|
31
|
+
grid-template-columns: 1fr auto;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: var(--dry-space-4);
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
43
36
|
```
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### Grid (CSS grid)
|
|
48
|
-
|
|
49
|
-
Grid layout configured entirely via `--dry-grid-*` CSS custom properties.
|
|
38
|
+
### Responsive columns with @container
|
|
50
39
|
|
|
51
40
|
```svelte
|
|
52
|
-
|
|
53
|
-
<div
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
<div class="grid-container">
|
|
42
|
+
<div class="grid">
|
|
43
|
+
<div>A</div>
|
|
44
|
+
<div>B</div>
|
|
45
|
+
<div>C</div>
|
|
46
|
+
</div>
|
|
57
47
|
</div>
|
|
58
48
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
<style>
|
|
50
|
+
.grid-container { container-type: inline-size; }
|
|
51
|
+
.grid {
|
|
52
|
+
display: grid;
|
|
53
|
+
grid-template-columns: 1fr;
|
|
54
|
+
gap: var(--dry-space-6);
|
|
55
|
+
}
|
|
56
|
+
@container (min-width: 40rem) {
|
|
57
|
+
.grid { grid-template-columns: repeat(3, 1fr); }
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
65
60
|
```
|
|
66
61
|
|
|
67
|
-
|
|
62
|
+
### Centered max-width content
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
<Grid
|
|
71
|
-
--dry-grid-areas="'header header' 'nav content'"
|
|
72
|
-
--dry-grid-columns="16rem 1fr"
|
|
73
|
-
--dry-grid-rows="auto 1fr"
|
|
74
|
-
--dry-grid-gap="var(--dry-space-4)"
|
|
75
|
-
>
|
|
76
|
-
<Grid.Area --dry-grid-area="header">...</Grid.Area>
|
|
77
|
-
<Grid.Area --dry-grid-area="nav">...</Grid.Area>
|
|
78
|
-
<Grid.Area --dry-grid-area="content">...</Grid.Area>
|
|
79
|
-
</Grid>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Container (centered max-width)
|
|
83
|
-
|
|
84
|
-
Centers content with a max-width constraint.
|
|
64
|
+
Use `Container` (simple component, no `.Root`) for constrained content width:
|
|
85
65
|
|
|
86
66
|
```svelte
|
|
87
|
-
<!-- Incorrect -->
|
|
88
|
-
<div style="max-width: 1200px; margin: 0 auto; padding: 0 1rem;">
|
|
89
|
-
<h1>Page Title</h1>
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
<!-- Correct -->
|
|
93
67
|
<Container>
|
|
94
|
-
|
|
68
|
+
<h1>Page Title</h1>
|
|
95
69
|
</Container>
|
|
96
70
|
```
|
|
97
71
|
|
|
98
|
-
### Spacer (flexible space)
|
|
99
|
-
|
|
100
|
-
Fills available space between flex/stack items.
|
|
101
|
-
|
|
102
|
-
```svelte
|
|
103
|
-
<Flex align="center">
|
|
104
|
-
<h2>Title</h2>
|
|
105
|
-
<Spacer />
|
|
106
|
-
<Button>Action</Button>
|
|
107
|
-
</Flex>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
72
|
## Form Composition
|
|
111
73
|
|
|
112
74
|
### Basic form
|
|
113
75
|
|
|
114
|
-
Wrap each input in Field.Root, stack them
|
|
76
|
+
Wrap each input in Field.Root, stack them with grid, put everything in a Card.
|
|
115
77
|
|
|
116
78
|
```svelte
|
|
117
79
|
<script>
|
|
118
|
-
|
|
119
|
-
|
|
80
|
+
import '@dryui/ui/themes/default.css';
|
|
81
|
+
import { Card, Field, Label, Input, Button } from '@dryui/ui';
|
|
120
82
|
|
|
121
|
-
|
|
122
|
-
|
|
83
|
+
let name = $state('');
|
|
84
|
+
let email = $state('');
|
|
123
85
|
</script>
|
|
124
86
|
|
|
125
87
|
<Card.Root>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
88
|
+
<Card.Header>Contact Info</Card.Header>
|
|
89
|
+
<Card.Content>
|
|
90
|
+
<form class="form-stack">
|
|
91
|
+
<Field.Root>
|
|
92
|
+
<Label>Name</Label>
|
|
93
|
+
<Input bind:value={name} />
|
|
94
|
+
</Field.Root>
|
|
95
|
+
<Field.Root>
|
|
96
|
+
<Label>Email</Label>
|
|
97
|
+
<Input type="email" bind:value={email} />
|
|
98
|
+
</Field.Root>
|
|
99
|
+
<Button type="submit" variant="solid">Save contact</Button>
|
|
100
|
+
</form>
|
|
101
|
+
</Card.Content>
|
|
140
102
|
</Card.Root>
|
|
103
|
+
|
|
104
|
+
<style>
|
|
105
|
+
.form-stack { display: grid; gap: var(--dry-space-4); }
|
|
106
|
+
</style>
|
|
141
107
|
```
|
|
142
108
|
|
|
143
109
|
### Form with validation
|
|
@@ -146,16 +112,16 @@ Use Field.Error to show validation messages.
|
|
|
146
112
|
|
|
147
113
|
```svelte
|
|
148
114
|
<script>
|
|
149
|
-
|
|
150
|
-
|
|
115
|
+
let email = $state('');
|
|
116
|
+
let error = $derived(email && !email.includes('@') ? 'Please enter a valid email' : '');
|
|
151
117
|
</script>
|
|
152
118
|
|
|
153
119
|
<Field.Root>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
120
|
+
<Label>Email</Label>
|
|
121
|
+
<Input type="email" bind:value={email} />
|
|
122
|
+
{#if error}
|
|
123
|
+
<Field.Error>{error}</Field.Error>
|
|
124
|
+
{/if}
|
|
159
125
|
</Field.Root>
|
|
160
126
|
```
|
|
161
127
|
|
|
@@ -163,45 +129,55 @@ Use Field.Error to show validation messages.
|
|
|
163
129
|
|
|
164
130
|
```svelte
|
|
165
131
|
<script>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
132
|
+
let name = $state('');
|
|
133
|
+
let bio = $state('');
|
|
134
|
+
let country = $state('');
|
|
135
|
+
let agreed = $state(false);
|
|
170
136
|
</script>
|
|
171
137
|
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</
|
|
138
|
+
<form class="form-stack">
|
|
139
|
+
<Field.Root>
|
|
140
|
+
<Label>Name</Label>
|
|
141
|
+
<Input bind:value={name} />
|
|
142
|
+
</Field.Root>
|
|
143
|
+
|
|
144
|
+
<Field.Root>
|
|
145
|
+
<Label>Bio</Label>
|
|
146
|
+
<Textarea bind:value={bio} />
|
|
147
|
+
</Field.Root>
|
|
148
|
+
|
|
149
|
+
<Field.Root>
|
|
150
|
+
<Label>Country</Label>
|
|
151
|
+
<Select.Root bind:value={country}>
|
|
152
|
+
<Select.Trigger>
|
|
153
|
+
<Select.Value placeholder="Select country..." />
|
|
154
|
+
</Select.Trigger>
|
|
155
|
+
<Select.Content>
|
|
156
|
+
<Select.Item value="us">United States</Select.Item>
|
|
157
|
+
<Select.Item value="uk">United Kingdom</Select.Item>
|
|
158
|
+
</Select.Content>
|
|
159
|
+
</Select.Root>
|
|
160
|
+
</Field.Root>
|
|
161
|
+
|
|
162
|
+
<Field.Root>
|
|
163
|
+
<div class="checkbox-row">
|
|
164
|
+
<Checkbox bind:checked={agreed} />
|
|
165
|
+
<Label>I agree to the terms</Label>
|
|
166
|
+
</div>
|
|
167
|
+
</Field.Root>
|
|
168
|
+
|
|
169
|
+
<Button type="submit" variant="solid">Submit form</Button>
|
|
170
|
+
</form>
|
|
171
|
+
|
|
172
|
+
<style>
|
|
173
|
+
.form-stack { display: grid; gap: var(--dry-space-4); }
|
|
174
|
+
.checkbox-row {
|
|
175
|
+
display: grid;
|
|
176
|
+
grid-template-columns: auto 1fr;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: var(--dry-space-2);
|
|
179
|
+
}
|
|
180
|
+
</style>
|
|
205
181
|
```
|
|
206
182
|
|
|
207
183
|
## Page Patterns
|
|
@@ -209,161 +185,177 @@ Use Field.Error to show validation messages.
|
|
|
209
185
|
### Page with sidebar
|
|
210
186
|
|
|
211
187
|
```svelte
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
188
|
+
<div class="page-with-sidebar">
|
|
189
|
+
<nav class="sidebar">
|
|
190
|
+
<Button variant="ghost">Dashboard</Button>
|
|
191
|
+
<Button variant="ghost">Settings</Button>
|
|
192
|
+
<Button variant="ghost">Profile</Button>
|
|
193
|
+
</nav>
|
|
194
|
+
<main class="content">
|
|
195
|
+
<h1>Dashboard</h1>
|
|
196
|
+
<p>Main content here.</p>
|
|
197
|
+
</main>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<style>
|
|
201
|
+
.page-with-sidebar {
|
|
202
|
+
display: grid;
|
|
203
|
+
grid-template-columns: 15rem 1fr;
|
|
204
|
+
gap: var(--dry-space-6);
|
|
205
|
+
}
|
|
206
|
+
.sidebar { display: grid; gap: var(--dry-space-2); align-content: start; }
|
|
207
|
+
.content { display: grid; gap: var(--dry-space-6); align-content: start; }
|
|
208
|
+
</style>
|
|
227
209
|
```
|
|
228
210
|
|
|
229
211
|
### Card grid
|
|
230
212
|
|
|
231
213
|
```svelte
|
|
232
|
-
<
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
214
|
+
<div class="card-grid-container">
|
|
215
|
+
<div class="card-grid">
|
|
216
|
+
{#each items as item (item.id)}
|
|
217
|
+
<Card.Root>
|
|
218
|
+
<Card.Header>{item.title}</Card.Header>
|
|
219
|
+
<Card.Content>
|
|
220
|
+
<p>{item.description}</p>
|
|
221
|
+
</Card.Content>
|
|
222
|
+
<Card.Footer>
|
|
223
|
+
<Button variant="outline">View details</Button>
|
|
224
|
+
</Card.Footer>
|
|
225
|
+
</Card.Root>
|
|
226
|
+
{/each}
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<style>
|
|
231
|
+
.card-grid-container { container-type: inline-size; }
|
|
232
|
+
.card-grid {
|
|
233
|
+
display: grid;
|
|
234
|
+
grid-template-columns: 1fr;
|
|
235
|
+
gap: var(--dry-space-6);
|
|
236
|
+
}
|
|
237
|
+
@container (min-width: 40rem) {
|
|
238
|
+
.card-grid { grid-template-columns: repeat(3, 1fr); }
|
|
239
|
+
}
|
|
240
|
+
</style>
|
|
245
241
|
```
|
|
246
242
|
|
|
247
243
|
### Settings page with tabs
|
|
248
244
|
|
|
249
245
|
```svelte
|
|
250
246
|
<script>
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
let activeTab = $state('general');
|
|
248
|
+
let displayName = $state('');
|
|
253
249
|
</script>
|
|
254
250
|
|
|
255
251
|
<Container>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
252
|
+
<div class="settings-stack">
|
|
253
|
+
<h1>Settings</h1>
|
|
254
|
+
<Tabs.Root bind:value={activeTab}>
|
|
255
|
+
<Tabs.List>
|
|
256
|
+
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
|
257
|
+
<Tabs.Trigger value="security">Security</Tabs.Trigger>
|
|
258
|
+
<Tabs.Trigger value="notifications">Notifications</Tabs.Trigger>
|
|
259
|
+
</Tabs.List>
|
|
260
|
+
<Tabs.Content value="general">
|
|
261
|
+
<Card.Root>
|
|
262
|
+
<Card.Content>
|
|
263
|
+
<form class="form-stack">
|
|
264
|
+
<Field.Root>
|
|
265
|
+
<Label>Display Name</Label>
|
|
266
|
+
<Input bind:value={displayName} />
|
|
267
|
+
</Field.Root>
|
|
268
|
+
<Button type="submit" variant="solid">Save settings</Button>
|
|
269
|
+
</form>
|
|
270
|
+
</Card.Content>
|
|
271
|
+
</Card.Root>
|
|
272
|
+
</Tabs.Content>
|
|
273
|
+
<Tabs.Content value="security">
|
|
274
|
+
<!-- Security settings -->
|
|
275
|
+
</Tabs.Content>
|
|
276
|
+
</Tabs.Root>
|
|
277
|
+
</div>
|
|
282
278
|
</Container>
|
|
279
|
+
|
|
280
|
+
<style>
|
|
281
|
+
.settings-stack { display: grid; gap: var(--dry-space-8); }
|
|
282
|
+
.form-stack { display: grid; gap: var(--dry-space-4); }
|
|
283
|
+
</style>
|
|
283
284
|
```
|
|
284
285
|
|
|
285
286
|
### Data table page
|
|
286
287
|
|
|
287
288
|
```svelte
|
|
288
289
|
<Container>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
290
|
+
<div class="page-stack">
|
|
291
|
+
<div class="page-header">
|
|
292
|
+
<h1>Users</h1>
|
|
293
|
+
<Button variant="solid">Add user</Button>
|
|
294
|
+
</div>
|
|
295
|
+
<Table.Root>
|
|
296
|
+
<Table.Header>
|
|
297
|
+
<Table.Row>
|
|
298
|
+
<Table.Head>Name</Table.Head>
|
|
299
|
+
<Table.Head>Email</Table.Head>
|
|
300
|
+
<Table.Head>Role</Table.Head>
|
|
301
|
+
</Table.Row>
|
|
302
|
+
</Table.Header>
|
|
303
|
+
<Table.Body>
|
|
304
|
+
{#each users as user (user.id)}
|
|
305
|
+
<Table.Row>
|
|
306
|
+
<Table.Cell>{user.name}</Table.Cell>
|
|
307
|
+
<Table.Cell>{user.email}</Table.Cell>
|
|
308
|
+
<Table.Cell>
|
|
309
|
+
<Badge variant="soft">{user.role}</Badge>
|
|
310
|
+
</Table.Cell>
|
|
311
|
+
</Table.Row>
|
|
312
|
+
{/each}
|
|
313
|
+
</Table.Body>
|
|
314
|
+
</Table.Root>
|
|
315
|
+
</div>
|
|
315
316
|
</Container>
|
|
317
|
+
|
|
318
|
+
<style>
|
|
319
|
+
.page-stack { display: grid; gap: var(--dry-space-6); }
|
|
320
|
+
.page-header {
|
|
321
|
+
display: grid;
|
|
322
|
+
grid-template-columns: 1fr auto;
|
|
323
|
+
align-items: center;
|
|
324
|
+
}
|
|
325
|
+
</style>
|
|
316
326
|
```
|
|
317
327
|
|
|
318
328
|
## Anti-Patterns
|
|
319
329
|
|
|
320
|
-
###
|
|
330
|
+
### Using flexbox or inline styles
|
|
321
331
|
|
|
322
332
|
```svelte
|
|
323
|
-
<!--
|
|
324
|
-
<
|
|
325
|
-
<Stack gap="sm">
|
|
326
|
-
<p>One item</p>
|
|
327
|
-
</Stack>
|
|
328
|
-
</Stack>
|
|
329
|
-
|
|
330
|
-
<!-- Correct: flat when possible -->
|
|
331
|
-
<Stack gap="md">
|
|
332
|
-
<p>One item</p>
|
|
333
|
-
</Stack>
|
|
334
|
-
```
|
|
333
|
+
<!-- Wrong: flexbox -->
|
|
334
|
+
<div style="display: flex; gap: 1rem;">...</div>
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
<!-- Wrong: inline styles -->
|
|
337
|
+
<div style="max-width: 1200px; margin: 0 auto;">...</div>
|
|
337
338
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
<
|
|
341
|
-
<h2>Title</h2>
|
|
342
|
-
<button>Action</button>
|
|
343
|
-
</div>
|
|
339
|
+
<!-- Right: scoped grid -->
|
|
340
|
+
<div class="layout">...</div>
|
|
341
|
+
<Container>...</Container>
|
|
344
342
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
<Button>Action</Button>
|
|
349
|
-
</Flex>
|
|
343
|
+
<style>
|
|
344
|
+
.layout { display: grid; gap: var(--dry-space-4); }
|
|
345
|
+
</style>
|
|
350
346
|
```
|
|
351
347
|
|
|
352
348
|
### Forgetting Card.Root in form layouts
|
|
353
349
|
|
|
354
350
|
```svelte
|
|
355
|
-
<!--
|
|
351
|
+
<!-- Wrong: bare Card -->
|
|
356
352
|
<Card>
|
|
357
|
-
|
|
358
|
-
<Stack gap="md">...</Stack>
|
|
359
|
-
</Card.Content>
|
|
353
|
+
<Card.Content>...</Card.Content>
|
|
360
354
|
</Card>
|
|
361
355
|
|
|
362
|
-
<!--
|
|
356
|
+
<!-- Right: Card.Root -->
|
|
363
357
|
<Card.Root>
|
|
364
|
-
|
|
365
|
-
<Stack gap="md">...</Stack>
|
|
366
|
-
</Card.Content>
|
|
358
|
+
<Card.Content>...</Card.Content>
|
|
367
359
|
</Card.Root>
|
|
368
360
|
```
|
|
369
361
|
|
|
@@ -371,37 +363,34 @@ Use Field.Error to show validation messages.
|
|
|
371
363
|
|
|
372
364
|
Before using any component, call `compose` to get the correct component and usage snippet. This table is a quick reference — `compose` has full snippets and anti-patterns.
|
|
373
365
|
|
|
374
|
-
| UI Need | Use This | NOT This
|
|
375
|
-
| ----------------- | -------------------------------------- |
|
|
376
|
-
| Date picker | `DatePicker.Root` | `<
|
|
377
|
-
| Date range | `DateRangePicker.Root` | Two `<
|
|
378
|
-
| Time input | `TimeInput` | `<
|
|
379
|
-
| Dropdown select | `Select.Root` | `<select>`
|
|
380
|
-
| Searchable select | `Combobox.Root` | `<input>` + custom dropdown
|
|
381
|
-
| Modal dialog | `Dialog.Root` | `<dialog>` or manual overlay
|
|
382
|
-
| Confirmation | `AlertDialog.Root` | `window.confirm()`
|
|
383
|
-
| Side panel | `Drawer.Root` | Fixed-position div
|
|
384
|
-
| Data table | `Table.Root` | `<table>`
|
|
385
|
-
| Person image | `Avatar` | Emoji or bare `<img>`
|
|
386
|
-
| Content image | `Image` | Bare `<img>`
|
|
387
|
-
| Multi-step flow | `Stepper.Root` | Manual step divs
|
|
388
|
-
| Progress bar | `Progress` | CSS-only bar
|
|
389
|
-
| Inline chart | `Sparkline` | Manual SVG
|
|
390
|
-
| Full chart | `Chart.Root` | External chart library
|
|
391
|
-
|
|
|
392
|
-
|
|
|
393
|
-
|
|
|
394
|
-
|
|
|
395
|
-
|
|
|
396
|
-
|
|
|
397
|
-
|
|
|
398
|
-
|
|
|
399
|
-
|
|
|
400
|
-
|
|
|
401
|
-
|
|
|
402
|
-
| File upload | `FileUpload.Root` | `<input type="file">` |
|
|
403
|
-
| Color picker | `ColorPicker.Root` | `<input type="color">` |
|
|
404
|
-
| Collapsible | `Accordion.Root` or `Collapsible.Root` | Manual toggle with if/else |
|
|
366
|
+
| UI Need | Use This | NOT This |
|
|
367
|
+
| ----------------- | -------------------------------------- | --------------------------- |
|
|
368
|
+
| Date picker | `DatePicker.Root` | `<input type="date">` |
|
|
369
|
+
| Date range | `DateRangePicker.Root` | Two `<input type="date">` |
|
|
370
|
+
| Time input | `TimeInput` | `<input type="time">` |
|
|
371
|
+
| Dropdown select | `Select.Root` | `<select>` |
|
|
372
|
+
| Searchable select | `Combobox.Root` | `<input>` + custom dropdown |
|
|
373
|
+
| Modal dialog | `Dialog.Root` | `<dialog>` or manual overlay|
|
|
374
|
+
| Confirmation | `AlertDialog.Root` | `window.confirm()` |
|
|
375
|
+
| Side panel | `Drawer.Root` | Fixed-position div |
|
|
376
|
+
| Data table | `Table.Root` | `<table>` |
|
|
377
|
+
| Person image | `Avatar` | Emoji or bare `<img>` |
|
|
378
|
+
| Content image | `Image` | Bare `<img>` |
|
|
379
|
+
| Multi-step flow | `Stepper.Root` | Manual step divs |
|
|
380
|
+
| Progress bar | `Progress` | CSS-only bar |
|
|
381
|
+
| Inline chart | `Sparkline` | Manual SVG |
|
|
382
|
+
| Full chart | `Chart.Root` | External chart library |
|
|
383
|
+
| Max-width wrapper | `Container` | `max-width` + `margin: auto`|
|
|
384
|
+
| Form field | `Field.Root` + `Label` + Input | `<label>` + `<input>` |
|
|
385
|
+
| Status indicator | `Badge` | Colored `<span>` |
|
|
386
|
+
| Loading state | `Skeleton` or `Spinner` | Text "Loading..." |
|
|
387
|
+
| Empty state | `EmptyState.Root` | Custom empty div |
|
|
388
|
+
| Notifications | `Toast` | Alert div |
|
|
389
|
+
| Keyboard shortcut | `Kbd` | `<code>` |
|
|
390
|
+
| Code display | `CodeBlock` | `<pre><code>` |
|
|
391
|
+
| File upload | `FileUpload.Root` | `<input type="file">` |
|
|
392
|
+
| Color picker | `ColorPicker.Root` | `<input type="color">` |
|
|
393
|
+
| Collapsible | `Accordion.Root` or `Collapsible.Root` | Manual toggle with if/else |
|
|
405
394
|
|
|
406
395
|
## Composition Recipes
|
|
407
396
|
|
|
@@ -419,7 +408,7 @@ Call `compose` with any recipe name to get a full working snippet.
|
|
|
419
408
|
| `sidebar-layout` | Page with sidebar nav | Grid, Sidebar, PageHeader |
|
|
420
409
|
| `dashboard-page` | Full dashboard layout | Grid, Sidebar, StatCard, Chart, Table |
|
|
421
410
|
| `user-profile-card` | User info card | Card, Avatar, Text, Badge, Button |
|
|
422
|
-
| `notification-list` | Notification feed |
|
|
411
|
+
| `notification-list` | Notification feed | Card, Avatar, Text, Badge |
|
|
423
412
|
| `command-bar` | Command palette trigger | CommandPalette, Hotkey |
|
|
424
413
|
| `file-upload-form` | File upload with progress | Card, FileUpload, Progress, Button |
|
|
425
|
-
| `pricing-table` | Pricing comparison |
|
|
414
|
+
| `pricing-table` | Pricing comparison | Card, Text, Button, Badge |
|