@spaethtech/svelte-ui 0.3.0 → 0.3.1-dev.10.ff03a8d

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/docs/usage.md ADDED
@@ -0,0 +1,450 @@
1
+ # Usage Examples
2
+
3
+ Common usage patterns for svelte-ui components. For the full, live gallery, run the demo and
4
+ browse every component at **http://localhost:5173**.
5
+
6
+ ## Installation
7
+
8
+ Published to npm:
9
+
10
+ ```bash
11
+ npm install @spaethtech/svelte-ui
12
+ ```
13
+
14
+ During active co-development you can instead link a local checkout with a `file:` dependency:
15
+
16
+ ```jsonc
17
+ // package.json
18
+ "dependencies": {
19
+ "@spaethtech/svelte-ui": "file:../svelte-ui"
20
+ }
21
+ ```
22
+
23
+ Peer dependencies:
24
+
25
+ ```bash
26
+ npm install svelte@^5 tailwindcss@^4 unplugin-icons@^23 @iconify-json/mdi@^1
27
+ ```
28
+
29
+ ## Setup
30
+
31
+ **1. Vite — add the icon resolver** (`vite.config.ts`):
32
+
33
+ ```ts
34
+ import Icons from "unplugin-icons/vite";
35
+
36
+ export default defineConfig({
37
+ plugins: [/* sveltekit(), tailwindcss(), */ Icons({ compiler: "svelte" })],
38
+ });
39
+ ```
40
+
41
+ **2. CSS — import Tailwind + the theme and scan the dist** (`app.css`, Tailwind v4):
42
+
43
+ ```css
44
+ @import "tailwindcss";
45
+ @import "@spaethtech/svelte-ui/theme.css";
46
+
47
+ /* Generate the utility classes the components use (path relative to this file). */
48
+ @source "../node_modules/@spaethtech/svelte-ui/dist";
49
+ ```
50
+
51
+ ## Basic Import
52
+
53
+ ```svelte
54
+ <script>
55
+ import { Button, Input, Select } from "@spaethtech/svelte-ui";
56
+ </script>
57
+ ```
58
+
59
+ ## Icons
60
+
61
+ There is **no `Icon` component**. Import an MDI icon component from `~icons/mdi/*` and render it
62
+ into a component's `icon` snippet — the library sizes and colors it:
63
+
64
+ ```svelte
65
+ <script>
66
+ import { Button, Input } from "@spaethtech/svelte-ui";
67
+ import IconHome from "~icons/mdi/home";
68
+ import IconSearch from "~icons/mdi/magnify";
69
+ </script>
70
+
71
+ <Button title="Home">{#snippet icon()}<IconHome />{/snippet}</Button>
72
+
73
+ <Input placeholder="Search…">
74
+ {#snippet icon()}<IconSearch />{/snippet}
75
+ </Input>
76
+ ```
77
+
78
+ ## Component Examples
79
+
80
+ ### Button
81
+
82
+ ```svelte
83
+ <script>
84
+ import { Button } from "@spaethtech/svelte-ui";
85
+ import IconContentSave from "~icons/mdi/content-save";
86
+ import IconPencil from "~icons/mdi/pencil";
87
+ import IconDelete from "~icons/mdi/delete";
88
+ </script>
89
+
90
+ <!-- Text button -->
91
+ <Button text="Click me" variant="primary" onclick={() => alert("Clicked!")} />
92
+
93
+ <!-- Button with icon -->
94
+ <Button text="Save" variant="success">
95
+ {#snippet icon()}<IconContentSave />{/snippet}
96
+ </Button>
97
+
98
+ <!-- Icon-only button (title → themed tooltip + aria-label) -->
99
+ <Button title="Edit" variant="plain" size="sm">
100
+ {#snippet icon()}<IconPencil />{/snippet}
101
+ </Button>
102
+
103
+ <!-- Button as link -->
104
+ <Button href="/home" text="Home" variant="ghost" />
105
+
106
+ <!-- Danger -->
107
+ <Button text="Delete" variant="danger">
108
+ {#snippet icon()}<IconDelete />{/snippet}
109
+ </Button>
110
+ ```
111
+
112
+ ### Input
113
+
114
+ ```svelte
115
+ <script>
116
+ import { Input } from "@spaethtech/svelte-ui";
117
+ import IconAccount from "~icons/mdi/account";
118
+
119
+ let username = $state("");
120
+ let validated = $state("");
121
+ </script>
122
+
123
+ <!-- Basic -->
124
+ <Input bind:value={username} placeholder="Username" />
125
+
126
+ <!-- With a leading icon -->
127
+ <Input bind:value={username} placeholder="Enter username" iconPosition="left">
128
+ {#snippet icon()}<IconAccount />{/snippet}
129
+ </Input>
130
+
131
+ <!-- With validation -->
132
+ <Input
133
+ bind:value={validated}
134
+ validate={(v) => (v.length >= 3 ? true : "Minimum 3 characters")}
135
+ placeholder="Validated input"
136
+ />
137
+
138
+ <!-- Clickable trailing icon -->
139
+ <Input bind:value={username} iconClickable onIconClick={() => console.log("clicked")}>
140
+ {#snippet icon()}<IconAccount />{/snippet}
141
+ </Input>
142
+ ```
143
+
144
+ ### Select
145
+
146
+ ```svelte
147
+ <script>
148
+ import { Select } from "@spaethtech/svelte-ui";
149
+
150
+ let selected = $state("");
151
+ const options = [
152
+ { value: "1", label: "Option 1" },
153
+ { value: "2", label: "Option 2" },
154
+ { value: "3", label: "Option 3" },
155
+ ];
156
+ </script>
157
+
158
+ <!-- Static select -->
159
+ <Select bind:value={selected} {options} placeholder="Choose option" />
160
+
161
+ <!-- API-driven select with search -->
162
+ <Select
163
+ bind:value={selected}
164
+ url="/api/items"
165
+ searchParam="q"
166
+ placeholder="Search items..."
167
+ transform={(data) => data.results.map((r) => ({ value: r.id, label: r.name }))}
168
+ />
169
+
170
+ <!-- Custom item rendering -->
171
+ <Select bind:value={selected} {options}>
172
+ {#snippet itemSnippet(option)}
173
+ <div class="flex items-center gap-2">
174
+ <span class="font-bold">{option.value}</span>
175
+ <span>{option.label}</span>
176
+ </div>
177
+ {/snippet}
178
+ </Select>
179
+ ```
180
+
181
+ ### List
182
+
183
+ ```svelte
184
+ <script>
185
+ import { List } from "@spaethtech/svelte-ui";
186
+
187
+ let selected = $state([]);
188
+ const options = [
189
+ { value: "1", label: "Item 1" },
190
+ { value: "2", label: "Item 2" },
191
+ { value: "3", label: "Item 3" },
192
+ ];
193
+ </script>
194
+
195
+ <!-- Single select -->
196
+ <List bind:value={selected} {options} />
197
+
198
+ <!-- Multi-select with checkboxes -->
199
+ <List bind:value={selected} {options} multiSelect showCheckboxes />
200
+ ```
201
+
202
+ ### Checkbox
203
+
204
+ ```svelte
205
+ <script>
206
+ import { Checkbox } from "@spaethtech/svelte-ui";
207
+ let checked = $state(false);
208
+ </script>
209
+
210
+ <Checkbox bind:checked />
211
+ <Checkbox checked indeterminate />
212
+ <Checkbox disabled />
213
+ ```
214
+
215
+ ### ConfirmDialog
216
+
217
+ The common title + message + Confirm/Cancel modal (composed from a `Card` on the `Dialog` shell).
218
+
219
+ ```svelte
220
+ <script>
221
+ import { ConfirmDialog, Button } from "@spaethtech/svelte-ui";
222
+
223
+ let showConfirm = $state(false);
224
+ let showDanger = $state(false);
225
+ </script>
226
+
227
+ <!-- Basic confirmation -->
228
+ <Button text="Show Dialog" onclick={() => (showConfirm = true)} />
229
+ <ConfirmDialog
230
+ bind:isOpen={showConfirm}
231
+ title="Confirm Action"
232
+ message="Are you sure you want to proceed?"
233
+ onConfirm={() => console.log("Confirmed")}
234
+ />
235
+
236
+ <!-- Danger + must-decide (no backdrop/Escape dismiss) -->
237
+ <ConfirmDialog
238
+ bind:isOpen={showDanger}
239
+ title="Delete Item"
240
+ message="This action cannot be undone!"
241
+ variant="danger"
242
+ confirmText="Delete"
243
+ dismissible={false}
244
+ onConfirm={() => console.log("Deleted")}
245
+ />
246
+ ```
247
+
248
+ ### Dialog (shell)
249
+
250
+ A pure modal shell — you supply the content. Use it for anything beyond the confirm pattern; size it
251
+ with `width` / `height` (any CSS value).
252
+
253
+ ```svelte
254
+ <script>
255
+ import { Dialog, Card, CardHeader, CardFooter, Button, Input } from "@spaethtech/svelte-ui";
256
+
257
+ let open = $state(false);
258
+ </script>
259
+
260
+ <Button text="Open" onclick={() => (open = true)} />
261
+
262
+ <Dialog bind:isOpen={open} width="80vw" height="60vh">
263
+ <Card class="w-full h-full flex flex-col">
264
+ <CardHeader><h3 class="text-lg font-medium">Custom modal</h3></CardHeader>
265
+ <div class="flex-1 overflow-auto space-y-2">
266
+ <p>You own the Card — control its separator, variant, sizing, and body.</p>
267
+ <Input placeholder="Enter value" />
268
+ </div>
269
+ <CardFooter separator={false}>
270
+ <Button text="Close" variant="secondary" onclick={() => (open = false)} />
271
+ </CardFooter>
272
+ </Card>
273
+ </Dialog>
274
+ ```
275
+
276
+ ### Badge
277
+
278
+ ```svelte
279
+ <script>
280
+ import { Badge } from "@spaethtech/svelte-ui";
281
+ import IconCheck from "~icons/mdi/check";
282
+
283
+ let showBadge = $state(true);
284
+ </script>
285
+
286
+ <!-- Basic badges -->
287
+ <Badge text="New" variant="primary" />
288
+ <Badge text="Beta" variant="warning" size="sm" />
289
+ <Badge text="v1.0.0" variant="success" size="lg" />
290
+
291
+ <!-- Dismissible -->
292
+ {#if showBadge}
293
+ <Badge text="Alert" variant="danger" dismissible on:dismiss={() => (showBadge = false)} />
294
+ {/if}
295
+
296
+ <!-- Custom content -->
297
+ <Badge variant="success">
298
+ <IconCheck />
299
+ <span>Verified</span>
300
+ </Badge>
301
+ ```
302
+
303
+ ### TextArea
304
+
305
+ ```svelte
306
+ <script>
307
+ import { TextArea } from "@spaethtech/svelte-ui";
308
+
309
+ let notes = $state("");
310
+ let code = $state("");
311
+ </script>
312
+
313
+ <!-- Auto-resizing -->
314
+ <TextArea bind:value={notes} placeholder="Enter notes..." minRows={3} maxRows={10} />
315
+
316
+ <!-- Fixed height -->
317
+ <TextArea bind:value={code} minRows={5} maxRows={5} placeholder="Paste code here..." />
318
+
319
+ <!-- Read-only with copy control -->
320
+ <TextArea value="Read-only content" readonly allowCopy />
321
+ ```
322
+
323
+ ### Specialized Inputs
324
+
325
+ ```svelte
326
+ <script>
327
+ import { PasswordInput, EmailInput, SearchInput, NumberInput } from "@spaethtech/svelte-ui";
328
+
329
+ let password = $state("");
330
+ let email = $state("");
331
+ let search = $state("");
332
+ let quantity = $state(1);
333
+ </script>
334
+
335
+ <PasswordInput bind:value={password} placeholder="Enter password" />
336
+ <EmailInput bind:value={email} required placeholder="user@example.com" />
337
+ <SearchInput bind:value={search} placeholder="Search..." />
338
+ <NumberInput bind:value={quantity} min={1} max={100} />
339
+ ```
340
+
341
+ ### Tooltips (use:tooltip action)
342
+
343
+ There is no `Tooltip` component — tooltips are a Svelte action built on the `anchored` engine.
344
+
345
+ ```svelte
346
+ <script>
347
+ import { Button, tooltip } from "@spaethtech/svelte-ui";
348
+ </script>
349
+
350
+ <!-- Button drives the action from its `title` automatically -->
351
+ <Button title="Delete" variant="danger" />
352
+
353
+ <!-- Apply the action directly to any element -->
354
+ <span use:tooltip={{ text: "Any element", side: "top" }}>hover me</span>
355
+
356
+ <!-- Falls back to the element's native title (which it then suppresses) -->
357
+ <a href="/help" title="Get help" use:tooltip>?</a>
358
+ ```
359
+
360
+ ### Popup & Menu
361
+
362
+ ```svelte
363
+ <script>
364
+ import { Popup, Menu, Button } from "@spaethtech/svelte-ui";
365
+ import IconPencil from "~icons/mdi/pencil";
366
+ import IconDelete from "~icons/mdi/delete";
367
+
368
+ let trigger = $state();
369
+ let popupOpen = $state(false);
370
+ let menuOpen = $state(false);
371
+
372
+ const items = [
373
+ { label: "Edit", icon: IconPencil, onclick: () => console.log("edit") },
374
+ { label: "Delete", icon: IconDelete, onclick: () => console.log("delete") },
375
+ ];
376
+ </script>
377
+
378
+ <!-- Popover (arbitrary content) -->
379
+ <Button bind:element={trigger} text="Open" onclick={() => (popupOpen = !popupOpen)} />
380
+ <Popup anchor={trigger} bind:open={popupOpen} side="bottom" align="start">
381
+ <div class="p-3">…your content…</div>
382
+ </Popup>
383
+
384
+ <!-- Action menu -->
385
+ <Button bind:element={trigger} text="Actions" onclick={() => (menuOpen = !menuOpen)} />
386
+ <Menu anchor={trigger} bind:open={menuOpen} {items} align="end" />
387
+ ```
388
+
389
+ ### DataTable & Query (@spaethtech/svelte-ui/data)
390
+
391
+ The data components are driven by the headless layer. Build a grid with `createGrid` and a filter
392
+ bar with `Query` over a `DataSet`.
393
+
394
+ ```svelte
395
+ <script>
396
+ import { DataTable, Query } from "@spaethtech/svelte-ui";
397
+ import { createGrid } from "@spaethtech/svelte-ui/data";
398
+ import IconPencil from "~icons/mdi/pencil";
399
+
400
+ const grid = createGrid({
401
+ columns: [
402
+ { id: "name", label: "Name" },
403
+ { id: "email", label: "Email" },
404
+ { id: "status", label: "Status", sortKey: "status" },
405
+ ],
406
+ fetch: (request) => /* a reactive query returning { rows, total } */ myQuery(request),
407
+ });
408
+ </script>
409
+
410
+ <DataTable
411
+ {grid}
412
+ selectable
413
+ rowActions={(row) => [{ label: "Edit", icon: IconPencil, onclick: () => edit(row) }]}
414
+ />
415
+ ```
416
+
417
+ See the live demo for the full DataTable + Query walkthrough — http://localhost:5173.
418
+
419
+ ## Theme Integration
420
+
421
+ ### Using CSS Variables
422
+
423
+ ```svelte
424
+ <style>
425
+ .custom-component {
426
+ background: var(--ui-color-background);
427
+ color: var(--ui-color-text);
428
+ height: var(--ui-height);
429
+ }
430
+
431
+ .custom-button {
432
+ background: var(--ui-color-primary);
433
+ &:hover {
434
+ background: color-mix(in srgb, var(--ui-color-primary), black 12%);
435
+ }
436
+ }
437
+ /* For neutral surfaces (rows, ghost buttons, section bands) use the shared tints instead:
438
+ var(--ui-color-hover) / var(--ui-color-surface) / var(--ui-color-active). */
439
+ </style>
440
+ ```
441
+
442
+ ### Theme Selector
443
+
444
+ ```svelte
445
+ <script>
446
+ import { ThemeSelector } from "@spaethtech/svelte-ui";
447
+ </script>
448
+
449
+ <ThemeSelector />
450
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaethtech/svelte-ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.1-dev.10.ff03a8d",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/spaethtech/svelte-ui.git"
@@ -33,7 +33,11 @@
33
33
  "files": [
34
34
  "dist",
35
35
  "!dist/**/*.test.*",
36
- "!dist/**/*.spec.*"
36
+ "!dist/**/*.spec.*",
37
+ "CLAUDE.md",
38
+ "docs",
39
+ ".claude/skills/themes",
40
+ ".claude/skills/svelte-ui"
37
41
  ],
38
42
  "sideEffects": [
39
43
  "**/*.css"