@mond-design-system/react 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,942 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties, HTMLAttributes, JSX, ReactElement, ReactNode, ButtonHTMLAttributes, ElementType, Ref, AnchorHTMLAttributes, InputHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes, LiHTMLAttributes, MouseEventHandler, RefObject, KeyboardEvent } from 'react';
3
+
4
+ /**
5
+ * Join class names, dropping anything falsy.
6
+ *
7
+ * Every component takes a `className` from the call site and owns one of its
8
+ * own from a CSS Module, so the merge happens in one place rather than in
9
+ * forty slightly different inline expressions.
10
+ */
11
+ declare function cx(...parts: Array<string | false | null | undefined>): string;
12
+ /**
13
+ * A style object that may also carry CSS custom properties.
14
+ *
15
+ * `React.CSSProperties` has no index signature, so `{ '--mds-x': 1 }` is a
16
+ * type error against it. Values that genuinely vary per render (a slider's
17
+ * percent, an avatar's pixel size) belong in a custom property the stylesheet
18
+ * reads — this types that without an assertion.
19
+ */
20
+ type CSSVars = CSSProperties & Record<`--${string}`, string | number>;
21
+
22
+ type TextVariant = "body" | "label" | "note" | "meta" | "eyebrow";
23
+ type TextTone = "primary" | "secondary" | "muted" | "inverse" | "accent" | "on-media" | "danger" | "warning" | "success";
24
+ interface TextProps extends HTMLAttributes<HTMLElement> {
25
+ /** Type role from the core scale. Default "body". */
26
+ variant?: TextVariant;
27
+ /** Element override when the default doesn't fit the markup. */
28
+ as?: keyof JSX.IntrinsicElements;
29
+ /** Semantic color. Variants carry sensible defaults. */
30
+ tone?: TextTone;
31
+ align?: "left" | "center" | "right";
32
+ /** Single-line ellipsis. */
33
+ truncate?: boolean;
34
+ }
35
+ /**
36
+ * Body-copy primitive. One class per type role — screens never hand-roll styled tags.
37
+ *
38
+ * ```tsx
39
+ * <Text>Body copy.</Text>
40
+ * <Text variant="label" as="span">Field label</Text>
41
+ * <Text variant="meta" tone="muted">Updated 2h ago</Text>
42
+ * <Text truncate>Very long single line…</Text>
43
+ * ```
44
+ */
45
+ declare function Text({ variant, as, tone, align, truncate, className, ...rest }: TextProps): ReactElement;
46
+
47
+ type HeadingVariant = "display" | "title" | "subtitle";
48
+ type HeadingLevel = 1 | 2 | 3 | 4;
49
+ type HeadingTone = "primary" | "secondary" | "inverse" | "accent" | "on-media";
50
+ interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
51
+ /** Document outline level → h1–h4, with a matching default type role. Default 2. */
52
+ level?: HeadingLevel;
53
+ /** Type role override — outline level and visual size are separate concerns. */
54
+ variant?: HeadingVariant;
55
+ tone?: HeadingTone;
56
+ }
57
+ /**
58
+ * Brand headings on the core roles. `level` = semantics, `variant` = size.
59
+ *
60
+ * ```tsx
61
+ * <Heading level={1}>Sessions</Heading>
62
+ * <Heading level={2} variant="subtitle" tone="secondary">This week</Heading>
63
+ * ```
64
+ */
65
+ declare function Heading({ level, variant, tone, className, ...rest }: HeadingProps): ReactElement;
66
+
67
+ interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
68
+ /** Diameter in px. Default 20. */
69
+ size?: number;
70
+ /** Accessible label. Default "Loading". */
71
+ label?: string;
72
+ }
73
+ /**
74
+ * Indeterminate loading ring. Arc takes currentColor, so it matches whatever
75
+ * text it sits beside. Keyframe is local: CSS Modules localizes
76
+ * animation-name, a global one resolves to nothing. base.css stops it under
77
+ * prefers-reduced-motion.
78
+ *
79
+ * ```tsx
80
+ * <Spinner label="Loading sessions" />
81
+ * ```
82
+ */
83
+ declare function Spinner({ size, label, className, style, ...rest }: SpinnerProps): ReactElement;
84
+
85
+ interface IconRenderProps {
86
+ /** Pixel size of the requested step. */
87
+ size: number;
88
+ }
89
+ /** Brand-owned glyph resolver: name → rendered glyph (svg, font ligature, …). */
90
+ type IconRender = (name: string, props: IconRenderProps) => ReactNode;
91
+ interface IconProviderProps {
92
+ render: IconRender;
93
+ children: ReactNode;
94
+ }
95
+ /**
96
+ * Registry seam. The system never bundles an icon set — the app registers its
97
+ * own (lucide, Material Symbols, …) once, at the root. Iconography is brand,
98
+ * like color.
99
+ *
100
+ * ```tsx
101
+ * // App root: map names to your icon set once.
102
+ * <IconProvider render={(name, { size }) => <MyGlyph name={name} width={size} />}>
103
+ * <App />
104
+ * </IconProvider>
105
+ *
106
+ * // Anywhere below:
107
+ * <Icon name="check" size="sm" />
108
+ * ```
109
+ */
110
+ declare function IconProvider({ render, children }: IconProviderProps): ReactElement;
111
+ type IconSize = "sm" | "md" | "lg";
112
+ interface IconProps extends HTMLAttributes<HTMLSpanElement> {
113
+ /** Glyph name in the app's registered set. */
114
+ name: string;
115
+ /** Default "md". */
116
+ size?: IconSize;
117
+ /** Accessible name. Omitted = decorative (aria-hidden). */
118
+ label?: string;
119
+ }
120
+ /** A glyph from the app-registered set, sized on the core scale. */
121
+ declare function Icon({ name, size, label, className, ...rest }: IconProps): ReactElement;
122
+
123
+ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
124
+ type ButtonSize = "sm" | "md" | "lg";
125
+ interface ButtonBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "type"> {
126
+ variant?: ButtonVariant;
127
+ size?: ButtonSize;
128
+ /** Glyph slots — pass an <Icon> or any node. Agnostic of icon set. */
129
+ iconLeft?: ReactNode;
130
+ iconRight?: ReactNode;
131
+ /** Spinner replaces the left slot; disables and announces busy. */
132
+ loading?: boolean;
133
+ fullWidth?: boolean;
134
+ type?: "button" | "submit" | "reset";
135
+ /** Renders an <a> — for navigation styled as a button. */
136
+ href?: string;
137
+ /** Element override, e.g. a router's Link. */
138
+ as?: ElementType;
139
+ ref?: Ref<HTMLButtonElement>;
140
+ }
141
+ type IconOnlyEnforcement = {
142
+ iconOnly: true;
143
+ "aria-label": string;
144
+ } | {
145
+ iconOnly?: false | undefined;
146
+ };
147
+ type ButtonProps = ButtonBaseProps & IconOnlyEnforcement;
148
+ /**
149
+ * The tappable action. Semantics: button by default, link when `href`/`as` says so.
150
+ *
151
+ * ```tsx
152
+ * <Button onClick={save}>Save</Button>
153
+ * <Button variant="secondary" size="sm">Cancel</Button>
154
+ * <Button variant="danger" loading={deleting} onClick={remove}>Delete</Button>
155
+ * <Button iconOnly aria-label="Close" variant="ghost"><Icon name="close" /></Button>
156
+ * <Button href="/settings">Settings</Button>
157
+ * ```
158
+ */
159
+ declare function Button({ variant, size, iconLeft, iconRight, loading, disabled, fullWidth, iconOnly, type, href, as, className, children, ...rest }: ButtonProps): ReactElement;
160
+
161
+ type LinkVariant = "inline" | "standalone";
162
+ interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
163
+ /** inline = underlined, lives in running text. standalone = nav/action link. */
164
+ variant?: LinkVariant;
165
+ /** Opens in a new tab with rel protection. */
166
+ external?: boolean;
167
+ /** Element override, e.g. a router's Link. */
168
+ as?: ElementType;
169
+ ref?: Ref<HTMLAnchorElement>;
170
+ }
171
+ /**
172
+ * Text link. Inline links keep their underline: color alone fails WCAG 1.4.1
173
+ * for links inside prose.
174
+ *
175
+ * ```tsx
176
+ * <Text>Read the <Link href="/terms">terms</Link>.</Text>
177
+ * <Link variant="standalone" href="https://example.com" external>Docs</Link>
178
+ * <Link as={NextLink} href="/about">About</Link>
179
+ * ```
180
+ */
181
+ declare function Link({ variant, external, as: Element, className, ...rest }: LinkProps): ReactElement;
182
+
183
+ type AvatarSize = "sm" | "md" | "lg";
184
+ interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
185
+ /** Person's display name — accessible name and initials source. */
186
+ name: string;
187
+ /** Image URL; initials render when absent or empty. */
188
+ src?: string;
189
+ size?: AvatarSize;
190
+ }
191
+ /**
192
+ * Person marker. Image when available, initials otherwise — never an empty circle.
193
+ *
194
+ * ```tsx
195
+ * <Avatar name="Ada Lovelace" src="/ada.jpg" />
196
+ * <Avatar name="Grace Hopper" size="lg" />
197
+ * ```
198
+ */
199
+ declare function Avatar({ name, src, size, className, ...rest }: AvatarProps): ReactElement;
200
+
201
+ interface AvatarGroupProps extends HTMLAttributes<HTMLDivElement> {
202
+ /** Avatar children. */
203
+ children: ReactNode;
204
+ /** Visible cap; the rest collapse into "+N". Default 4. */
205
+ max?: number;
206
+ /** Sizes the overflow chip to match the avatars. Default "md". */
207
+ size?: AvatarSize;
208
+ }
209
+ /**
210
+ * Overlapping stack of avatars with a "+N" chip past `max`.
211
+ *
212
+ * ```tsx
213
+ * <AvatarGroup max={3}>
214
+ * <Avatar name="Ada Lovelace" />
215
+ * <Avatar name="Grace Hopper" />
216
+ * <Avatar name="Margaret Hamilton" />
217
+ * <Avatar name="Katherine Johnson" />
218
+ * </AvatarGroup>
219
+ * ```
220
+ */
221
+ declare function AvatarGroup({ children, max, size, className, ...rest }: AvatarGroupProps): ReactElement;
222
+
223
+ type BadgeTone = "neutral" | "accent" | "danger" | "warning" | "success";
224
+ interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
225
+ tone?: BadgeTone;
226
+ }
227
+ /**
228
+ * Small status marker — counts, "new", state words. Soft fill, strong text.
229
+ *
230
+ * ```tsx
231
+ * <Badge tone="success">Paid</Badge>
232
+ * <Badge tone="danger">3</Badge>
233
+ * ```
234
+ */
235
+ declare function Badge({ tone, className, ...rest }: BadgeProps): ReactElement;
236
+
237
+ type TagTone = "neutral" | "accent";
238
+ interface TagProps extends HTMLAttributes<HTMLSpanElement> {
239
+ children: ReactNode;
240
+ tone?: TagTone;
241
+ /** Renders a labelled ✕ button. Label reads "Remove <content>" when content is text. */
242
+ onRemove?: () => void;
243
+ }
244
+ /**
245
+ * Content label chip — categories, filters, topics.
246
+ *
247
+ * ```tsx
248
+ * <Tag>Design</Tag>
249
+ * <Tag tone="accent" onRemove={() => remove(id)}>Beginner</Tag>
250
+ * ```
251
+ */
252
+ declare function Tag({ children, tone, onRemove, className, ...rest }: TagProps): ReactElement;
253
+
254
+ interface DividerProps extends HTMLAttributes<HTMLDivElement> {
255
+ orientation?: "horizontal" | "vertical";
256
+ }
257
+ /**
258
+ * Visual separator. Semantic separator role via <hr>-equivalent div.
259
+ *
260
+ * ```tsx
261
+ * <Divider />
262
+ * <Divider orientation="vertical" />
263
+ * ```
264
+ */
265
+ declare function Divider({ orientation, className, ...rest }: DividerProps): ReactElement;
266
+
267
+ type SkeletonVariant = "text" | "rect" | "circle";
268
+ interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
269
+ variant?: SkeletonVariant;
270
+ /** Any CSS length. Text defaults to full width, one line tall. */
271
+ width?: string;
272
+ height?: string;
273
+ }
274
+ /**
275
+ * Loading placeholder. aria-hidden — the loading announcement belongs to the
276
+ * region (aria-busy), not to each shimmering box.
277
+ *
278
+ * ```tsx
279
+ * <div aria-busy={loading}>
280
+ * {loading ? (
281
+ * <Stack gap="tight">
282
+ * <Skeleton width="40%" />
283
+ * <Skeleton />
284
+ * <Skeleton variant="rect" height="8rem" />
285
+ * </Stack>
286
+ * ) : (
287
+ * content
288
+ * )}
289
+ * </div>
290
+ * ```
291
+ */
292
+ declare function Skeleton({ variant, width, height, className, style, ...rest }: SkeletonProps): ReactElement;
293
+
294
+ interface ProgressBarProps extends HTMLAttributes<HTMLDivElement> {
295
+ /** 0–100; clamped. Ignored when indeterminate. */
296
+ value?: number;
297
+ /** Accessible name — required. A bare bar announces nothing useful. */
298
+ label: string;
299
+ indeterminate?: boolean;
300
+ }
301
+ /**
302
+ * Determinate or indeterminate progress track.
303
+ *
304
+ * ```tsx
305
+ * <ProgressBar label="Upload" value={64} />
306
+ * <ProgressBar label="Syncing" indeterminate />
307
+ * ```
308
+ */
309
+ declare function ProgressBar({ value, label, indeterminate, className, style, ...rest }: ProgressBarProps): ReactElement;
310
+
311
+ interface FieldContextValue {
312
+ /** Control id — the label points here. */
313
+ id: string;
314
+ /** Space-joined ids of hint/error, for aria-describedby. */
315
+ describedBy: string | undefined;
316
+ invalid: boolean;
317
+ }
318
+ /** Controls call this to pick up Field wiring. Null outside a Field. */
319
+ declare function useFieldContext(): FieldContextValue | null;
320
+ interface FieldProps {
321
+ label: string;
322
+ children: ReactNode;
323
+ /** Guidance under the control. */
324
+ hint?: string;
325
+ /** Validation message; replaces the hint and flags the control invalid. */
326
+ error?: string;
327
+ required?: boolean;
328
+ className?: string;
329
+ }
330
+ /**
331
+ * Label + control + hint/error, wired for assistive tech. The control inside
332
+ * (Input, Textarea, Select, …) picks up id, aria-describedby and aria-invalid
333
+ * from context — no manual plumbing at call sites.
334
+ *
335
+ * ```tsx
336
+ * <Field label="Email" hint="Work email preferred" error={errors.email} required>
337
+ * <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
338
+ * </Field>
339
+ * ```
340
+ */
341
+ declare function Field({ label, children, hint, error, required, className }: FieldProps): ReactElement;
342
+
343
+ type InputSize = "sm" | "md" | "lg";
344
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
345
+ size?: InputSize;
346
+ ref?: Ref<HTMLInputElement>;
347
+ }
348
+ /**
349
+ * Single-line text input. Inside a Field it inherits id/description/invalid.
350
+ *
351
+ * ```tsx
352
+ * <Field label="Name">
353
+ * <Input value={name} onChange={(e) => setName(e.target.value)} />
354
+ * </Field>
355
+ *
356
+ * <Input aria-label="Amount" type="number" size="sm" />
357
+ * ```
358
+ */
359
+ declare function Input({ size, className, ...rest }: InputProps): ReactElement;
360
+
361
+ type PasswordInputProps = Omit<InputProps, "type">;
362
+ /**
363
+ * Password field with a reveal toggle. Composes Input, so inside a Field it
364
+ * inherits id/description/invalid the same way.
365
+ *
366
+ * ```tsx
367
+ * <Field label="Password" hint="At least 12 characters">
368
+ * <PasswordInput autoComplete="new-password" />
369
+ * </Field>
370
+ * ```
371
+ */
372
+ declare function PasswordInput({ className, ...rest }: PasswordInputProps): ReactElement;
373
+
374
+ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
375
+ /** Character counter under the control; pairs with native maxLength. */
376
+ showCount?: boolean;
377
+ ref?: Ref<HTMLTextAreaElement>;
378
+ }
379
+ /**
380
+ * Multi-line text input. Inside a Field it inherits id/description/invalid.
381
+ *
382
+ * ```tsx
383
+ * <Field label="Bio">
384
+ * <Textarea maxLength={160} showCount />
385
+ * </Field>
386
+ * ```
387
+ */
388
+ declare function Textarea({ rows, showCount, className, ...rest }: TextareaProps): ReactElement;
389
+
390
+ type SelectSize = "sm" | "md" | "lg";
391
+ interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"> {
392
+ size?: SelectSize;
393
+ ref?: Ref<HTMLSelectElement>;
394
+ }
395
+ /**
396
+ * Native select, styled. Native = free keyboard, mobile pickers, screen
397
+ * reader behavior; a custom popover earns its complexity only when options
398
+ * need rich content.
399
+ *
400
+ * ```tsx
401
+ * <Field label="Language">
402
+ * <Select value={lang} onChange={(e) => setLang(e.target.value)}>
403
+ * <option value="de">Deutsch</option>
404
+ * <option value="en">English</option>
405
+ * </Select>
406
+ * </Field>
407
+ * ```
408
+ */
409
+ declare function Select({ size, className, children, ...rest }: SelectProps): ReactElement;
410
+
411
+ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
412
+ /** Inline label — part of the click target. */
413
+ label: string;
414
+ ref?: Ref<HTMLInputElement>;
415
+ }
416
+ /**
417
+ * Native checkbox, styled box. Label is the touch target too.
418
+ *
419
+ * ```tsx
420
+ * <Checkbox label="Remember me" defaultChecked />
421
+ * <Checkbox
422
+ * label="Accept terms"
423
+ * checked={accepted}
424
+ * onChange={(e) => setAccepted(e.target.checked)}
425
+ * />
426
+ * ```
427
+ */
428
+ declare function Checkbox({ label, className, ...rest }: CheckboxProps): ReactElement;
429
+
430
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
431
+ /** Inline label — part of the click target. */
432
+ label: string;
433
+ ref?: Ref<HTMLInputElement>;
434
+ }
435
+ /**
436
+ * Native radio, styled dot. Group by `name`; wrap the group in
437
+ * `<fieldset>`/`<legend>` for a group label.
438
+ *
439
+ * ```tsx
440
+ * <fieldset>
441
+ * <legend>Plan</legend>
442
+ * <Radio name="plan" label="Free" checked={plan === "free"} onChange={() => setPlan("free")} />
443
+ * <Radio name="plan" label="Pro" checked={plan === "pro"} onChange={() => setPlan("pro")} />
444
+ * </fieldset>
445
+ * ```
446
+ */
447
+ declare function Radio({ label, className, ...rest }: RadioProps): ReactElement;
448
+
449
+ interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
450
+ /** Inline label — part of the click target. */
451
+ label: string;
452
+ ref?: Ref<HTMLInputElement>;
453
+ }
454
+ /**
455
+ * On/off toggle. Checkbox under the hood with role="switch" — native
456
+ * semantics and forms integration, switch announcement.
457
+ *
458
+ * ```tsx
459
+ * <Switch
460
+ * label="Notifications"
461
+ * checked={enabled}
462
+ * onChange={(e) => setEnabled(e.target.checked)}
463
+ * />
464
+ * ```
465
+ */
466
+ declare function Switch({ label, className, ...rest }: SwitchProps): ReactElement;
467
+
468
+ interface SegmentOption<T extends string = string> {
469
+ value: T;
470
+ label: string;
471
+ }
472
+ interface SegmentedControlProps<T extends string = string> {
473
+ /** Accessible group name. */
474
+ label: string;
475
+ options: readonly SegmentOption<T>[];
476
+ value: T;
477
+ onChange: (value: T) => void;
478
+ className?: string;
479
+ }
480
+ /**
481
+ * Exclusive choice among few visible options. Native radios under the hood —
482
+ * free keyboard model and semantics.
483
+ *
484
+ * ```tsx
485
+ * <SegmentedControl
486
+ * label="View"
487
+ * options={[
488
+ * { value: "list", label: "List" },
489
+ * { value: "grid", label: "Grid" },
490
+ * ]}
491
+ * value={view}
492
+ * onChange={setView}
493
+ * />
494
+ * ```
495
+ */
496
+ declare function SegmentedControl<T extends string = string>({ label, options, value, onChange, className, }: SegmentedControlProps<T>): ReactElement;
497
+
498
+ interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size" | "value" | "onChange"> {
499
+ /** Accessible name. */
500
+ label: string;
501
+ value: string;
502
+ /** Receives the new text — "" when cleared. */
503
+ onChange: (value: string) => void;
504
+ ref?: Ref<HTMLInputElement>;
505
+ }
506
+ /**
507
+ * Controlled search input with a clear affordance once there is text.
508
+ *
509
+ * ```tsx
510
+ * <SearchField label="Search sessions" value={query} onChange={setQuery} />
511
+ * ```
512
+ */
513
+ declare function SearchField({ label, value, onChange, className, ...rest }: SearchFieldProps): ReactElement;
514
+
515
+ interface ListGroupProps extends HTMLAttributes<HTMLUListElement> {
516
+ children: ReactNode;
517
+ }
518
+ /**
519
+ * Card-styled list container for ListItems.
520
+ *
521
+ * ```tsx
522
+ * <ListGroup>
523
+ * <ListItem
524
+ * title="Profile"
525
+ * description="Name, avatar"
526
+ * leading={<Icon name="user" />}
527
+ * href="/profile"
528
+ * />
529
+ * <ListItem title="Log out" onClick={logout} />
530
+ * </ListGroup>
531
+ * ```
532
+ */
533
+ declare function ListGroup({ className, ...rest }: ListGroupProps): ReactElement;
534
+ interface ListItemProps extends Omit<LiHTMLAttributes<HTMLLIElement>, "title" | "onClick"> {
535
+ title: string;
536
+ description?: string;
537
+ /** Slot before the text — Avatar, Icon, … */
538
+ leading?: ReactNode;
539
+ /** Slot after the text — Badge, chevron, … */
540
+ trailing?: ReactNode;
541
+ /** Makes the whole row a button. */
542
+ onClick?: () => void;
543
+ /** Makes the whole row a link. Wins over onClick. */
544
+ href?: string;
545
+ }
546
+ /** One row. Static by default; interactive as one whole-row button/link. */
547
+ declare function ListItem({ title, description, leading, trailing, onClick, href, className, ...rest }: ListItemProps): ReactElement;
548
+
549
+ interface EmptyStateProps {
550
+ title: string;
551
+ description?: string;
552
+ /** Illustration or Icon slot. */
553
+ icon?: ReactNode;
554
+ /** Usually a Button. */
555
+ action?: ReactNode;
556
+ className?: string;
557
+ }
558
+ /**
559
+ * Nothing-here view: explains the empty screen and offers the next step.
560
+ *
561
+ * ```tsx
562
+ * <EmptyState
563
+ * icon={<Icon name="inbox" size="lg" />}
564
+ * title="No sessions yet"
565
+ * description="Create your first session to get started."
566
+ * action={<Button onClick={create}>New session</Button>}
567
+ * />
568
+ * ```
569
+ */
570
+ declare function EmptyState({ title, description, icon, action, className }: EmptyStateProps): ReactElement;
571
+
572
+ interface TabsProps {
573
+ value: string;
574
+ onChange: (value: string) => void;
575
+ children: ReactNode;
576
+ }
577
+ /**
578
+ * Controlled tabs root. Compose with TabList, Tab, TabPanel.
579
+ *
580
+ * ```tsx
581
+ * <Tabs value={tab} onChange={setTab}>
582
+ * <TabList label="Session details">
583
+ * <Tab value="info">Info</Tab>
584
+ * <Tab value="notes">Notes</Tab>
585
+ * </TabList>
586
+ * <TabPanel value="info">…</TabPanel>
587
+ * <TabPanel value="notes">…</TabPanel>
588
+ * </Tabs>
589
+ * ```
590
+ */
591
+ declare function Tabs({ value, onChange, children }: TabsProps): ReactElement;
592
+ interface TabListProps extends HTMLAttributes<HTMLDivElement> {
593
+ /** Accessible name for the tab set. */
594
+ label: string;
595
+ children: ReactNode;
596
+ }
597
+ declare function TabList({ label, className, children, ...rest }: TabListProps): ReactElement;
598
+ interface TabProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "value" | "type"> {
599
+ value: string;
600
+ children: ReactNode;
601
+ }
602
+ declare function Tab({ value, className, children, ...rest }: TabProps): ReactElement;
603
+ interface TabPanelProps extends HTMLAttributes<HTMLDivElement> {
604
+ value: string;
605
+ children: ReactNode;
606
+ }
607
+ declare function TabPanel({ value, className, children, ...rest }: TabPanelProps): ReactElement;
608
+
609
+ type ToastTone = "neutral" | "success" | "danger";
610
+ interface ToastOptions {
611
+ title: string;
612
+ description?: string | undefined;
613
+ tone?: ToastTone | undefined;
614
+ /** ms until auto-dismiss. 0 = stays until dismissed. Default 5000. */
615
+ duration?: number | undefined;
616
+ }
617
+ interface ToastContextValue {
618
+ toast: (options: ToastOptions) => number;
619
+ dismiss: (id: number) => void;
620
+ }
621
+ declare function useToast(): ToastContextValue;
622
+ interface ToastProviderProps {
623
+ children: ReactNode;
624
+ }
625
+ /**
626
+ * Mount once near the root. Renders the notification region itself.
627
+ *
628
+ * ```tsx
629
+ * // App root
630
+ * <ToastProvider>
631
+ * <App />
632
+ * </ToastProvider>
633
+ *
634
+ * // Anywhere below
635
+ * const { toast } = useToast();
636
+ * toast({ title: "Saved", tone: "success" });
637
+ * ```
638
+ */
639
+ declare function ToastProvider({ children }: ToastProviderProps): ReactElement;
640
+
641
+ type StackGap = "hairline" | "tight" | "base" | "loose" | "section";
642
+ type StackAlign = "start" | "center" | "end" | "stretch";
643
+ interface StackProps extends HTMLAttributes<HTMLElement> {
644
+ /** Space between children, on the gap scale. Default "base". */
645
+ gap?: StackGap;
646
+ /** Cross-axis alignment. Default "stretch". */
647
+ align?: StackAlign;
648
+ /** Element to render. Default div. */
649
+ as?: ElementType;
650
+ }
651
+ /**
652
+ * Vertical flow. The parent owns spacing between siblings via gap —
653
+ * children carry no outer margins. Token-bounded on purpose: no arbitrary
654
+ * lengths, no style prop.
655
+ *
656
+ * ```tsx
657
+ * <Stack gap="base">
658
+ * <Heading level={2}>Profile</Heading>
659
+ * <Text tone="secondary">Public information</Text>
660
+ * </Stack>
661
+ * ```
662
+ */
663
+ declare function Stack({ gap, align, as, className, ...rest }: StackProps): ReactElement;
664
+
665
+ type InlineGap = "hairline" | "tight" | "base" | "loose";
666
+ type InlineAlign = "start" | "center" | "end" | "baseline";
667
+ type InlineJustify = "start" | "center" | "end" | "between";
668
+ interface InlineProps extends HTMLAttributes<HTMLElement> {
669
+ /** Space between children, on the gap scale. Default "base". */
670
+ gap?: InlineGap;
671
+ /** Cross-axis alignment. Default "center". */
672
+ align?: InlineAlign;
673
+ /** Main-axis distribution. Default "start". */
674
+ justify?: InlineJustify;
675
+ /** Allow wrapping onto new lines. */
676
+ wrap?: boolean;
677
+ /** Element to render. Default div. */
678
+ as?: ElementType;
679
+ }
680
+ /**
681
+ * Horizontal flow — Stack's row twin. Same token-bounded contract.
682
+ *
683
+ * ```tsx
684
+ * <Inline gap="tight" align="center" justify="between">
685
+ * <Text variant="label">Total</Text>
686
+ * <Text>42</Text>
687
+ * </Inline>
688
+ * ```
689
+ */
690
+ declare function Inline({ gap, align, justify, wrap, as, className, ...rest }: InlineProps): ReactElement;
691
+
692
+ type ContainerWidth = "content" | "wide";
693
+ interface ContainerProps extends HTMLAttributes<HTMLElement> {
694
+ /** content = reading column, wide = desktop shell. Default "content". */
695
+ width?: ContainerWidth;
696
+ /** Element to render. Default div. */
697
+ as?: ElementType;
698
+ }
699
+ /**
700
+ * Centered max-width column with page-edge padding.
701
+ *
702
+ * ```tsx
703
+ * <Container>
704
+ * <Stack gap="section">…</Stack>
705
+ * </Container>
706
+ * ```
707
+ */
708
+ declare function Container({ width, as, className, ...rest }: ContainerProps): ReactElement;
709
+
710
+ type CardVariant = "card" | "raised";
711
+ interface CardProps extends Omit<HTMLAttributes<HTMLElement>, "onClick"> {
712
+ children: ReactNode;
713
+ /** card = flat on border, raised = elevated. Default "card". */
714
+ variant?: CardVariant;
715
+ /** Makes the whole card a button. */
716
+ onClick?: () => void;
717
+ /** Makes the whole card a link. Wins over onClick. */
718
+ href?: string;
719
+ }
720
+ /**
721
+ * Surface container. Compose with CardHeader/CardBody/CardFooter — the card
722
+ * owns internal spacing, sections own their content.
723
+ *
724
+ * ```tsx
725
+ * <Card>
726
+ * <CardHeader><Heading level={3}>Next session</Heading></CardHeader>
727
+ * <CardBody><Text>Tomorrow, 19:00</Text></CardBody>
728
+ * <CardFooter><Button size="sm">Join</Button></CardFooter>
729
+ * </Card>
730
+ *
731
+ * <Card href="/items/42" variant="flat">…</Card>
732
+ * ```
733
+ */
734
+ declare function Card({ children, variant, onClick, href, className, ...rest }: CardProps): ReactElement;
735
+ type CardSectionProps = HTMLAttributes<HTMLDivElement>;
736
+ /** Top slot — title row, media, tabs. */
737
+ declare function CardHeader({ className, ...rest }: CardSectionProps): ReactElement;
738
+ /** Main content slot. */
739
+ declare function CardBody({ className, ...rest }: CardSectionProps): ReactElement;
740
+ /** Bottom slot — actions, meta. */
741
+ declare function CardFooter({ className, ...rest }: CardSectionProps): ReactElement;
742
+
743
+ interface ModalProps {
744
+ open: boolean;
745
+ onClose: () => void;
746
+ /** Accessible name of the dialog. */
747
+ label: string;
748
+ /** Scrim click dismisses. Default true. */
749
+ closeOnScrimClick?: boolean;
750
+ children: ReactNode;
751
+ }
752
+ /**
753
+ * Centered dialog. Escape, scrim click (`closeOnScrimClick`), and focus trap
754
+ * are handled. Compose the sections:
755
+ *
756
+ * ```tsx
757
+ * <Modal open={open} onClose={() => setOpen(false)} label="Edit session">
758
+ * <ModalHeader>Edit session</ModalHeader>
759
+ * <ModalBody>…</ModalBody>
760
+ * <ModalFooter><Button onClick={save}>Save</Button></ModalFooter>
761
+ * </Modal>
762
+ * ```
763
+ */
764
+ declare function Modal({ open, onClose, label, closeOnScrimClick, children }: ModalProps): react.JSX.Element;
765
+ declare function ModalHeader({ children }: {
766
+ children: ReactNode;
767
+ }): react.JSX.Element;
768
+ declare function ModalBody({ children }: {
769
+ children: ReactNode;
770
+ }): react.JSX.Element;
771
+ declare function ModalFooter({ children }: {
772
+ children: ReactNode;
773
+ }): react.JSX.Element;
774
+
775
+ interface SheetProps {
776
+ open: boolean;
777
+ onClose: () => void;
778
+ /** Accessible name of the dialog. */
779
+ label: string;
780
+ /** Scrim click dismisses. Default true. */
781
+ closeOnScrimClick?: boolean;
782
+ children: ReactNode;
783
+ }
784
+ /**
785
+ * Bottom sheet. Same composition and dismissal model as Modal:
786
+ *
787
+ * ```tsx
788
+ * <Sheet open={open} onClose={() => setOpen(false)} label="Filters">
789
+ * <SheetHeader>Filters</SheetHeader>
790
+ * <SheetBody>…</SheetBody>
791
+ * <SheetFooter><Button onClick={apply}>Apply</Button></SheetFooter>
792
+ * </Sheet>
793
+ * ```
794
+ */
795
+ declare function Sheet({ open, onClose, label, closeOnScrimClick, children }: SheetProps): react.JSX.Element;
796
+ declare function SheetHeader({ children }: {
797
+ children: ReactNode;
798
+ }): react.JSX.Element;
799
+ declare function SheetBody({ children }: {
800
+ children: ReactNode;
801
+ }): react.JSX.Element;
802
+ declare function SheetFooter({ children }: {
803
+ children: ReactNode;
804
+ }): react.JSX.Element;
805
+
806
+ interface ConfirmDialogProps {
807
+ open: boolean;
808
+ onClose: () => void;
809
+ onConfirm: () => void;
810
+ title: string;
811
+ description?: string | undefined;
812
+ confirmLabel: string;
813
+ cancelLabel?: string | undefined;
814
+ /** Style the confirm action as destructive. */
815
+ danger?: boolean | undefined;
816
+ }
817
+ /**
818
+ * Pre-composed Modal for the confirm/cancel pattern.
819
+ *
820
+ * ```tsx
821
+ * <ConfirmDialog
822
+ * open={open}
823
+ * onClose={() => setOpen(false)}
824
+ * onConfirm={deleteItem}
825
+ * title="Delete item?"
826
+ * description="This cannot be undone."
827
+ * confirmLabel="Delete"
828
+ * danger
829
+ * />
830
+ * ```
831
+ */
832
+ declare function ConfirmDialog({ open, onClose, onConfirm, title, description, confirmLabel, cancelLabel, danger, }: ConfirmDialogProps): react.JSX.Element;
833
+
834
+ /**
835
+ * Page scaffold. Compose:
836
+ *
837
+ * ```tsx
838
+ * <Screen>
839
+ * <AppBar title="Sessions" />
840
+ * <ScreenContent>…</ScreenContent>
841
+ * <TabBar label="Primary">…</TabBar>
842
+ * </Screen>
843
+ * ```
844
+ */
845
+ declare function Screen({ children }: {
846
+ children: ReactNode;
847
+ }): react.JSX.Element;
848
+ /** The scrollable main region of a Screen. */
849
+ declare function ScreenContent({ children }: {
850
+ children: ReactNode;
851
+ }): react.JSX.Element;
852
+
853
+ interface AppBarProps {
854
+ title?: string | undefined;
855
+ /** Left slot — typically a back icon-only Button. */
856
+ leading?: ReactNode;
857
+ /** Right slot — actions. */
858
+ trailing?: ReactNode;
859
+ }
860
+ /**
861
+ * Top app bar. Sticky, one per screen; the title is the page h1.
862
+ *
863
+ * ```tsx
864
+ * <AppBar
865
+ * title="Sessions"
866
+ * trailing={
867
+ * <Button iconOnly aria-label="Settings" variant="ghost" onClick={openSettings}>
868
+ * <Icon name="settings" />
869
+ * </Button>
870
+ * }
871
+ * />
872
+ * ```
873
+ */
874
+ declare function AppBar({ title, leading, trailing }: AppBarProps): react.JSX.Element;
875
+
876
+ interface TabBarProps {
877
+ /** Accessible name of the navigation landmark. */
878
+ label: string;
879
+ children: ReactNode;
880
+ }
881
+ /**
882
+ * Bottom navigation. Items are links (or buttons for non-route actions);
883
+ * they stay in the natural tab order — roving focus is for composite
884
+ * widgets, not navigation landmarks.
885
+ *
886
+ * ```tsx
887
+ * <TabBar label="Primary">
888
+ * <TabBarItem label="Home" icon={<Icon name="home" />} href="/" active />
889
+ * <TabBarItem label="Search" icon={<Icon name="search" />} href="/search" />
890
+ * </TabBar>
891
+ * ```
892
+ */
893
+ declare function TabBar({ label, children }: TabBarProps): react.JSX.Element;
894
+ interface TabBarItemProps {
895
+ label: string;
896
+ icon: ReactNode;
897
+ href?: string | undefined;
898
+ onClick?: MouseEventHandler | undefined;
899
+ active?: boolean | undefined;
900
+ }
901
+ declare function TabBarItem({ label, icon, href, onClick, active }: TabBarItemProps): react.JSX.Element;
902
+
903
+ interface UseOverlayOptions {
904
+ open: boolean;
905
+ onClose: () => void;
906
+ }
907
+ /**
908
+ * Shared modal-surface behaviour: focus capture and restore, Escape to
909
+ * close, Tab cycling inside the panel, body scroll lock. Attach the
910
+ * returned ref to the dialog element (it needs tabIndex={-1}).
911
+ *
912
+ * Anchor-positioned overlays (Tooltip/Popover) will extend this hook with
913
+ * a floating-ui middleware pass; the options object leaves room for that.
914
+ */
915
+ declare function useOverlay<T extends HTMLElement>(options: UseOverlayOptions): RefObject<T | null>;
916
+
917
+ interface Presence {
918
+ /** Keep the element in the DOM (true during exit animation). */
919
+ mounted: boolean;
920
+ /** Drive the visual state — false during exit. */
921
+ visible: boolean;
922
+ }
923
+ /**
924
+ * Exit-animation helper. `open` flips instantly; `mounted` lags by
925
+ * `durationMs` on close so the exit transition can play.
926
+ */
927
+ declare function usePresence(open: boolean, durationMs: number): Presence;
928
+
929
+ interface RovingGroupOptions {
930
+ /** Selector for the focusable items inside the container. */
931
+ selector: string;
932
+ /** Arrow axis. Default "horizontal". */
933
+ orientation?: "horizontal" | "vertical" | "both";
934
+ }
935
+ /**
936
+ * Arrow-key focus movement for composite widgets (toolbars, menus, tab bars).
937
+ * Wraps at the edges; Home/End jump. Returns a keydown handler for the
938
+ * container.
939
+ */
940
+ declare function useRovingGroup(ref: RefObject<HTMLElement | null>, { selector, orientation }: RovingGroupOptions): (event: KeyboardEvent<HTMLElement>) => void;
941
+
942
+ export { AppBar, type AppBarProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeTone, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type CSSVars, Card, CardBody, CardFooter, CardHeader, type CardProps, type CardSectionProps, type CardVariant, Checkbox, type CheckboxProps, ConfirmDialog, type ConfirmDialogProps, Container, type ContainerProps, type ContainerWidth, Divider, type DividerProps, EmptyState, type EmptyStateProps, Field, type FieldContextValue, type FieldProps, Heading, type HeadingLevel, type HeadingProps, type HeadingTone, Icon, type IconProps, IconProvider, type IconProviderProps, type IconRender, type IconRenderProps, type IconSize, Inline, type InlineAlign, type InlineGap, type InlineJustify, type InlineProps, Input, type InputProps, type InputSize, Link, type LinkProps, type LinkVariant, ListGroup, type ListGroupProps, ListItem, type ListItemProps, Modal, ModalBody, ModalFooter, ModalHeader, type ModalProps, PasswordInput, type PasswordInputProps, type Presence, ProgressBar, type ProgressBarProps, Radio, type RadioProps, type RovingGroupOptions, Screen, ScreenContent, SearchField, type SearchFieldProps, type SegmentOption, SegmentedControl, type SegmentedControlProps, Select, type SelectProps, type SelectSize, Sheet, SheetBody, SheetFooter, SheetHeader, type SheetProps, Skeleton, type SkeletonProps, type SkeletonVariant, Spinner, type SpinnerProps, Stack, type StackAlign, type StackGap, type StackProps, Switch, type SwitchProps, Tab, TabBar, TabBarItem, type TabBarItemProps, type TabBarProps, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps, Tag, type TagProps, type TagTone, Text, type TextProps, type TextTone, type TextVariant, Textarea, type TextareaProps, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, type UseOverlayOptions, cx, useFieldContext, useOverlay, usePresence, useRovingGroup, useToast };