@human-synthesis/norns-ui 0.0.12 → 0.0.14

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/COMPONENTS.md ADDED
@@ -0,0 +1,1462 @@
1
+ # norns-ui — component reference
2
+
3
+ Generated by `bun run docs:components` from `src/types/*.d.ts`. Do not edit by hand.
4
+
5
+ Every component under **components/** is auto-imported by name in `.n` files when the consumer wires `presetUI()` into `nornsAutoImport` (`Btn(variant="primary") Save`). Components under **motion/** are opt-in: `import { Reveal } from '@human-synthesis/norns-ui/motion'`. Snippet props (`children`, `header`, `trigger`, …) are filled with `+snippet('name')` blocks in Pug. Every component accepts `class` and merges it with `cn()`.
6
+
7
+ Live usage of every component: `norns-demo/src/routes/examples/ui/+page.n`.
8
+
9
+ ## Components
10
+
11
+ [Accordion](#accordion) · [Audio](#audio) · [Autocomplete](#autocomplete) · [Avatar](#avatar) · [AvatarGroup](#avatargroup) · [Badge](#badge) · [Banner](#banner) · [Breadcrumbs](#breadcrumbs) · [Btn](#btn) · [ButtonGroup](#buttongroup) · [Calendar](#calendar) · [Card](#card) · [Carousel](#carousel) · [Checkbox](#checkbox) · [Chip](#chip) · [Collapsible](#collapsible) · [ColorPicker](#colorpicker) · [ContextMenu](#contextmenu) · [CopyButton](#copybutton) · [DataTable](#datatable) · [DatePicker](#datepicker) · [DateRangePicker](#daterangepicker) · [Dialog](#dialog) · [Dropdown](#dropdown) · [Field](#field) · [FieldGroup](#fieldgroup) · [Form](#form) · [GradientText](#gradienttext) · [Header](#header) · [HeroBanner](#herobanner) · [HierarchicalMenu](#hierarchicalmenu) · [Icon](#icon) · [Image](#image) · [Input](#input) · [MegaMenu](#megamenu) · [MultiSelect](#multiselect) · [NumberInput](#numberinput) · [OtpField](#otpfield) · [Pagination](#pagination) · [Popover](#popover) · [Progress](#progress) · [ProgressCircular](#progresscircular) · [Radio](#radio) · [RippleButton](#ripplebutton) · [ScrollArea](#scrollarea) · [Select](#select) · [Separator](#separator) · [Sheet](#sheet) · [ShinyButton](#shinybutton) · [Skeleton](#skeleton) · [Stepper](#stepper) · [Surface](#surface) · [Switch](#switch) · [Tabs](#tabs) · [TagsInput](#tagsinput) · [Textarea](#textarea) · [ThemeToggler](#themetoggler) · [Timeline](#timeline) · [TimePicker](#timepicker) · [ToastProvider](#toastprovider) · [ToggleButton](#togglebutton) · [ToggleButtonGroup](#togglebuttongroup) · [Toolbar](#toolbar) · [Tooltip](#tooltip) · [Tree](#tree) · [Uploader](#uploader) · [Video](#video) · [Window](#window)
12
+
13
+ ### Accordion
14
+
15
+ `@human-synthesis/norns-ui/components/Accordion.n`
16
+
17
+ ```ts
18
+ export type AccordionItem = {
19
+ value: string;
20
+ title: string;
21
+ content?: Snippet;
22
+ };
23
+
24
+ export type AccordionProps = {
25
+ items?: AccordionItem[];
26
+ multiple?: boolean;
27
+ value?: string | string[];
28
+ class?: string;
29
+ };
30
+ ```
31
+
32
+ ### Audio
33
+
34
+ `@human-synthesis/norns-ui/components/Audio.n`
35
+
36
+ ```ts
37
+ export type AudioSource = { src: string; type?: string };
38
+
39
+ export type AudioProps = {
40
+ src?: string;
41
+ sources?: AudioSource[];
42
+ controls?: boolean;
43
+ autoplay?: boolean;
44
+ loop?: boolean;
45
+ muted?: boolean;
46
+ preload?: 'none' | 'metadata' | 'auto';
47
+ fallback?: string;
48
+ class?: string;
49
+ };
50
+ ```
51
+
52
+ ### Autocomplete
53
+
54
+ `@human-synthesis/norns-ui/components/Autocomplete.n`
55
+
56
+ ```ts
57
+ export type ComboboxItem = { value: string; label: string };
58
+
59
+ /** Remote option source: resolves the items for the current input, or a `{ data }` envelope. */
60
+ export type ComboboxSource = (term: string) => Promise<ComboboxItem[] | { data?: ComboboxItem[] } | null | undefined> | ComboboxItem[];
61
+
62
+ export type AutocompleteProps = {
63
+ /** Local options, filtered client-side by label. Ignored when `source` is set. */
64
+ items?: ComboboxItem[];
65
+ /** Remote options: called (debounced) with the typed text while the list is open. */
66
+ source?: ComboboxSource;
67
+ /** Milliseconds to wait after typing before calling `source`. Default 200. */
68
+ debounce?: number;
69
+ /** Minimum typed characters before `source` is called. Default 0. */
70
+ minChars?: number;
71
+ value?: string;
72
+ open?: boolean;
73
+ placeholder?: string;
74
+ emptyMessage?: string;
75
+ loadingMessage?: string;
76
+ disabled?: boolean;
77
+ name?: string;
78
+ id?: string;
79
+ error?: boolean;
80
+ };
81
+ ```
82
+
83
+ ### Avatar
84
+
85
+ `@human-synthesis/norns-ui/components/Avatar.n`
86
+
87
+ ```ts
88
+ export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
89
+
90
+ export type AvatarProps = {
91
+ src?: string;
92
+ /** Display name; first + last initial used as fallback when `src` is missing. */
93
+ name?: string;
94
+ size?: AvatarSize;
95
+ class?: string;
96
+ };
97
+ ```
98
+
99
+ ### AvatarGroup
100
+
101
+ `@human-synthesis/norns-ui/components/AvatarGroup.n`
102
+
103
+ ```ts
104
+ export type AvatarGroupItem = { src?: string; name?: string };
105
+
106
+ export type AvatarGroupProps = {
107
+ items?: AvatarGroupItem[];
108
+ max?: number;
109
+ size?: AvatarSize;
110
+ label?: string;
111
+ class?: string;
112
+ };
113
+ ```
114
+
115
+ ### Badge
116
+
117
+ `@human-synthesis/norns-ui/components/Badge.n`
118
+
119
+ ```ts
120
+ export type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
121
+ export type BadgeSize = 'sm' | 'md';
122
+
123
+ export type BadgeProps = {
124
+ variant?: BadgeVariant;
125
+ size?: BadgeSize;
126
+ children?: Snippet;
127
+ class?: string;
128
+ };
129
+ ```
130
+
131
+ ### Banner
132
+
133
+ `@human-synthesis/norns-ui/components/Banner.n`
134
+
135
+ ```ts
136
+ export type BannerVariant = 'info' | 'success' | 'warning' | 'danger';
137
+
138
+ export type BannerProps = {
139
+ variant?: BannerVariant;
140
+ /** Iconify icon name (e.g. `"lucide:alert-triangle"`). */
141
+ icon?: string;
142
+ actions?: Snippet;
143
+ children?: Snippet;
144
+ class?: string;
145
+ };
146
+ ```
147
+
148
+ ### Breadcrumbs
149
+
150
+ `@human-synthesis/norns-ui/components/Breadcrumbs.n`
151
+
152
+ ```ts
153
+ export type BreadcrumbItem = {
154
+ label: string;
155
+ href?: string;
156
+ };
157
+
158
+ export type BreadcrumbsProps = {
159
+ items?: BreadcrumbItem[];
160
+ separator?: string;
161
+ class?: string;
162
+ };
163
+ ```
164
+
165
+ ### Btn
166
+
167
+ `@human-synthesis/norns-ui/components/Btn.n`
168
+
169
+ ```ts
170
+ /**
171
+ * Type shim for `Btn.n`. Hand-rolled until we have a proper
172
+ * `svelte-package` build over the Civet+Pug source.
173
+ */
174
+
175
+
176
+ export type BtnVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
177
+ export type BtnSize = 'sm' | 'md' | 'lg';
178
+
179
+ export type BtnProps = Omit<HTMLButtonAttributes, 'class' | 'children'> & {
180
+ variant?: BtnVariant;
181
+ size?: BtnSize;
182
+ loading?: boolean;
183
+ disabled?: boolean;
184
+ type?: 'button' | 'submit' | 'reset';
185
+ /**
186
+ * Convenience: Iconify icon name (e.g. `"lucide:save"`). Renders an
187
+ * `<Icon>` in the leading slot. If you also pass a `leading` snippet,
188
+ * the snippet wins.
189
+ */
190
+ icon?: string;
191
+ /** When set, renders as an `<a>` styled like the button. */
192
+ href?: string;
193
+ target?: string;
194
+ rel?: string;
195
+ class?: string;
196
+ children?: Snippet;
197
+ leading?: Snippet;
198
+ trailing?: Snippet;
199
+ };
200
+ ```
201
+
202
+ ### ButtonGroup
203
+
204
+ `@human-synthesis/norns-ui/components/ButtonGroup.n`
205
+
206
+ ```ts
207
+ export type ButtonGroupProps = {
208
+ orientation?: 'horizontal' | 'vertical';
209
+ children?: Snippet;
210
+ class?: string;
211
+ };
212
+ ```
213
+
214
+ ### Calendar
215
+
216
+ `@human-synthesis/norns-ui/components/Calendar.n`
217
+
218
+ ```ts
219
+ export type CalendarDateRange = { start: string; end: string };
220
+
221
+ export type CalendarProps = {
222
+ /** ISO YYYY-MM-DD. Bindable. */
223
+ value?: string;
224
+ /** Range mode. Bindable. Use instead of `value` when `range` is true. */
225
+ rangeValue?: CalendarDateRange;
226
+ range?: boolean;
227
+ /** ISO YYYY-MM-DD inclusive. */
228
+ min?: string;
229
+ max?: string;
230
+ label?: string;
231
+ onchange?: (next: string | CalendarDateRange) => void;
232
+ class?: string;
233
+ };
234
+ ```
235
+
236
+ ### Card
237
+
238
+ `@human-synthesis/norns-ui/components/Card.n`
239
+
240
+ ```ts
241
+ export type CardProps = {
242
+ /** Wrap body in `.card-body` (default true). Set false for full-bleed content. */
243
+ padded?: boolean;
244
+ /** Apply `.card-interactive` hover/focus treatment. Implicit if `href` is set. */
245
+ interactive?: boolean;
246
+ /** Renders as `<a>` instead of `<div>` when set. */
247
+ href?: string;
248
+ target?: string;
249
+ rel?: string;
250
+ header?: Snippet;
251
+ footer?: Snippet;
252
+ children?: Snippet;
253
+ class?: string;
254
+ };
255
+ ```
256
+
257
+ ### Carousel
258
+
259
+ `@human-synthesis/norns-ui/components/Carousel.n`
260
+
261
+ ```ts
262
+ export type CarouselSlide = {
263
+ src?: string;
264
+ alt?: string;
265
+ [key: string]: unknown;
266
+ };
267
+
268
+ export type CarouselProps = {
269
+ slides?: CarouselSlide[];
270
+ index?: number;
271
+ autoplay?: boolean;
272
+ /** Autoplay interval in milliseconds. Default 5000. */
273
+ interval?: number;
274
+ /** Default aspect ratio applied if no explicit `class="aspect-..."` is passed. */
275
+ aspect?: 'video' | 'square' | 'wide' | 'none';
276
+ /** Custom slide renderer; receives `(slide, i)` and runs only for slides without `src`. */
277
+ children?: Snippet<[CarouselSlide, number]>;
278
+ class?: string;
279
+ };
280
+ ```
281
+
282
+ ### Checkbox
283
+
284
+ `@human-synthesis/norns-ui/components/Checkbox.n`
285
+
286
+ ```ts
287
+ export type CheckboxProps = Omit<HTMLInputAttributes, 'class' | 'type' | 'checked'> & {
288
+ checked?: boolean;
289
+ error?: boolean;
290
+ class?: string;
291
+ };
292
+ ```
293
+
294
+ ### Chip
295
+
296
+ `@human-synthesis/norns-ui/components/Chip.n`
297
+
298
+ ```ts
299
+ export type ChipProps = {
300
+ icon?: string;
301
+ removable?: boolean;
302
+ onremove?: (event: MouseEvent) => void;
303
+ children?: Snippet;
304
+ class?: string;
305
+ };
306
+ ```
307
+
308
+ ### Collapsible
309
+
310
+ `@human-synthesis/norns-ui/components/Collapsible.n`
311
+
312
+ ```ts
313
+ export type CollapsibleProps = {
314
+ open?: boolean;
315
+ title?: string;
316
+ trigger?: Snippet;
317
+ children?: Snippet;
318
+ onopenchange?: (open: boolean) => void;
319
+ class?: string;
320
+ };
321
+ ```
322
+
323
+ ### ColorPicker
324
+
325
+ `@human-synthesis/norns-ui/components/ColorPicker.n`
326
+
327
+ ```ts
328
+ export type ColorPickerProps = {
329
+ /** Hex value with leading `#`. Bindable. */
330
+ value?: string;
331
+ disabled?: boolean;
332
+ /** Hide the clipboard-copy button. */
333
+ hideCopy?: boolean;
334
+ name?: string;
335
+ id?: string;
336
+ class?: string;
337
+ };
338
+ ```
339
+
340
+ ### ContextMenu
341
+
342
+ `@human-synthesis/norns-ui/components/ContextMenu.n`
343
+
344
+ ```ts
345
+ export type ContextMenuItem = {
346
+ label?: string;
347
+ icon?: string;
348
+ separator?: boolean;
349
+ disabled?: boolean;
350
+ onSelect?: () => void;
351
+ /**
352
+ * NOTE: nested submenus are not supported in 0.0.6+ (the property is
353
+ * accepted but children are ignored). Re-introduce when the upstream
354
+ * design needs them.
355
+ */
356
+ children?: ContextMenuItem[];
357
+ };
358
+
359
+ export type ContextMenuProps = {
360
+ items?: ContextMenuItem[];
361
+ trigger?: Snippet;
362
+ triggerClass?: string;
363
+ };
364
+ ```
365
+
366
+ ### CopyButton
367
+
368
+ `@human-synthesis/norns-ui/components/CopyButton.n`
369
+
370
+ ```ts
371
+ export type CopyButtonProps = {
372
+ value?: string | number;
373
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
374
+ size?: 'sm' | 'md' | 'lg';
375
+ icon?: string;
376
+ disabled?: boolean;
377
+ label?: string;
378
+ oncopy?: (value: string | number) => void;
379
+ children?: Snippet;
380
+ class?: string;
381
+ };
382
+ ```
383
+
384
+ ### DataTable
385
+
386
+ `@human-synthesis/norns-ui/components/DataTable.n`
387
+
388
+ ```ts
389
+ export type DataTableColumn = {
390
+ key: string;
391
+ label?: string;
392
+ width?: string;
393
+ class?: string;
394
+ cellClass?: string;
395
+ sortable?: boolean;
396
+ /** Render the cell value (e.g. a date or number formatter). */
397
+ format?: (value: unknown, row: Record<string, unknown>) => string | number;
398
+ };
399
+
400
+ export type DataTableProps = {
401
+ columns?: DataTableColumn[];
402
+ rows?: Record<string, unknown>[];
403
+ striped?: boolean;
404
+ dense?: boolean;
405
+ stickyHeader?: boolean;
406
+ /** Dims the table and swaps the empty message while a page is being fetched (see `useList()`). */
407
+ loading?: boolean;
408
+ emptyMessage?: string;
409
+ loadingMessage?: string;
410
+ /**
411
+ * Currently-sorted column key. Bindable. The table only flips these two;
412
+ * sort `rows` yourself or bind them to `useList()` for server-side ordering.
413
+ */
414
+ sortKey?: string;
415
+ sortDir?: 'asc' | 'desc';
416
+ onrowclick?: (row: Record<string, unknown>, index: number) => void;
417
+ class?: string;
418
+ };
419
+ ```
420
+
421
+ ### DatePicker
422
+
423
+ `@human-synthesis/norns-ui/components/DatePicker.n`
424
+
425
+ ```ts
426
+ export type DatePickerProps = {
427
+ /** ISO date string (YYYY-MM-DD). Bindable. */
428
+ value?: string;
429
+ /** Whether the popover is open. Bindable. */
430
+ open?: boolean;
431
+ min?: string;
432
+ max?: string;
433
+ placeholder?: string;
434
+ disabled?: boolean;
435
+ /** Close the popover automatically once a date is picked (default true). */
436
+ closeOnSelect?: boolean;
437
+ /** aria-label override; defaults to "Pick a date". */
438
+ label?: string;
439
+ name?: string;
440
+ id?: string;
441
+ error?: boolean;
442
+ class?: string;
443
+ };
444
+ ```
445
+
446
+ ### DateRangePicker
447
+
448
+ `@human-synthesis/norns-ui/components/DateRangePicker.n`
449
+
450
+ ```ts
451
+ export type DateRange = { start: string; end: string };
452
+
453
+ export type DateRangePickerProps = {
454
+ value?: DateRange;
455
+ /** Whether the popover is open. Bindable. */
456
+ open?: boolean;
457
+ min?: string;
458
+ max?: string;
459
+ placeholder?: string;
460
+ disabled?: boolean;
461
+ class?: string;
462
+ };
463
+ ```
464
+
465
+ ### Dialog
466
+
467
+ `@human-synthesis/norns-ui/components/Dialog.n`
468
+
469
+ ```ts
470
+ export type DialogProps = {
471
+ open?: boolean;
472
+ title?: string;
473
+ description?: string;
474
+ hideClose?: boolean;
475
+ /** Close on overlay click and Escape. Default true. */
476
+ dismissable?: boolean;
477
+ /** Used as `aria-label` on the dialog when no `title` is provided. */
478
+ ariaLabel?: string;
479
+ trigger?: Snippet;
480
+ actions?: Snippet;
481
+ children?: Snippet;
482
+ triggerClass?: string;
483
+ overlayClass?: string;
484
+ class?: string;
485
+ };
486
+ ```
487
+
488
+ ### Dropdown
489
+
490
+ `@human-synthesis/norns-ui/components/Dropdown.n`
491
+
492
+ ```ts
493
+ export type DropdownItem = {
494
+ label?: string;
495
+ icon?: string;
496
+ separator?: boolean;
497
+ disabled?: boolean;
498
+ onSelect?: () => void;
499
+ };
500
+
501
+ export type DropdownProps = {
502
+ open?: boolean;
503
+ side?: 'top' | 'right' | 'bottom' | 'left';
504
+ align?: 'start' | 'center' | 'end';
505
+ sideOffset?: number;
506
+ items?: DropdownItem[];
507
+ trigger?: Snippet;
508
+ children?: Snippet;
509
+ triggerClass?: string;
510
+ class?: string;
511
+ };
512
+ ```
513
+
514
+ ### Field
515
+
516
+ `@human-synthesis/norns-ui/components/Field.n`
517
+
518
+ ```ts
519
+ export type FieldProps = {
520
+ /** Label text rendered above the control. Used as id-derivation fallback if no `id`/`name` is given. */
521
+ label?: string;
522
+ /** Helper text shown below the control when there's no error. */
523
+ help?: string;
524
+ /**
525
+ * Explicit error message. Overrides any auto-resolved error from the
526
+ * parent `<Form form={...}>` context. Switches the inner control to
527
+ * error styling.
528
+ */
529
+ error?: string;
530
+ /**
531
+ * Field name — used to look up an auto-error from the parent `<Form>`
532
+ * context (matching `form.errors[*].path[0].key`) and as the default
533
+ * id for the inner control. When set, an inner `<Input>` doesn't need
534
+ * an explicit `name=` either if it inherits from this Field.
535
+ */
536
+ name?: string;
537
+ /** Adds a red asterisk after the label. Cosmetic; pair with `required` on the actual input. */
538
+ required?: boolean;
539
+ /** Explicit id for the control. Otherwise: `name`, then slugified `label`, then undefined. */
540
+ id?: string;
541
+ /** Optional class merged into the field wrapper. */
542
+ class?: string;
543
+ /** Snippet that renders the actual control. Children should call `<Input />`, `<Textarea />`, etc. */
544
+ children?: Snippet;
545
+ };
546
+ ```
547
+
548
+ ### FieldGroup
549
+
550
+ `@human-synthesis/norns-ui/components/FieldGroup.n`
551
+
552
+ ```ts
553
+ export type FieldGroupProps = {
554
+ /** Optional legend text rendered as the fieldset's `<legend>`. */
555
+ legend?: string;
556
+ class?: string;
557
+ children?: Snippet;
558
+ };
559
+ ```
560
+
561
+ ### Form
562
+
563
+ `@human-synthesis/norns-ui/components/Form.n`
564
+
565
+ ```ts
566
+ /**
567
+ * The shape returned by `fail(400, { errors, values })` from a SvelteKit
568
+ * action — what `+page.server.c`'s `page.actions` produces on validation
569
+ * failure (or a manually-constructed equivalent). `errors` is a valibot
570
+ * issue list; `values` is the raw form data echoed back for re-rendering.
571
+ */
572
+ export type FormActionResult = {
573
+ errors?: Array<{ path?: Array<{ key?: string }>; message: string }>;
574
+ values?: Record<string, string>;
575
+ } | null | undefined;
576
+
577
+ export type FormProps = Omit<HTMLFormAttributes, 'class' | 'children'> & {
578
+ method?: 'GET' | 'POST';
579
+ action?: string;
580
+ enctype?: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';
581
+ /**
582
+ * Pass the page's `form` prop here. Form derives a `name → message` errors
583
+ * map and exposes it via context so descendant `<Field name="...">` looks
584
+ * up its own error automatically.
585
+ */
586
+ form?: FormActionResult;
587
+ /**
588
+ * API mode. Intercepts the submit, collects the fields into a plain object
589
+ * and calls this instead of posting the form — typically
590
+ * `(values) => api.post('/api/notes', values)` against a norns `route()`.
591
+ * A rejection whose `body.issues` (or `issues`) is a valibot issue list is
592
+ * mapped into the same errors context as `form`.
593
+ */
594
+ submit?: (values: Record<string, FormDataEntryValue>, event: SubmitEvent) => Promise<unknown> | unknown;
595
+ /** Called with the resolved value of `submit`. */
596
+ onsuccess?: (result: unknown, values: Record<string, FormDataEntryValue>) => void;
597
+ /** Called when `submit` rejects (after the errors map was updated). */
598
+ onerror?: (error: unknown) => void;
599
+ /** True while `submit` is pending. Bindable. */
600
+ submitting?: boolean;
601
+ /** API mode: clear the fields after `submit` resolves. */
602
+ resetOnSuccess?: boolean;
603
+ class?: string;
604
+ children?: Snippet;
605
+ };
606
+ ```
607
+
608
+ ### GradientText
609
+
610
+ `@human-synthesis/norns-ui/components/GradientText.n`
611
+
612
+ ```ts
613
+ export type GradientTextProps = {
614
+ /** Start color. Default `var(--color-primary-500)`. Accepts any CSS color. */
615
+ from?: string;
616
+ /** End color. Default `var(--color-info-500)`. */
617
+ to?: string;
618
+ /** Optional middle stop. */
619
+ via?: string;
620
+ /** Animate the gradient via background-position keyframes. Default true. */
621
+ animate?: boolean;
622
+ /** Gradient angle in degrees. Default 90. */
623
+ angle?: number;
624
+ children?: Snippet;
625
+ class?: string;
626
+ };
627
+ ```
628
+
629
+ ### Header
630
+
631
+ `@human-synthesis/norns-ui/components/Header.n`
632
+
633
+ ```ts
634
+ export type HeaderProps = {
635
+ sticky?: boolean;
636
+ brand?: Snippet;
637
+ nav?: Snippet;
638
+ actions?: Snippet;
639
+ class?: string;
640
+ };
641
+ ```
642
+
643
+ ### HeroBanner
644
+
645
+ `@human-synthesis/norns-ui/components/HeroBanner.n`
646
+
647
+ ```ts
648
+ export type HeroBannerProps = {
649
+ title: string;
650
+ description?: string;
651
+ /** Background/decorative image src; rendered with reduced opacity behind the content. */
652
+ image?: string;
653
+ align?: 'left' | 'center';
654
+ /** Apply animated `GradientText` to the title. Default false. */
655
+ gradient?: boolean;
656
+ actions?: Snippet;
657
+ children?: Snippet;
658
+ class?: string;
659
+ };
660
+ ```
661
+
662
+ ### HierarchicalMenu
663
+
664
+ `@human-synthesis/norns-ui/components/HierarchicalMenu.n`
665
+
666
+ ```ts
667
+ export type HierarchicalMenuItem = {
668
+ label: string;
669
+ href?: string;
670
+ icon?: string;
671
+ description?: string;
672
+ children?: HierarchicalMenuItem[];
673
+ };
674
+
675
+ export type HierarchicalMenuProps = {
676
+ items?: HierarchicalMenuItem[];
677
+ /** aria-label for the <nav> wrapper. */
678
+ label?: string;
679
+ onSelect?: (item: HierarchicalMenuItem) => void;
680
+ class?: string;
681
+ };
682
+ ```
683
+
684
+ ### Icon
685
+
686
+ `@human-synthesis/norns-ui/components/Icon.n`
687
+
688
+ ```ts
689
+ export type IconProps = {
690
+ /**
691
+ * Iconify icon name in `<set>:<name>` form, e.g. `"lucide:check"` or
692
+ * `"mdi:home"`. The `@iconify-json/lucide` package ships with the library;
693
+ * other sets can be installed alongside (`@iconify-json/heroicons`, etc.).
694
+ */
695
+ name: string;
696
+ /**
697
+ * Tailwind size utility for the icon, e.g. `"size-4"`, `"size-5"`. Default
698
+ * `"size-4"`. Pass any class string accepted by Tailwind.
699
+ */
700
+ size?: string;
701
+ flip?: 'horizontal' | 'vertical' | 'horizontal,vertical';
702
+ rotate?: 90 | 180 | 270 | string;
703
+ class?: string;
704
+ };
705
+ ```
706
+
707
+ ### Image
708
+
709
+ `@human-synthesis/norns-ui/components/Image.n`
710
+
711
+ ```ts
712
+ export type ImageProps = {
713
+ src: string;
714
+ alt?: string;
715
+ loading?: 'lazy' | 'eager';
716
+ fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
717
+ rounded?: boolean | 'sm' | 'md' | 'lg' | 'full';
718
+ width?: number | string;
719
+ height?: number | string;
720
+ class?: string;
721
+ };
722
+ ```
723
+
724
+ ### Input
725
+
726
+ `@human-synthesis/norns-ui/components/Input.n`
727
+
728
+ ```ts
729
+ export type InputSize = 'sm' | 'md' | 'lg';
730
+ export type InputType =
731
+ | 'text'
732
+ | 'email'
733
+ | 'password'
734
+ | 'number'
735
+ | 'tel'
736
+ | 'url'
737
+ | 'search'
738
+ | 'date'
739
+ | 'time'
740
+ | 'datetime-local'
741
+ | 'month'
742
+ | 'week';
743
+
744
+ export type InputProps = Omit<HTMLInputAttributes, 'class' | 'type' | 'size' | 'value'> & {
745
+ value?: string | number;
746
+ type?: InputType;
747
+ size?: InputSize;
748
+ /** Force the error styling regardless of the parent Field's error state. */
749
+ error?: boolean;
750
+ class?: string;
751
+ };
752
+ ```
753
+
754
+ ### MegaMenu
755
+
756
+ `@human-synthesis/norns-ui/components/MegaMenu.n`
757
+
758
+ ```ts
759
+ export type MegaMenuItem = {
760
+ label: string;
761
+ href?: string;
762
+ icon?: string;
763
+ description?: string;
764
+ };
765
+
766
+ export type MegaMenuColumn = {
767
+ title?: string;
768
+ items: MegaMenuItem[];
769
+ };
770
+
771
+ export type MegaMenuSection = {
772
+ label: string;
773
+ cols?: 2 | 3 | 4;
774
+ columns: MegaMenuColumn[];
775
+ };
776
+
777
+ export type MegaMenuProps = {
778
+ sections?: MegaMenuSection[];
779
+ /** aria-label for the <nav> wrapper. */
780
+ label?: string;
781
+ class?: string;
782
+ };
783
+ ```
784
+
785
+ ### MultiSelect
786
+
787
+ `@human-synthesis/norns-ui/components/MultiSelect.n`
788
+
789
+ ```ts
790
+ export type MultiSelectProps = {
791
+ /** Local options, filtered client-side by label. Ignored when `source` is set. */
792
+ items?: ComboboxItem[];
793
+ /** Remote options: called (debounced) with the typed text while the list is open. Picked labels are remembered across searches. */
794
+ source?: ComboboxSource;
795
+ /** Milliseconds to wait after typing before calling `source`. Default 200. */
796
+ debounce?: number;
797
+ /** Minimum typed characters before `source` is called. Default 0. */
798
+ minChars?: number;
799
+ value?: string[];
800
+ open?: boolean;
801
+ placeholder?: string;
802
+ emptyMessage?: string;
803
+ loadingMessage?: string;
804
+ disabled?: boolean;
805
+ name?: string;
806
+ id?: string;
807
+ error?: boolean;
808
+ };
809
+ ```
810
+
811
+ ### NumberInput
812
+
813
+ `@human-synthesis/norns-ui/components/NumberInput.n`
814
+
815
+ ```ts
816
+ export type NumberInputProps = {
817
+ value?: number;
818
+ min?: number;
819
+ max?: number;
820
+ stepSize?: number;
821
+ disabled?: boolean;
822
+ readonly?: boolean;
823
+ name?: string;
824
+ id?: string;
825
+ error?: boolean;
826
+ class?: string;
827
+ };
828
+ ```
829
+
830
+ ### OtpField
831
+
832
+ `@human-synthesis/norns-ui/components/OtpField.n`
833
+
834
+ ```ts
835
+ export type OtpFieldProps = {
836
+ value?: string;
837
+ length?: number;
838
+ mask?: boolean;
839
+ label?: string;
840
+ oncomplete?: (value: string) => void;
841
+ class?: string;
842
+ };
843
+ ```
844
+
845
+ ### Pagination
846
+
847
+ `@human-synthesis/norns-ui/components/Pagination.n`
848
+
849
+ ```ts
850
+ export type PaginationProps = {
851
+ page?: number;
852
+ total?: number;
853
+ pageSize?: number;
854
+ /** How many pages to show on each side of the current. Default 1. */
855
+ siblingCount?: number;
856
+ class?: string;
857
+ };
858
+ ```
859
+
860
+ ### Popover
861
+
862
+ `@human-synthesis/norns-ui/components/Popover.n`
863
+
864
+ ```ts
865
+ export type PopoverSide = 'top' | 'right' | 'bottom' | 'left';
866
+ export type PopoverAlign = 'start' | 'center' | 'end';
867
+
868
+ export type PopoverProps = {
869
+ open?: boolean;
870
+ side?: PopoverSide;
871
+ align?: PopoverAlign;
872
+ sideOffset?: number;
873
+ trigger?: Snippet;
874
+ children?: Snippet;
875
+ triggerClass?: string;
876
+ class?: string;
877
+ };
878
+ ```
879
+
880
+ ### Progress
881
+
882
+ `@human-synthesis/norns-ui/components/Progress.n`
883
+
884
+ ```ts
885
+ export type ProgressVariant = 'primary' | 'success' | 'warning' | 'danger';
886
+
887
+ export type ProgressProps = {
888
+ value?: number;
889
+ max?: number;
890
+ variant?: ProgressVariant;
891
+ indeterminate?: boolean;
892
+ class?: string;
893
+ };
894
+ ```
895
+
896
+ ### ProgressCircular
897
+
898
+ `@human-synthesis/norns-ui/components/ProgressCircular.n`
899
+
900
+ ```ts
901
+ export type ProgressCircularSize = 'sm' | 'md' | 'lg';
902
+
903
+ export type ProgressCircularProps = {
904
+ value?: number;
905
+ max?: number;
906
+ size?: ProgressCircularSize;
907
+ indeterminate?: boolean;
908
+ class?: string;
909
+ };
910
+ ```
911
+
912
+ ### Radio
913
+
914
+ `@human-synthesis/norns-ui/components/Radio.n`
915
+
916
+ ```ts
917
+ export type RadioProps = Omit<HTMLInputAttributes, 'class' | 'type'> & {
918
+ /** Two-way bound shared group value — pass `bind:group={selected}` from parent. */
919
+ group?: string;
920
+ value?: string;
921
+ error?: boolean;
922
+ class?: string;
923
+ };
924
+ ```
925
+
926
+ ### RippleButton
927
+
928
+ `@human-synthesis/norns-ui/components/RippleButton.n`
929
+
930
+ ```ts
931
+ export type RippleButtonProps = {
932
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
933
+ size?: 'sm' | 'md' | 'lg';
934
+ type?: 'button' | 'submit' | 'reset';
935
+ disabled?: boolean;
936
+ onclick?: (event: MouseEvent) => void;
937
+ icon?: string;
938
+ children?: Snippet;
939
+ class?: string;
940
+ };
941
+ ```
942
+
943
+ ### ScrollArea
944
+
945
+ `@human-synthesis/norns-ui/components/ScrollArea.n`
946
+
947
+ ```ts
948
+ export type ScrollAreaProps = {
949
+ /** Allow horizontal scrolling instead of just vertical. */
950
+ horizontal?: boolean;
951
+ children?: Snippet;
952
+ class?: string;
953
+ };
954
+ ```
955
+
956
+ ### Select
957
+
958
+ `@human-synthesis/norns-ui/components/Select.n`
959
+
960
+ ```ts
961
+ export type SelectProps = Omit<HTMLSelectAttributes, 'class' | 'value' | 'children'> & {
962
+ value?: string | number | string[];
963
+ error?: boolean;
964
+ class?: string;
965
+ /** `<option>` children. Pass via Pug body. */
966
+ children?: Snippet;
967
+ };
968
+ ```
969
+
970
+ ### Separator
971
+
972
+ `@human-synthesis/norns-ui/components/Separator.n`
973
+
974
+ ```ts
975
+ export type SeparatorProps = {
976
+ orientation?: 'horizontal' | 'vertical';
977
+ class?: string;
978
+ };
979
+ ```
980
+
981
+ ### Sheet
982
+
983
+ `@human-synthesis/norns-ui/components/Sheet.n`
984
+
985
+ ```ts
986
+ export type SheetSide = 'top' | 'right' | 'bottom' | 'left';
987
+
988
+ export type SheetProps = {
989
+ open?: boolean;
990
+ /** `'left'` was previously the default for the now-removed `<Drawer>`. */
991
+ side?: SheetSide;
992
+ title?: string;
993
+ description?: string;
994
+ hideClose?: boolean;
995
+ dismissable?: boolean;
996
+ ariaLabel?: string;
997
+ trigger?: Snippet;
998
+ actions?: Snippet;
999
+ children?: Snippet;
1000
+ triggerClass?: string;
1001
+ overlayClass?: string;
1002
+ class?: string;
1003
+ };
1004
+ ```
1005
+
1006
+ ### ShinyButton
1007
+
1008
+ `@human-synthesis/norns-ui/components/ShinyButton.n`
1009
+
1010
+ ```ts
1011
+ export type ShinyButtonProps = {
1012
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
1013
+ size?: 'sm' | 'md' | 'lg';
1014
+ type?: 'button' | 'submit' | 'reset';
1015
+ disabled?: boolean;
1016
+ onclick?: (event: MouseEvent) => void;
1017
+ icon?: string;
1018
+ children?: Snippet;
1019
+ class?: string;
1020
+ };
1021
+ ```
1022
+
1023
+ ### Skeleton
1024
+
1025
+ `@human-synthesis/norns-ui/components/Skeleton.n`
1026
+
1027
+ ```ts
1028
+ export type SkeletonVariant = 'rect' | 'text' | 'circle';
1029
+
1030
+ export type SkeletonProps = {
1031
+ variant?: SkeletonVariant;
1032
+ width?: number | string;
1033
+ height?: number | string;
1034
+ class?: string;
1035
+ };
1036
+ ```
1037
+
1038
+ ### Stepper
1039
+
1040
+ `@human-synthesis/norns-ui/components/Stepper.n`
1041
+
1042
+ ```ts
1043
+ export type StepperStep = {
1044
+ id?: string;
1045
+ label: string;
1046
+ complete?: boolean;
1047
+ current?: boolean;
1048
+ };
1049
+
1050
+ export type StepperProps = {
1051
+ steps?: StepperStep[];
1052
+ /** Index of the active step. Steps before are auto-marked complete unless overridden. */
1053
+ current?: number;
1054
+ orientation?: 'horizontal' | 'vertical';
1055
+ class?: string;
1056
+ };
1057
+ ```
1058
+
1059
+ ### Surface
1060
+
1061
+ `@human-synthesis/norns-ui/components/Surface.n`
1062
+
1063
+ ```ts
1064
+ export type SurfaceProps = {
1065
+ tone?: 'glass';
1066
+ children?: Snippet;
1067
+ class?: string;
1068
+ };
1069
+ ```
1070
+
1071
+ ### Switch
1072
+
1073
+ `@human-synthesis/norns-ui/components/Switch.n`
1074
+
1075
+ ```ts
1076
+ export type SwitchProps = Omit<HTMLInputAttributes, 'class' | 'type' | 'checked' | 'role'> & {
1077
+ checked?: boolean;
1078
+ error?: boolean;
1079
+ class?: string;
1080
+ };
1081
+ ```
1082
+
1083
+ ### Tabs
1084
+
1085
+ `@human-synthesis/norns-ui/components/Tabs.n`
1086
+
1087
+ ```ts
1088
+ export type TabsItem = {
1089
+ value: string;
1090
+ label: string;
1091
+ panel?: Snippet;
1092
+ disabled?: boolean;
1093
+ };
1094
+
1095
+ export type TabsProps = {
1096
+ value?: string;
1097
+ items?: TabsItem[];
1098
+ /** ARIA label for the tablist. Default 'Tabs'. */
1099
+ label?: string;
1100
+ class?: string;
1101
+ };
1102
+ ```
1103
+
1104
+ ### TagsInput
1105
+
1106
+ `@human-synthesis/norns-ui/components/TagsInput.n`
1107
+
1108
+ ```ts
1109
+ export type TagsInputProps = {
1110
+ value?: string[];
1111
+ max?: number;
1112
+ placeholder?: string;
1113
+ commitOnBlur?: boolean;
1114
+ separators?: string[];
1115
+ disabled?: boolean;
1116
+ name?: string;
1117
+ id?: string;
1118
+ error?: boolean;
1119
+ class?: string;
1120
+ };
1121
+ ```
1122
+
1123
+ ### Textarea
1124
+
1125
+ `@human-synthesis/norns-ui/components/Textarea.n`
1126
+
1127
+ ```ts
1128
+ export type TextareaProps = Omit<HTMLTextareaAttributes, 'class' | 'value' | 'rows'> & {
1129
+ value?: string;
1130
+ rows?: number;
1131
+ error?: boolean;
1132
+ class?: string;
1133
+ };
1134
+ ```
1135
+
1136
+ ### ThemeToggler
1137
+
1138
+ `@human-synthesis/norns-ui/components/ThemeToggler.n`
1139
+
1140
+ ```ts
1141
+ export type ThemeTogglerProps = {
1142
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
1143
+ size?: 'sm' | 'md' | 'lg';
1144
+ showLabel?: boolean;
1145
+ labelLight?: string;
1146
+ labelDark?: string;
1147
+ lightIcon?: string;
1148
+ darkIcon?: string;
1149
+ /** localStorage key. Default `'norns-theme'`. */
1150
+ storageKey?: string;
1151
+ class?: string;
1152
+ };
1153
+ ```
1154
+
1155
+ ### Timeline
1156
+
1157
+ `@human-synthesis/norns-ui/components/Timeline.n`
1158
+
1159
+ ```ts
1160
+ export type TimelineItem = {
1161
+ time?: string;
1162
+ title?: string;
1163
+ description?: string;
1164
+ icon?: string;
1165
+ variant?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
1166
+ };
1167
+
1168
+ export type TimelineProps = {
1169
+ items?: TimelineItem[];
1170
+ class?: string;
1171
+ };
1172
+ ```
1173
+
1174
+ ### TimePicker
1175
+
1176
+ `@human-synthesis/norns-ui/components/TimePicker.n`
1177
+
1178
+ ```ts
1179
+ export type TimePickerProps = {
1180
+ /** Time string `HH:MM` (24h regardless of display `hourCycle`). Bindable. */
1181
+ value?: string;
1182
+ open?: boolean;
1183
+ /** Minute increment in the picker (5, 10, 15, 30). Default 5. */
1184
+ minuteStep?: number;
1185
+ hourCycle?: 12 | 24;
1186
+ placeholder?: string;
1187
+ disabled?: boolean;
1188
+ label?: string;
1189
+ name?: string;
1190
+ id?: string;
1191
+ error?: boolean;
1192
+ class?: string;
1193
+ };
1194
+ ```
1195
+
1196
+ ### ToastProvider
1197
+
1198
+ `@human-synthesis/norns-ui/components/ToastProvider.n`
1199
+
1200
+ ```ts
1201
+ export type ToastVariant = 'info' | 'success' | 'warning' | 'error';
1202
+
1203
+ export type ToastProviderProps = {
1204
+ class?: string;
1205
+ };
1206
+
1207
+ export type ToastOpts = {
1208
+ variant?: ToastVariant;
1209
+ duration?: number;
1210
+ };
1211
+ ```
1212
+
1213
+ ### ToggleButton
1214
+
1215
+ `@human-synthesis/norns-ui/components/ToggleButton.n`
1216
+
1217
+ ```ts
1218
+ export type ToggleButtonProps = {
1219
+ pressed?: boolean;
1220
+ disabled?: boolean;
1221
+ icon?: string;
1222
+ /** Any `.btn-*` variant. The toggle visual state is controlled by `data-pressed`. */
1223
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
1224
+ size?: 'sm' | 'md' | 'lg';
1225
+ onpressedchange?: (pressed: boolean) => void;
1226
+ children?: Snippet;
1227
+ class?: string;
1228
+ };
1229
+ ```
1230
+
1231
+ ### ToggleButtonGroup
1232
+
1233
+ `@human-synthesis/norns-ui/components/ToggleButtonGroup.n`
1234
+
1235
+ ```ts
1236
+ export type ToggleGroupItem = {
1237
+ value: string;
1238
+ label?: string;
1239
+ icon?: string;
1240
+ iconOnly?: boolean;
1241
+ disabled?: boolean;
1242
+ };
1243
+
1244
+ export type ToggleButtonGroupProps = {
1245
+ items?: ToggleGroupItem[];
1246
+ value?: string | string[] | undefined;
1247
+ multiple?: boolean;
1248
+ disabled?: boolean;
1249
+ label?: string;
1250
+ class?: string;
1251
+ };
1252
+ ```
1253
+
1254
+ ### Toolbar
1255
+
1256
+ `@human-synthesis/norns-ui/components/Toolbar.n`
1257
+
1258
+ ```ts
1259
+ export type ToolbarProps = {
1260
+ orientation?: 'horizontal' | 'vertical';
1261
+ /** Accessible label, used as aria-label. */
1262
+ label?: string;
1263
+ children?: Snippet;
1264
+ class?: string;
1265
+ };
1266
+ ```
1267
+
1268
+ ### Tooltip
1269
+
1270
+ `@human-synthesis/norns-ui/components/Tooltip.n`
1271
+
1272
+ ```ts
1273
+ export type TooltipProps = {
1274
+ content?: string;
1275
+ side?: 'top' | 'right' | 'bottom' | 'left';
1276
+ align?: 'start' | 'center' | 'end';
1277
+ sideOffset?: number;
1278
+ delay?: number;
1279
+ /** Wider, surface-elevated styling for tooltips with markup/multiline content. */
1280
+ rich?: boolean;
1281
+ trigger?: Snippet;
1282
+ children?: Snippet;
1283
+ triggerClass?: string;
1284
+ class?: string;
1285
+ };
1286
+ ```
1287
+
1288
+ ### Tree
1289
+
1290
+ `@human-synthesis/norns-ui/components/Tree.n`
1291
+
1292
+ ```ts
1293
+ export type TreeNode = {
1294
+ id: string | number;
1295
+ label: string;
1296
+ icon?: string;
1297
+ children?: TreeNode[];
1298
+ };
1299
+
1300
+ export type TreeProps = {
1301
+ items?: TreeNode[];
1302
+ /** Array of node ids that are expanded. Bindable. */
1303
+ expanded?: (string | number)[];
1304
+ /** Currently-selected node id. Bindable. */
1305
+ selected?: string | number;
1306
+ class?: string;
1307
+ };
1308
+ ```
1309
+
1310
+ ### Uploader
1311
+
1312
+ `@human-synthesis/norns-ui/components/Uploader.n`
1313
+
1314
+ ```ts
1315
+ export type UploaderProps = {
1316
+ files?: File[];
1317
+ multiple?: boolean;
1318
+ accept?: string;
1319
+ disabled?: boolean;
1320
+ placeholder?: string;
1321
+ helper?: string;
1322
+ onchange?: (files: File[]) => void;
1323
+ class?: string;
1324
+ };
1325
+ ```
1326
+
1327
+ ### Video
1328
+
1329
+ `@human-synthesis/norns-ui/components/Video.n`
1330
+
1331
+ ```ts
1332
+ export type VideoSource = { src: string; type?: string };
1333
+
1334
+ export type VideoProps = {
1335
+ src?: string;
1336
+ sources?: VideoSource[];
1337
+ controls?: boolean;
1338
+ autoplay?: boolean;
1339
+ loop?: boolean;
1340
+ muted?: boolean;
1341
+ playsinline?: boolean;
1342
+ preload?: 'none' | 'metadata' | 'auto';
1343
+ poster?: string;
1344
+ fallback?: string;
1345
+ class?: string;
1346
+ };
1347
+ ```
1348
+
1349
+ ### Window
1350
+
1351
+ `@human-synthesis/norns-ui/components/Window.n`
1352
+
1353
+ ```ts
1354
+ export type WindowProps = {
1355
+ /** Header title (truncated). */
1356
+ title?: string;
1357
+ /** Remove the whole header bar; keeps the rounded shell. */
1358
+ hideHeader?: boolean;
1359
+ /** When set, renders a `lucide:x` close button (marked `.nodrag`). */
1360
+ onClose?: () => void;
1361
+ /** Fires on double-click over the header bar (e.g. maximise). */
1362
+ onHeaderDoubleClick?: (event: MouseEvent) => void;
1363
+ /** Extra header controls, rendered before the close button. */
1364
+ actions?: Snippet;
1365
+ children?: Snippet;
1366
+ /** Classes for the outer shell — pass dimensions here (`w-[520px] h-[380px]`). */
1367
+ class?: string;
1368
+ /** Classes for the body section. */
1369
+ bodyClass?: string;
1370
+ };
1371
+ ```
1372
+
1373
+ ## Motion (opt-in)
1374
+
1375
+ [AnimatedNumber](#animatednumber) · [GradientBackground](#gradientbackground) · [LiquidButton](#liquidbutton) · [Reveal](#reveal) · [Sparkles](#sparkles)
1376
+
1377
+ ### AnimatedNumber
1378
+
1379
+ `import { AnimatedNumber } from '@human-synthesis/norns-ui/motion'`
1380
+
1381
+ ```ts
1382
+ export type AnimatedNumberProps = {
1383
+ value: number;
1384
+ from?: number;
1385
+ /** Duration in seconds. */
1386
+ duration?: number;
1387
+ decimals?: number;
1388
+ /** Custom number-to-string formatter. */
1389
+ format?: (n: number) => string;
1390
+ class?: string;
1391
+ };
1392
+ ```
1393
+
1394
+ ### GradientBackground
1395
+
1396
+ `import { GradientBackground } from '@human-synthesis/norns-ui/motion'`
1397
+
1398
+ ```ts
1399
+ export type GradientBackgroundProps = {
1400
+ colors?: string[];
1401
+ /** Loop duration in seconds. Default 18. */
1402
+ duration?: number;
1403
+ /** Gradient angle. Default 130. */
1404
+ angle?: number;
1405
+ children?: Snippet;
1406
+ class?: string;
1407
+ };
1408
+ ```
1409
+
1410
+ ### LiquidButton
1411
+
1412
+ `import { LiquidButton } from '@human-synthesis/norns-ui/motion'`
1413
+
1414
+ ```ts
1415
+ export type LiquidButtonProps = {
1416
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
1417
+ size?: 'sm' | 'md' | 'lg';
1418
+ type?: 'button' | 'submit' | 'reset';
1419
+ disabled?: boolean;
1420
+ onclick?: (event: MouseEvent) => void;
1421
+ icon?: string;
1422
+ children?: Snippet;
1423
+ class?: string;
1424
+ };
1425
+ ```
1426
+
1427
+ ### Reveal
1428
+
1429
+ `import { Reveal } from '@human-synthesis/norns-ui/motion'`
1430
+
1431
+ ```ts
1432
+ export type RevealProps = {
1433
+ direction?: 'up' | 'down' | 'left' | 'right';
1434
+ /** Travel distance in pixels. Default 12. */
1435
+ distance?: number;
1436
+ /** Animation duration in seconds. Default 0.5. */
1437
+ duration?: number;
1438
+ /** Delay before animating, seconds. Default 0. */
1439
+ delay?: number;
1440
+ /** Re-trigger on every entry (false) or just the first time (true). Default true. */
1441
+ once?: boolean;
1442
+ /** IntersectionObserver threshold (0–1). Default 0.15. */
1443
+ threshold?: number;
1444
+ children?: Snippet;
1445
+ class?: string;
1446
+ };
1447
+ ```
1448
+
1449
+ ### Sparkles
1450
+
1451
+ `import { Sparkles } from '@human-synthesis/norns-ui/motion'`
1452
+
1453
+ ```ts
1454
+ export type SparklesProps = {
1455
+ density?: number;
1456
+ colors?: string[];
1457
+ minSize?: number;
1458
+ maxSize?: number;
1459
+ children?: Snippet;
1460
+ class?: string;
1461
+ };
1462
+ ```