@human-synthesis/norns-ui 0.0.12 → 0.0.13

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