@molecule/app-ui-react 1.3.2 → 1.3.4

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.
Files changed (2) hide show
  1. package/README.md +1447 -1438
  2. package/package.json +3 -2
package/README.md CHANGED
@@ -3,20 +3,20 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-03T14:45:44.646Z
6
+ Generated: 2026-10-16T23:38:48.542Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-ui-react
10
10
 
11
- > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
- > It is written to be read by coding agents as much as by people, and is generated from this
13
- > package's source — edit `src/index.ts` JSDoc, not this file.
14
-
15
11
  React UI components for molecule.dev.
16
12
 
17
13
  Provides React implementations of the `@molecule/app-ui` component
18
14
  interfaces using the UIClassMap abstraction from `@molecule/app-ui`.
19
15
 
16
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
17
+ > It is written to be read by coding agents as much as by people, and is generated from this
18
+ > package's source — edit `src/index.ts` JSDoc, not this file.
19
+
20
20
  ## Quick Start
21
21
 
22
22
  ```tsx
@@ -66,331 +66,385 @@ npm install -D @types/react @types/react-dom
66
66
 
67
67
  ### Interfaces
68
68
 
69
- #### `AccordionItem`
69
+ #### `CSSProperties`
70
70
 
71
- A single item in an Accordion component.
71
+ Framework-agnostic CSS properties.
72
+ Mirrors React.CSSProperties but without React dependency.
72
73
 
73
74
  ```typescript
74
- interface AccordionItem<T = string> {
75
- /**
76
- * Item value/id.
77
- */
78
- value: T
79
- /**
80
- * Item header/trigger.
81
- */
82
- header: Children
83
- /**
84
- * Item content.
85
- */
86
- content: Children
87
- /**
88
- * Whether the item is disabled.
89
- */
90
- disabled?: boolean
75
+ interface CSSProperties {
76
+ [key: string]: string | number | undefined
91
77
  }
92
78
  ```
93
79
 
94
- #### `AccordionProps`
80
+ #### `BaseProps`
95
81
 
96
- Props for the Accordion component.
82
+ Base props shared by all components.
97
83
 
98
84
  ```typescript
99
- interface AccordionProps<T = string> extends BaseProps {
85
+ interface BaseProps {
100
86
  /**
101
- * Accordion items.
87
+ * Additional CSS class name(s).
102
88
  */
103
- items: AccordionItem<T>[]
89
+ className?: string
104
90
  /**
105
- * Expanded item(s).
91
+ * Inline styles.
106
92
  */
107
- value?: T | T[]
93
+ style?: CSSProperties
108
94
  /**
109
- * Default expanded item(s).
95
+ * Test ID for automated testing.
110
96
  */
111
- defaultValue?: T | T[]
97
+ testId?: string
112
98
  /**
113
- * Change handler.
99
+ * Automation ID for AI agents and E2E tests. Maps to the `data-mol-id`
100
+ * HTML attribute. Use `molId()` from `./automation.js` to generate
101
+ * semantic IDs. (Tooling only — screen readers do not expose `data-*`
102
+ * attributes; accessible names come from labels/`aria-*`.)
114
103
  */
115
- onChange?: (value: T | T[]) => void
104
+ automationId?: string
116
105
  /**
117
- * Whether multiple items can be expanded.
106
+ * Whether the component is disabled.
118
107
  */
119
- multiple?: boolean
108
+ disabled?: boolean
109
+ }
110
+ ```
111
+
112
+ #### `HTMLElementProps`
113
+
114
+ Base props for HTML elements.
115
+ Framework bindings should extend this with framework-specific attributes.
116
+
117
+ ```typescript
118
+ interface HTMLElementProps extends BaseProps {
119
+ id?: string
120
+ title?: string
121
+ tabIndex?: number
122
+ role?: string
123
+ 'aria-label'?: string
124
+ 'aria-labelledby'?: string
125
+ 'aria-describedby'?: string
126
+ 'aria-hidden'?: boolean
127
+ 'aria-disabled'?: boolean
128
+ 'aria-expanded'?: boolean
129
+ 'aria-selected'?: boolean
130
+ 'aria-checked'?: boolean | 'mixed'
131
+ 'aria-pressed'?: boolean | 'mixed'
132
+ 'aria-invalid'?: boolean
133
+ 'aria-required'?: boolean
134
+ 'aria-readonly'?: boolean
135
+ 'aria-busy'?: boolean
136
+ 'aria-live'?: 'off' | 'polite' | 'assertive'
137
+ onClick?: MouseEventHandler
138
+ onDoubleClick?: MouseEventHandler
139
+ onMouseEnter?: MouseEventHandler
140
+ onMouseLeave?: MouseEventHandler
141
+ onFocus?: FocusEventHandler
142
+ onBlur?: FocusEventHandler
143
+ onKeyDown?: KeyboardEventHandler
144
+ onKeyUp?: KeyboardEventHandler
145
+ onKeyPress?: KeyboardEventHandler
146
+ }
147
+ ```
148
+
149
+ #### `InputElementProps`
150
+
151
+ Base props for input elements.
152
+
153
+ ```typescript
154
+ interface InputElementProps extends HTMLElementProps {
155
+ name?: string
156
+ value?: string | number | readonly string[]
157
+ defaultValue?: string | number | readonly string[]
158
+ placeholder?: string
159
+ required?: boolean
160
+ readOnly?: boolean
161
+ autoFocus?: boolean
162
+ autoComplete?: string
163
+ maxLength?: number
164
+ minLength?: number
165
+ pattern?: string
166
+ onChange?: ChangeEventHandler
167
+ onInput?: FormEventHandler
168
+ }
169
+ ```
170
+
171
+ #### `ButtonElementProps`
172
+
173
+ Base props for button elements.
174
+
175
+ ```typescript
176
+ interface ButtonElementProps extends HTMLElementProps {
120
177
  /**
121
- * Whether items can be collapsed.
178
+ * Defaults to `'button'` in every framework binding, so a button never submits a
179
+ * form by accident. A form's primary action must set `type="submit"`: Enter then
180
+ * submits the form, and tests that select `button[type="submit"]` find it.
122
181
  */
123
- collapsible?: boolean
182
+ type?: 'button' | 'submit' | 'reset'
183
+ name?: string
184
+ value?: string
185
+ form?: string
124
186
  }
125
187
  ```
126
188
 
127
- #### `AlertProps`
189
+ #### `TextareaElementProps`
128
190
 
129
- Props for the Alert component.
191
+ Base props for textarea elements.
130
192
 
131
193
  ```typescript
132
- interface AlertProps extends HTMLElementProps {
194
+ interface TextareaElementProps extends HTMLElementProps {
195
+ name?: string
196
+ value?: string
197
+ defaultValue?: string
198
+ placeholder?: string
199
+ required?: boolean
200
+ readOnly?: boolean
201
+ autoFocus?: boolean
202
+ rows?: number
203
+ cols?: number
204
+ maxLength?: number
205
+ minLength?: number
206
+ wrap?: 'hard' | 'soft' | 'off'
207
+ onChange?: ChangeEventHandler
208
+ onInput?: FormEventHandler
209
+ }
210
+ ```
211
+
212
+ #### `SelectElementProps`
213
+
214
+ Base props for select elements.
215
+
216
+ ```typescript
217
+ interface SelectElementProps extends HTMLElementProps {
218
+ name?: string
219
+ required?: boolean
220
+ autoFocus?: boolean
221
+ multiple?: boolean
222
+ onChange?: ChangeEventHandler
223
+ }
224
+ ```
225
+
226
+ #### `FormElementProps`
227
+
228
+ Base props for form elements.
229
+
230
+ ```typescript
231
+ interface FormElementProps extends HTMLElementProps {
232
+ action?: string
233
+ method?: 'get' | 'post'
234
+ encType?: string
235
+ target?: string
236
+ noValidate?: boolean
237
+ autoComplete?: 'on' | 'off'
238
+ onSubmit?: FormEventHandler
239
+ onReset?: FormEventHandler
240
+ }
241
+ ```
242
+
243
+ #### `ButtonProps`
244
+
245
+ Props for the Button component.
246
+
247
+ ```typescript
248
+ interface ButtonProps extends ButtonElementProps {
133
249
  /**
134
- * Alert content.
250
+ * Button content.
135
251
  */
136
252
  children?: Children
137
253
  /**
138
- * Alert title.
254
+ * Visual variant.
139
255
  */
140
- title?: string
256
+ variant?: ButtonVariant
141
257
  /**
142
- * Alert status/type.
258
+ * Color scheme.
143
259
  */
144
- status?: ColorVariant
260
+ color?: ColorVariant
145
261
  /**
146
- * Alert variant.
262
+ * Button size.
147
263
  */
148
- variant?: 'solid' | 'subtle' | 'outline' | 'left-accent'
264
+ size?: ButtonSize
149
265
  /**
150
- * Whether the alert is dismissible.
266
+ * Whether the button is in a loading state.
151
267
  */
152
- dismissible?: boolean
268
+ loading?: boolean
153
269
  /**
154
- * Called when dismissed.
270
+ * Loading text to display.
155
271
  */
156
- onDismiss?: () => void
272
+ loadingText?: string
157
273
  /**
158
- * Icon to display.
274
+ * Whether the button takes full width.
159
275
  */
160
- icon?: Children
276
+ fullWidth?: boolean
161
277
  /**
162
- * Accessible label for the dismiss button.
163
- * @default 'Dismiss'
278
+ * Icon to display before the label.
164
279
  */
165
- dismissLabel?: string
280
+ leftIcon?: Children
281
+ /**
282
+ * Icon to display after the label.
283
+ */
284
+ rightIcon?: Children
166
285
  }
167
286
  ```
168
287
 
169
- #### `AuthGuardProps`
170
-
171
- Props for {@link AuthGuard}.
288
+ #### `InputProps`
172
289
 
173
- All props are optional — the default behavior (loading tag → redirect
174
- to `/login` → `<Outlet />`) matches the bare-bones guard apps were
175
- shipping locally. The props let apps customize per-call without
176
- forking the component.
290
+ Props for the Input component (text field, email, password, etc.).
177
291
 
178
292
  ```typescript
179
- interface AuthGuardProps {
293
+ interface InputProps extends InputElementProps {
180
294
  /**
181
- * Element rendered while `useAuth().state.initialized` is `false`.
182
- * Overrides the default `<div data-mol-id="auth-guard-loading">…</div>`
183
- * loading tag — pass a full-page spinner or any other UI you want
184
- * during the auth bootstrap window.
295
+ * Input type.
185
296
  */
186
- loadingFallback?: ReactNode
297
+ type?: InputType
187
298
  /**
188
- * i18n key used for the default loading-tag text. Defaults to
189
- * `'common.loading'`. Ignored when `loadingFallback` is set.
299
+ * Input size.
190
300
  */
191
- loadingKey?: string
301
+ size?: Size
192
302
  /**
193
- * Fallback text if the i18n key is missing. Defaults to `'Loading...'`
194
- * (the project-canonical ASCII glyph — Phase C of the locale
195
- * canonicalization plan). Ignored when `loadingFallback` is set.
196
- */
197
- loadingDefault?: string
198
- /**
199
- * Path to redirect to when the user is not authenticated. Defaults to
200
- * `'/login'`. The current `useLocation()` is preserved as `state.from`
201
- * for post-login restoration.
303
+ * Horizontal text alignment inside the input (see
304
+ * {@link InputClassOptions.align}). Defaults to the bond's own style.
202
305
  */
203
- loginPath?: string
306
+ align?: 'left' | 'center'
204
307
  /**
205
- * Callback invoked when `isAuthenticated` transitions to `true` (or is
206
- * already `true` on first render). Useful for one-shot per-app
207
- * bootstrap effects (e.g. seeding fixture data). The caller is
208
- * responsible for idempotency if the auth state can flip back and
209
- * forth — this fires on every transition.
308
+ * Label text.
210
309
  */
211
- onAuthenticated?: () => void
310
+ label?: string
212
311
  /**
213
- * Children to render when authenticated. Defaults to `<Outlet />`,
214
- * which is the React Router pattern this guard is designed for.
312
+ * Error message.
215
313
  */
216
- children?: ReactNode
217
- }
218
- ```
219
-
220
- #### `AvatarProps`
221
-
222
- Props for the Avatar component.
223
-
224
- ```typescript
225
- interface AvatarProps extends HTMLElementProps {
314
+ error?: string
226
315
  /**
227
- * Image source URL.
316
+ * Hint/help text.
228
317
  */
229
- src?: string
318
+ hint?: string
230
319
  /**
231
- * Alt text for the image.
320
+ * Element to display on the left.
232
321
  */
233
- alt?: string
322
+ leftElement?: Children
234
323
  /**
235
- * Name for fallback initials.
324
+ * Element to display on the right.
236
325
  */
237
- name?: string
326
+ rightElement?: Children
238
327
  /**
239
- * Avatar size.
328
+ * Whether to show a clear button.
240
329
  */
241
- size?: Size | number
330
+ clearable?: boolean
242
331
  /**
243
- * Whether the avatar is rounded.
332
+ * Called when the clear button is clicked.
244
333
  */
245
- rounded?: boolean
334
+ onClear?: () => void
246
335
  /**
247
- * Fallback element when no image.
336
+ * Accessible label for the clear button.
337
+ * @default 'Clear'
248
338
  */
249
- fallback?: Children
339
+ clearLabel?: string
250
340
  }
251
341
  ```
252
342
 
253
- #### `BadgeProps`
343
+ #### `TextareaProps`
254
344
 
255
- Props for the Badge component (status labels, counts, tags).
345
+ Props for the Textarea component.
256
346
 
257
347
  ```typescript
258
- interface BadgeProps extends HTMLElementProps {
348
+ interface TextareaProps extends TextareaElementProps {
259
349
  /**
260
- * Badge content.
350
+ * Text size tier (see {@link TextareaClassOptions.size}) — pass the same
351
+ * `size` as a neighboring Input for identical font sizes.
261
352
  */
262
- children?: Children
353
+ size?: Size
263
354
  /**
264
- * Badge color.
355
+ * Label text.
265
356
  */
266
- color?: ColorVariant
357
+ label?: string
267
358
  /**
268
- * Badge variant.
359
+ * Error message.
269
360
  */
270
- variant?: 'solid' | 'outline' | 'subtle'
361
+ error?: string
271
362
  /**
272
- * Badge size.
363
+ * Hint/help text.
273
364
  */
274
- size?: Size
365
+ hint?: string
275
366
  /**
276
- * Whether the badge is rounded.
367
+ * Whether the textarea auto-resizes.
277
368
  */
278
- rounded?: boolean
369
+ autoResize?: boolean
370
+ /**
371
+ * Minimum number of rows.
372
+ */
373
+ minRows?: number
374
+ /**
375
+ * Maximum number of rows.
376
+ */
377
+ maxRows?: number
279
378
  }
280
379
  ```
281
380
 
282
- #### `BaseProps`
381
+ #### `SelectOption`
283
382
 
284
- Base props shared by all components.
383
+ A single option in a Select dropdown.
285
384
 
286
385
  ```typescript
287
- interface BaseProps {
288
- /**
289
- * Additional CSS class name(s).
290
- */
291
- className?: string
386
+ interface SelectOption<T = string> {
292
387
  /**
293
- * Inline styles.
388
+ * Option value.
294
389
  */
295
- style?: CSSProperties
390
+ value: T
296
391
  /**
297
- * Test ID for automated testing.
392
+ * Display label.
298
393
  */
299
- testId?: string
394
+ label: string
300
395
  /**
301
- * Automation ID for AI agents and E2E tests. Maps to the `data-mol-id`
302
- * HTML attribute. Use `molId()` from `./automation.js` to generate
303
- * semantic IDs. (Tooling only — screen readers do not expose `data-*`
304
- * attributes; accessible names come from labels/`aria-*`.)
396
+ * Whether the option is disabled.
305
397
  */
306
- automationId?: string
398
+ disabled?: boolean
307
399
  /**
308
- * Whether the component is disabled.
400
+ * Option group (for grouped selects).
309
401
  */
310
- disabled?: boolean
311
- }
312
- ```
313
-
314
- #### `ButtonElementProps`
315
-
316
- Base props for button elements.
317
-
318
- ```typescript
319
- interface ButtonElementProps extends HTMLElementProps {
320
- type?: 'button' | 'submit' | 'reset'
321
- name?: string
322
- value?: string
323
- form?: string
402
+ group?: string
324
403
  }
325
404
  ```
326
405
 
327
- #### `ButtonProps`
406
+ #### `SelectProps`
328
407
 
329
- Props for the Button component.
408
+ Props for the Select dropdown component (single or multi-select).
330
409
 
331
410
  ```typescript
332
- interface ButtonProps extends ButtonElementProps {
333
- /**
334
- * Button content.
335
- */
336
- children?: Children
337
- /**
338
- * Visual variant.
339
- */
340
- variant?: ButtonVariant
341
- /**
342
- * Color scheme.
343
- */
344
- color?: ColorVariant
345
- /**
346
- * Button size.
347
- */
348
- size?: ButtonSize
411
+ interface SelectProps<T = string> extends SelectElementProps {
349
412
  /**
350
- * Whether the button is in a loading state.
413
+ * Select options.
351
414
  */
352
- loading?: boolean
415
+ options: SelectOption<T>[]
353
416
  /**
354
- * Loading text to display.
417
+ * Current value.
355
418
  */
356
- loadingText?: string
419
+ value?: T
357
420
  /**
358
- * Whether the button takes full width.
421
+ * Change handler (with typed value).
359
422
  */
360
- fullWidth?: boolean
423
+ onValueChange?: (value: T) => void
361
424
  /**
362
- * Icon to display before the label.
425
+ * Select size.
363
426
  */
364
- leftIcon?: Children
427
+ size?: Size
365
428
  /**
366
- * Icon to display after the label.
429
+ * Label text.
367
430
  */
368
- rightIcon?: Children
369
- }
370
- ```
371
-
372
- #### `CardProps`
373
-
374
- Props for the Card container component (elevated, outlined, or filled surface).
375
-
376
- ```typescript
377
- interface CardProps extends HTMLElementProps {
431
+ label?: string
378
432
  /**
379
- * Card content.
433
+ * Placeholder text.
380
434
  */
381
- children?: Children
435
+ placeholder?: string
382
436
  /**
383
- * Card variant.
437
+ * Error message.
384
438
  */
385
- variant?: 'elevated' | 'outlined' | 'filled'
439
+ error?: string
386
440
  /**
387
- * Whether the card is interactive (clickable).
441
+ * Hint/help text.
388
442
  */
389
- interactive?: boolean
443
+ hint?: string
390
444
  /**
391
- * Padding size.
445
+ * Whether to allow clearing the selection.
392
446
  */
393
- padding?: Size | 'none'
447
+ clearable?: boolean
394
448
  }
395
449
  ```
396
450
 
@@ -423,1027 +477,813 @@ interface CheckboxProps extends InputElementProps {
423
477
  }
424
478
  ```
425
479
 
426
- #### `ConfirmButtonProps`
480
+ #### `RadioOption`
427
481
 
428
- Props for {@link ConfirmButton}.
482
+ A single option in a RadioGroup.
429
483
 
430
484
  ```typescript
431
- interface ConfirmButtonProps extends Omit<
432
- ButtonProps,
433
- 'onClick' | 'children' | 'onConfirm' | keyof React.DOMAttributes<HTMLButtonElement>
434
- > {
485
+ interface RadioOption<T = string> {
435
486
  /**
436
- * Fired ONLY on the confirming (second) click — i.e. when the action
437
- * actually commits. Errors should be handled by the caller (render an
438
- * inline banner); while the returned promise is pending the button stays
439
- * in its confirming state and ignores further clicks.
487
+ * Option value.
440
488
  */
441
- onConfirm: () => void | Promise<void>
489
+ value: T
442
490
  /**
443
- * Visible label in the idle (armed-to-be-clicked) state.
491
+ * Display label.
444
492
  */
445
- label: React.ReactNode
493
+ label: Children
446
494
  /**
447
- * Visible label in the armed state — the "click again to confirm" step.
448
- * Defaults to a localized "Confirm?" via the i18n bond.
495
+ * Whether the option is disabled.
449
496
  */
450
- confirmLabel?: React.ReactNode
497
+ disabled?: boolean
498
+ }
499
+ ```
500
+
501
+ #### `RadioGroupProps`
502
+
503
+ Props for the RadioGroup component.
504
+
505
+ ```typescript
506
+ interface RadioGroupProps<T = string> extends BaseProps {
451
507
  /**
452
- * Visible label while {@link onConfirm} is pending. Defaults to a
453
- * localized "Working…".
508
+ * Radio options.
454
509
  */
455
- pendingLabel?: React.ReactNode
510
+ options: RadioOption<T>[]
456
511
  /**
457
- * Seconds before the armed state auto-disarms back to idle. Low-stakes
458
- * actions tolerate a long window; destructive ones should keep it short.
459
- * `0` disables auto-disarm (not recommended — a stray click then leaves
460
- * the button armed forever).
512
+ * Current value.
461
513
  */
462
- disarmSeconds?: number
514
+ value?: T
463
515
  /**
464
- * Disable the control entirely (idle and confirming states).
516
+ * Change handler.
465
517
  */
466
- disabled?: boolean
518
+ onChange?: (value: T) => void
467
519
  /**
468
- * data-mol-id for the button (all states share one element).
520
+ * Radio size.
469
521
  */
470
- testId?: string
471
- }
472
- ```
473
-
474
- #### `ConfirmDialogProps`
475
-
476
- Props for {@link ConfirmDialog}.
477
-
478
- ```typescript
479
- interface ConfirmDialogProps {
480
- /** Whether the dialog is open. */
481
- open: boolean
482
- /** Called when the dialog should close (escape, backdrop, cancel). */
483
- onClose: () => void
484
- /** Title. */
485
- title: ReactNode
486
- /** Body / warning text. */
487
- description: ReactNode
488
- /** Confirm-button label (e.g. "Delete"). */
489
- confirmLabel?: ReactNode
490
- /** Cancel-button label. */
491
- cancelLabel?: ReactNode
492
- /** Called when the user confirms. */
493
- onConfirm: () => void | Promise<void>
494
- /** Is the confirm action destructive? Defaults to true. */
495
- destructive?: boolean
496
- /** Extra body content between description and footer. */
497
- children?: ReactNode
498
- /** Disables both buttons. Set it yourself while your `onConfirm` promise is pending — the dialog does not track it and does not auto-close. */
499
- loading?: boolean
500
- }
501
- ```
502
-
503
- #### `ContainerProps`
504
-
505
- Props for the Container layout component.
506
-
507
- ```typescript
508
- interface ContainerProps extends HTMLElementProps {
522
+ size?: Size
509
523
  /**
510
- * Container content.
524
+ * Group label.
511
525
  */
512
- children?: Children
526
+ label?: string
513
527
  /**
514
- * Maximum width.
528
+ * Shared `name` attribute for the group's radio inputs (used for native
529
+ * form submission). When omitted, a unique per-instance name is generated
530
+ * so separate groups never merge — the visible `label` is deliberately
531
+ * NOT used as the name, because two groups with the same label (e.g. two
532
+ * "Size" pickers) would otherwise form ONE native radio group and
533
+ * deselect each other.
515
534
  */
516
- maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | string
535
+ name?: string
517
536
  /**
518
- * Whether to center the container.
537
+ * Layout direction.
519
538
  */
520
- centered?: boolean
539
+ direction?: 'horizontal' | 'vertical'
521
540
  /**
522
- * Horizontal padding.
541
+ * Error message.
523
542
  */
524
- paddingX?: Size | string
543
+ error?: string
525
544
  }
526
545
  ```
527
546
 
528
- #### `CSSProperties`
547
+ #### `SwitchProps`
529
548
 
530
- Framework-agnostic CSS properties.
531
- Mirrors React.CSSProperties but without React dependency.
549
+ Props for the Switch/Toggle component.
532
550
 
533
551
  ```typescript
534
- interface CSSProperties {
535
- [key: string]: string | number | undefined
552
+ interface SwitchProps extends InputElementProps {
553
+ /**
554
+ * Switch label.
555
+ */
556
+ label?: Children
557
+ /**
558
+ * Whether the switch is on.
559
+ */
560
+ checked?: boolean
561
+ /**
562
+ * Switch size.
563
+ */
564
+ size?: Size
565
+ /**
566
+ * Color when on.
567
+ */
568
+ color?: ColorVariant
536
569
  }
537
570
  ```
538
571
 
539
- #### `FlexProps`
572
+ #### `ModalProps`
540
573
 
541
- Flex container props.
574
+ Props for the Modal/Dialog component.
542
575
 
543
576
  ```typescript
544
- interface FlexProps extends HTMLElementProps {
577
+ interface ModalProps extends HTMLElementProps {
545
578
  /**
546
- * Flex content.
579
+ * Whether the modal is open.
580
+ */
581
+ open: boolean
582
+ /**
583
+ * Called when the modal should close.
584
+ */
585
+ onClose: () => void
586
+ /**
587
+ * Modal title.
588
+ */
589
+ title?: string
590
+ /**
591
+ * Modal content.
547
592
  */
548
593
  children?: Children
549
594
  /**
550
- * Flex direction.
595
+ * Modal size.
551
596
  */
552
- direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse'
597
+ size?: ModalSize
553
598
  /**
554
- * Justify content.
599
+ * Whether to show a close button.
555
600
  */
556
- justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'
601
+ showCloseButton?: boolean
557
602
  /**
558
- * Align items.
603
+ * Whether clicking the overlay closes the modal.
559
604
  */
560
- align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'
605
+ closeOnOverlayClick?: boolean
561
606
  /**
562
- * Flex wrap.
607
+ * Whether pressing Escape closes the modal.
563
608
  */
564
- wrap?: 'wrap' | 'nowrap' | 'wrap-reverse'
609
+ closeOnEscape?: boolean
565
610
  /**
566
- * Gap between items.
611
+ * Footer content (typically action buttons).
567
612
  */
568
- gap?: Size | string | number
613
+ footer?: Children
614
+ /**
615
+ * Whether the modal is centered vertically.
616
+ */
617
+ centered?: boolean
618
+ /**
619
+ * Whether to prevent body scroll when open.
620
+ */
621
+ preventScroll?: boolean
622
+ /**
623
+ * Accessible label for the close button.
624
+ * @default 'Close'
625
+ */
626
+ closeLabel?: string
569
627
  }
570
628
  ```
571
629
 
572
- #### `FormElementProps`
630
+ #### `SpinnerProps`
573
631
 
574
- Base props for form elements.
632
+ Props for the Spinner/loading indicator component.
575
633
 
576
634
  ```typescript
577
- interface FormElementProps extends HTMLElementProps {
578
- action?: string
579
- method?: 'get' | 'post'
580
- encType?: string
581
- target?: string
582
- noValidate?: boolean
583
- autoComplete?: 'on' | 'off'
584
- onSubmit?: FormEventHandler
585
- onReset?: FormEventHandler
635
+ interface SpinnerProps extends BaseProps {
636
+ /**
637
+ * Spinner size.
638
+ */
639
+ size?: Size
640
+ /**
641
+ * Spinner color.
642
+ */
643
+ color?: ColorVariant | string
644
+ /**
645
+ * Loading label (for accessibility).
646
+ */
647
+ label?: string
648
+ /**
649
+ * Spinner thickness.
650
+ */
651
+ thickness?: number
586
652
  }
587
653
  ```
588
654
 
589
- #### `FormFieldProps`
655
+ #### `AvatarProps`
590
656
 
591
- Form field wrapper props.
657
+ Props for the Avatar component.
592
658
 
593
659
  ```typescript
594
- interface FormFieldProps extends HTMLElementProps {
660
+ interface AvatarProps extends HTMLElementProps {
595
661
  /**
596
- * Field content.
662
+ * Image source URL.
597
663
  */
598
- children?: Children
664
+ src?: string
599
665
  /**
600
- * Field label.
666
+ * Alt text for the image.
601
667
  */
602
- label?: string
668
+ alt?: string
603
669
  /**
604
- * Field name.
670
+ * Name for fallback initials.
605
671
  */
606
672
  name?: string
607
673
  /**
608
- * Error message.
674
+ * Avatar size.
609
675
  */
610
- error?: string
676
+ size?: Size | number
611
677
  /**
612
- * Hint/help text.
678
+ * Whether the avatar is rounded.
613
679
  */
614
- hint?: string
680
+ rounded?: boolean
615
681
  /**
616
- * Whether the field is required.
682
+ * Fallback element when no image.
617
683
  */
618
- required?: boolean
684
+ fallback?: Children
619
685
  }
620
686
  ```
621
687
 
622
- #### `FormProps`
688
+ #### `BadgeProps`
623
689
 
624
- Props for the Form component (wraps inputs with submission handling and validation).
690
+ Props for the Badge component (status labels, counts, tags).
625
691
 
626
692
  ```typescript
627
- interface FormProps extends FormElementProps {
693
+ interface BadgeProps extends HTMLElementProps {
628
694
  /**
629
- * Form content.
695
+ * Badge content.
630
696
  */
631
697
  children?: Children
632
698
  /**
633
- * Submit handler with form data.
699
+ * Badge color.
634
700
  */
635
- onFormSubmit?: (data: Record<string, unknown>) => void | Promise<void>
701
+ color?: ColorVariant
636
702
  /**
637
- * Whether the form is submitting.
703
+ * Badge variant.
638
704
  */
639
- submitting?: boolean
705
+ variant?: 'solid' | 'outline' | 'subtle'
706
+ /**
707
+ * Badge size.
708
+ */
709
+ size?: Size
710
+ /**
711
+ * Whether the badge is rounded.
712
+ */
713
+ rounded?: boolean
640
714
  }
641
715
  ```
642
716
 
643
- #### `GridProps`
717
+ #### `TooltipProps`
644
718
 
645
- Grid container props.
719
+ Props for the Tooltip component (hover/focus popover with informational text).
646
720
 
647
721
  ```typescript
648
- interface GridProps extends HTMLElementProps {
649
- /**
650
- * Grid content.
651
- */
652
- children?: Children
722
+ interface TooltipProps extends HTMLElementProps {
653
723
  /**
654
- * Number of columns.
724
+ * Tooltip content.
655
725
  */
656
- columns?: number | string
726
+ content: Children
657
727
  /**
658
- * Number of rows.
728
+ * Element that triggers the tooltip.
659
729
  */
660
- rows?: number | string
730
+ children: Children
661
731
  /**
662
- * Gap between items.
732
+ * Tooltip placement.
663
733
  */
664
- gap?: Size | string | number
734
+ placement?: TooltipPlacement
665
735
  /**
666
- * Column gap.
736
+ * Delay before showing (ms).
667
737
  */
668
- columnGap?: Size | string | number
738
+ delay?: number
669
739
  /**
670
- * Row gap.
740
+ * Whether the tooltip has an arrow.
671
741
  */
672
- rowGap?: Size | string | number
742
+ hasArrow?: boolean
673
743
  }
674
744
  ```
675
745
 
676
- #### `HTMLElementProps`
746
+ #### `CardProps`
677
747
 
678
- Base props for HTML elements.
679
- Framework bindings should extend this with framework-specific attributes.
748
+ Props for the Card container component (elevated, outlined, or filled surface).
680
749
 
681
750
  ```typescript
682
- interface HTMLElementProps extends BaseProps {
683
- id?: string
684
- title?: string
685
- tabIndex?: number
686
- role?: string
687
- 'aria-label'?: string
688
- 'aria-labelledby'?: string
689
- 'aria-describedby'?: string
690
- 'aria-hidden'?: boolean
691
- 'aria-disabled'?: boolean
692
- 'aria-expanded'?: boolean
693
- 'aria-selected'?: boolean
694
- 'aria-checked'?: boolean | 'mixed'
695
- 'aria-pressed'?: boolean | 'mixed'
696
- 'aria-invalid'?: boolean
697
- 'aria-required'?: boolean
698
- 'aria-readonly'?: boolean
699
- 'aria-busy'?: boolean
700
- 'aria-live'?: 'off' | 'polite' | 'assertive'
701
- onClick?: MouseEventHandler
702
- onDoubleClick?: MouseEventHandler
703
- onMouseEnter?: MouseEventHandler
704
- onMouseLeave?: MouseEventHandler
705
- onFocus?: FocusEventHandler
706
- onBlur?: FocusEventHandler
707
- onKeyDown?: KeyboardEventHandler
708
- onKeyUp?: KeyboardEventHandler
709
- onKeyPress?: KeyboardEventHandler
710
- }
711
- ```
712
-
713
- #### `IconProps`
714
-
715
- Props for {@link Icon}.
716
-
717
- Extends `SVGProps<SVGSVGElement>` so callers can pass any SVG / HTML
718
- attribute the underlying `<svg>` accepts, including `data-mol-id`,
719
- `aria-*`, `role`, event handlers, `style`, etc. — without the
720
- component needing to enumerate them.
721
-
722
- ```typescript
723
- interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'width' | 'height' | 'viewBox' | 'fill'> {
751
+ interface CardProps extends HTMLElementProps {
724
752
  /**
725
- * Name of the glyph to look up in the bonded icon set. Typed as
726
- * {@link IconName} so a typo fails the type-check instead of throwing at
727
- * render time; sets with extra glyphs augment `CustomIconNames` in
728
- * `@molecule/app-icons` to widen it.
753
+ * Card content.
729
754
  */
730
- name: IconName
731
- /** Width and height of the rendered SVG in pixels. Defaults to 20. */
732
- size?: number
733
- /** Class name forwarded to the root `<svg>`. */
734
- className?: string
755
+ children?: Children
756
+ /**
757
+ * Card variant.
758
+ */
759
+ variant?: 'elevated' | 'outlined' | 'filled'
760
+ /**
761
+ * Whether the card is interactive (clickable).
762
+ */
763
+ interactive?: boolean
764
+ /**
765
+ * Padding size.
766
+ */
767
+ padding?: Size | 'none'
735
768
  }
736
769
  ```
737
770
 
738
- #### `InputElementProps`
771
+ #### `ContainerProps`
739
772
 
740
- Base props for input elements.
773
+ Props for the Container layout component.
741
774
 
742
775
  ```typescript
743
- interface InputElementProps extends HTMLElementProps {
744
- name?: string
745
- value?: string | number | readonly string[]
746
- defaultValue?: string | number | readonly string[]
747
- placeholder?: string
748
- required?: boolean
749
- readOnly?: boolean
750
- autoFocus?: boolean
751
- autoComplete?: string
752
- maxLength?: number
753
- minLength?: number
754
- pattern?: string
755
- onChange?: ChangeEventHandler
756
- onInput?: FormEventHandler
776
+ interface ContainerProps extends HTMLElementProps {
777
+ /**
778
+ * Container content.
779
+ */
780
+ children?: Children
781
+ /**
782
+ * Maximum width.
783
+ */
784
+ maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | string
785
+ /**
786
+ * Whether to center the container.
787
+ */
788
+ centered?: boolean
789
+ /**
790
+ * Horizontal padding.
791
+ */
792
+ paddingX?: Size | string
757
793
  }
758
794
  ```
759
795
 
760
- #### `InputProps`
796
+ #### `FlexProps`
761
797
 
762
- Props for the Input component (text field, email, password, etc.).
798
+ Flex container props.
763
799
 
764
800
  ```typescript
765
- interface InputProps extends InputElementProps {
801
+ interface FlexProps extends HTMLElementProps {
766
802
  /**
767
- * Input type.
803
+ * Flex content.
768
804
  */
769
- type?: InputType
805
+ children?: Children
770
806
  /**
771
- * Input size.
807
+ * Flex direction.
772
808
  */
773
- size?: Size
809
+ direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse'
774
810
  /**
775
- * Horizontal text alignment inside the input (see
776
- * {@link InputClassOptions.align}). Defaults to the bond's own style.
811
+ * Justify content.
777
812
  */
778
- align?: 'left' | 'center'
813
+ justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'
779
814
  /**
780
- * Label text.
815
+ * Align items.
781
816
  */
782
- label?: string
817
+ align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'
783
818
  /**
784
- * Error message.
819
+ * Flex wrap.
785
820
  */
786
- error?: string
821
+ wrap?: 'wrap' | 'nowrap' | 'wrap-reverse'
787
822
  /**
788
- * Hint/help text.
823
+ * Gap between items.
789
824
  */
790
- hint?: string
825
+ gap?: Size | string | number
826
+ }
827
+ ```
828
+
829
+ #### `GridProps`
830
+
831
+ Grid container props.
832
+
833
+ ```typescript
834
+ interface GridProps extends HTMLElementProps {
791
835
  /**
792
- * Element to display on the left.
836
+ * Grid content.
793
837
  */
794
- leftElement?: Children
838
+ children?: Children
795
839
  /**
796
- * Element to display on the right.
840
+ * Number of columns.
797
841
  */
798
- rightElement?: Children
842
+ columns?: number | string
799
843
  /**
800
- * Whether to show a clear button.
844
+ * Number of rows.
801
845
  */
802
- clearable?: boolean
846
+ rows?: number | string
803
847
  /**
804
- * Called when the clear button is clicked.
848
+ * Gap between items.
805
849
  */
806
- onClear?: () => void
850
+ gap?: Size | string | number
807
851
  /**
808
- * Accessible label for the clear button.
809
- * @default 'Clear'
852
+ * Column gap.
810
853
  */
811
- clearLabel?: string
854
+ columnGap?: Size | string | number
855
+ /**
856
+ * Row gap.
857
+ */
858
+ rowGap?: Size | string | number
812
859
  }
813
860
  ```
814
861
 
815
- #### `LanguagePickerProps`
816
-
817
- Props for {@link LanguagePicker}.
862
+ #### `SpacerProps`
818
863
 
819
- Extends standard `<button>` attributes so callers can pass any extra
820
- `data-*`, `aria-*`, event handler, or `style` prop without forking the
821
- component. The named props let apps customize the trigger label, icon,
822
- size, and modal heading without spreading attribute concerns.
864
+ Props for the Spacer layout component (adds whitespace between elements).
823
865
 
824
866
  ```typescript
825
- interface LanguagePickerProps extends Omit<
826
- ButtonHTMLAttributes<HTMLButtonElement>,
827
- 'aria-label' | 'onClick' | 'type' | 'children'
828
- > {
829
- /** i18n key for the trigger button label / aria-label. Defaults to `'footer.language'`. */
830
- labelKey?: string
831
- /** Fallback label if the i18n key is missing. Defaults to `'Language'`. */
832
- labelDefault?: string
833
- /** i18n key for the modal title. Defaults to `'languagePicker.modalTitle'`. */
834
- modalTitleKey?: string
835
- /** Fallback modal title if the i18n key is missing. Defaults to `'Choose language'`. */
836
- modalTitleDefault?: string
837
- /** Icon glyph to render before the current locale name. Defaults to `'globe'`. */
838
- icon?: IconName
839
- /** Pixel size for the rendered icon. Defaults to `16`. */
840
- iconSize?: number
867
+ interface SpacerProps extends BaseProps {
841
868
  /**
842
- * What to render inside the trigger button.
843
- *
844
- * - `'name'` — globe icon + current locale's native name (e.g. `🌐 English`).
845
- * This is the default; matches the molecule-dev footer pattern.
846
- * - `'code'` — globe icon + lowercase locale code (e.g. `🌐 en`). Useful in
847
- * tight chrome where the long native name (e.g. "Bahasa Indonesia") would wrap.
848
- * - `'icon'` — globe icon only. Useful in icon-bar headers.
869
+ * Space size.
849
870
  */
850
- display?: 'name' | 'code' | 'icon'
851
- /** Optional className appended to the trigger button. */
852
- className?: string
853
- /** Optional render override: receives `{ open }` and replaces the default trigger. */
854
- renderTrigger?: (open: () => void) => ReactNode
871
+ size?: Size | string | number
872
+ /**
873
+ * Whether the spacer is horizontal.
874
+ */
875
+ horizontal?: boolean
855
876
  }
856
877
  ```
857
878
 
858
- #### `LoadErrorBannerProps`
879
+ #### `FormProps`
859
880
 
860
- Props for {@link LoadErrorBanner}. Rest props are forwarded to the root
861
- `<div>`, so they are typed as its attributes.
881
+ Props for the Form component (wraps inputs with submission handling and validation).
862
882
 
863
883
  ```typescript
864
- interface LoadErrorBannerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
865
- /** The primary error line (e.g. the localized "Could not load X."). */
866
- message: React.ReactNode
867
- /** Secondary line under the message (status code, hint). */
868
- detail?: React.ReactNode
884
+ interface FormProps extends FormElementProps {
869
885
  /**
870
- * Called when Retry is clicked. Return the promise; while pending, the
871
- * button is disabled and shows "Retrying…". Omit for a display-only
872
- * banner.
886
+ * Form content.
873
887
  */
874
- onRetry?: () => void | Promise<void>
875
- /** Retry-button label. Falls back to a localized "Retry". */
876
- retryLabel?: React.ReactNode
877
- /** `data-mol-id` for the retry button (default `load-error-retry`). */
878
- retryMolId?: string
879
- /** Extra content between message and retry row (e.g. an error code). */
880
- children?: React.ReactNode
888
+ children?: Children
889
+ /**
890
+ * Submit handler with form data.
891
+ */
892
+ onFormSubmit?: (data: Record<string, unknown>) => void | Promise<void>
893
+ /**
894
+ * Whether the form is submitting.
895
+ */
896
+ submitting?: boolean
881
897
  }
882
898
  ```
883
899
 
884
- #### `ModalProps`
900
+ #### `FormFieldProps`
885
901
 
886
- Props for the Modal/Dialog component.
902
+ Form field wrapper props.
887
903
 
888
904
  ```typescript
889
- interface ModalProps extends HTMLElementProps {
905
+ interface FormFieldProps extends HTMLElementProps {
890
906
  /**
891
- * Whether the modal is open.
907
+ * Field content.
892
908
  */
893
- open: boolean
909
+ children?: Children
894
910
  /**
895
- * Called when the modal should close.
911
+ * Field label.
896
912
  */
897
- onClose: () => void
913
+ label?: string
898
914
  /**
899
- * Modal title.
915
+ * Field name.
900
916
  */
901
- title?: string
917
+ name?: string
902
918
  /**
903
- * Modal content.
919
+ * Error message.
904
920
  */
905
- children?: Children
921
+ error?: string
906
922
  /**
907
- * Modal size.
923
+ * Hint/help text.
908
924
  */
909
- size?: ModalSize
925
+ hint?: string
910
926
  /**
911
- * Whether to show a close button.
927
+ * Whether the field is required.
912
928
  */
913
- showCloseButton?: boolean
929
+ required?: boolean
930
+ }
931
+ ```
932
+
933
+ #### `AlertProps`
934
+
935
+ Props for the Alert component.
936
+
937
+ ```typescript
938
+ interface AlertProps extends HTMLElementProps {
914
939
  /**
915
- * Whether clicking the overlay closes the modal.
940
+ * Alert content.
916
941
  */
917
- closeOnOverlayClick?: boolean
942
+ children?: Children
918
943
  /**
919
- * Whether pressing Escape closes the modal.
944
+ * Alert title.
920
945
  */
921
- closeOnEscape?: boolean
946
+ title?: string
922
947
  /**
923
- * Footer content (typically action buttons).
948
+ * Alert status/type.
924
949
  */
925
- footer?: Children
950
+ status?: ColorVariant
926
951
  /**
927
- * Whether the modal is centered vertically.
952
+ * Alert variant.
928
953
  */
929
- centered?: boolean
954
+ variant?: 'solid' | 'subtle' | 'outline' | 'left-accent'
930
955
  /**
931
- * Whether to prevent body scroll when open.
956
+ * Whether the alert is dismissible.
932
957
  */
933
- preventScroll?: boolean
958
+ dismissible?: boolean
934
959
  /**
935
- * Accessible label for the close button.
936
- * @default 'Close'
960
+ * Called when dismissed.
937
961
  */
938
- closeLabel?: string
962
+ onDismiss?: () => void
963
+ /**
964
+ * Icon to display.
965
+ */
966
+ icon?: Children
967
+ /**
968
+ * Accessible label for the dismiss button.
969
+ * @default 'Dismiss'
970
+ */
971
+ dismissLabel?: string
939
972
  }
940
973
  ```
941
974
 
942
- #### `PaginationProps`
975
+ #### `ToastProps`
943
976
 
944
- Props for the Pagination component (page navigation with current page, total, page size, and change callbacks).
977
+ Props for the Toast/notification component.
945
978
 
946
979
  ```typescript
947
- interface PaginationProps extends BaseProps {
980
+ interface ToastProps extends HTMLElementProps {
948
981
  /**
949
- * Current page (1-indexed).
982
+ * Toast content.
950
983
  */
951
- page: number
984
+ children?: Children
952
985
  /**
953
- * Total number of pages.
986
+ * Toast title.
954
987
  */
955
- totalPages: number
988
+ title?: string
956
989
  /**
957
- * Page change handler.
990
+ * Toast description.
958
991
  */
959
- onChange: (page: number) => void
992
+ description?: string
960
993
  /**
961
- * Number of sibling pages to show.
994
+ * Toast status/type.
962
995
  */
963
- siblings?: number
996
+ status?: ColorVariant
964
997
  /**
965
- * Number of boundary pages to show.
998
+ * Duration in milliseconds (0 for persistent).
966
999
  */
967
- boundaries?: number
1000
+ duration?: number
968
1001
  /**
969
- * Pagination size.
1002
+ * Whether the toast is dismissible.
970
1003
  */
971
- size?: Size
1004
+ dismissible?: boolean
972
1005
  /**
973
- * Whether to show first/last buttons.
1006
+ * Called when dismissed.
974
1007
  */
975
- showFirstLast?: boolean
1008
+ onDismiss?: () => void
976
1009
  /**
977
- * Whether to show previous/next buttons.
1010
+ * Toast position.
978
1011
  */
979
- showPrevNext?: boolean
1012
+ position?: 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
980
1013
  /**
981
- * Accessible labels for pagination controls.
1014
+ * Accessible label for the close button.
1015
+ * @default 'Close'
982
1016
  */
983
- labels?: {
984
- nav?: string
985
- first?: string
986
- previous?: string
987
- next?: string
988
- last?: string
989
- goToPage?: (page: number) => string
990
- }
991
- }
992
- ```
993
-
994
- #### `PanelCloseProviderProps`
995
-
996
- Props for {@link PanelCloseProvider}.
997
-
998
- ```typescript
999
- interface PanelCloseProviderProps {
1000
- /** Callback that dismisses the enclosing drawer/modal. */
1001
- close: () => void
1002
- /** Panel content that may call {@link usePanelClose}. */
1003
- children: ReactNode
1017
+ closeLabel?: string
1004
1018
  }
1005
1019
  ```
1006
1020
 
1007
- #### `ProgressProps`
1021
+ #### `TabItem`
1008
1022
 
1009
- Props for the Progress component.
1023
+ A single tab in a Tabs component.
1010
1024
 
1011
1025
  ```typescript
1012
- interface ProgressProps extends BaseProps {
1013
- /**
1014
- * Progress value (0-100).
1015
- */
1016
- value: number
1017
-
1018
- /**
1019
- * Maximum value.
1020
- */
1021
- max?: number
1022
-
1026
+ interface TabItem<T = string> {
1023
1027
  /**
1024
- * Progress size.
1028
+ * Tab value/id.
1025
1029
  */
1026
- size?: Size
1027
-
1030
+ value: T
1028
1031
  /**
1029
- * Progress color.
1032
+ * Tab label.
1030
1033
  */
1031
- color?: ColorVariant
1032
-
1034
+ label: Children
1033
1035
  /**
1034
- * Whether to show the value label.
1036
+ * Tab content.
1035
1037
  */
1036
- showValue?: boolean
1037
-
1038
+ content?: Children
1038
1039
  /**
1039
- * Accessible label.
1040
+ * Whether the tab is disabled.
1040
1041
  */
1041
- label?: string
1042
-
1042
+ disabled?: boolean
1043
1043
  /**
1044
- * Whether the progress is indeterminate.
1044
+ * Icon to display.
1045
1045
  */
1046
- indeterminate?: boolean
1046
+ icon?: Children
1047
1047
  }
1048
1048
  ```
1049
1049
 
1050
- #### `PromptDialogProps`
1050
+ #### `TabsProps`
1051
1051
 
1052
- Props for {@link PromptDialog}.
1052
+ Props for the Tabs component (switchable tabbed content panels).
1053
1053
 
1054
1054
  ```typescript
1055
- interface PromptDialogProps {
1056
- /** Whether the dialog is open. */
1057
- open: boolean
1058
- /** Called when the dialog should close (escape, backdrop, cancel, or a fulfilled {@link PromptDialogProps.onSubmit}). */
1059
- onClose: () => void
1060
- /** Dialog heading. */
1061
- title: React.ReactNode
1062
- /** Explanatory line under the title. */
1063
- description?: React.ReactNode
1064
- /** Input placeholder. */
1065
- placeholder?: string
1066
- /** Value the input starts from every time the dialog opens. */
1067
- initialValue?: string
1055
+ interface TabsProps<T = string> extends BaseProps {
1068
1056
  /**
1069
- * Called with the entered value on confirm. Resolve to close the dialog;
1070
- * reject or throw to keep it open (show the error via {@link PromptDialogProps.children}).
1057
+ * Tab items.
1071
1058
  */
1072
- onSubmit: (value: string) => void | Promise<void>
1073
- /** Confirm-button label. Falls back to a localized "Confirm". */
1074
- confirmLabel?: React.ReactNode
1075
- /** Cancel-button label. Falls back to a localized "Cancel". */
1076
- cancelLabel?: React.ReactNode
1077
- /** Render the confirm button in the error color (e.g. an irreversible reset). */
1078
- destructive?: boolean
1079
- /** `data-mol-id` for the text input. */
1080
- inputMolId?: string
1081
- /** `data-mol-id` for the confirm button. */
1082
- confirmMolId?: string
1083
- /** `data-mol-id` for the cancel button. */
1084
- cancelMolId?: string
1085
- /** Extra body content under the input (error banners, hints). */
1086
- children?: React.ReactNode
1087
- }
1088
- ```
1089
-
1090
- #### `RadioGroupProps`
1091
-
1092
- Props for the RadioGroup component.
1093
-
1094
- ```typescript
1095
- interface RadioGroupProps<T = string> extends BaseProps {
1059
+ items: TabItem<T>[]
1096
1060
  /**
1097
- * Radio options.
1061
+ * Current active tab.
1098
1062
  */
1099
- options: RadioOption<T>[]
1063
+ value?: T
1100
1064
  /**
1101
- * Current value.
1065
+ * Default active tab.
1102
1066
  */
1103
- value?: T
1067
+ defaultValue?: T
1104
1068
  /**
1105
1069
  * Change handler.
1106
1070
  */
1107
1071
  onChange?: (value: T) => void
1108
1072
  /**
1109
- * Radio size.
1110
- */
1111
- size?: Size
1112
- /**
1113
- * Group label.
1114
- */
1115
- label?: string
1116
- /**
1117
- * Shared `name` attribute for the group's radio inputs (used for native
1118
- * form submission). When omitted, a unique per-instance name is generated
1119
- * so separate groups never merge — the visible `label` is deliberately
1120
- * NOT used as the name, because two groups with the same label (e.g. two
1121
- * "Size" pickers) would otherwise form ONE native radio group and
1122
- * deselect each other.
1073
+ * Tab variant.
1123
1074
  */
1124
- name?: string
1075
+ variant?: 'line' | 'enclosed' | 'soft-rounded' | 'solid-rounded'
1125
1076
  /**
1126
- * Layout direction.
1077
+ * Tab size.
1127
1078
  */
1128
- direction?: 'horizontal' | 'vertical'
1079
+ size?: Size
1129
1080
  /**
1130
- * Error message.
1081
+ * Whether tabs are fitted (take full width).
1131
1082
  */
1132
- error?: string
1083
+ fitted?: boolean
1133
1084
  }
1134
1085
  ```
1135
1086
 
1136
- #### `RadioOption`
1087
+ #### `AccordionItem`
1137
1088
 
1138
- A single option in a RadioGroup.
1089
+ A single item in an Accordion component.
1139
1090
 
1140
1091
  ```typescript
1141
- interface RadioOption<T = string> {
1092
+ interface AccordionItem<T = string> {
1142
1093
  /**
1143
- * Option value.
1094
+ * Item value/id.
1144
1095
  */
1145
1096
  value: T
1146
1097
  /**
1147
- * Display label.
1098
+ * Item header/trigger.
1148
1099
  */
1149
- label: Children
1100
+ header: Children
1150
1101
  /**
1151
- * Whether the option is disabled.
1102
+ * Item content.
1103
+ */
1104
+ content: Children
1105
+ /**
1106
+ * Whether the item is disabled.
1152
1107
  */
1153
1108
  disabled?: boolean
1154
1109
  }
1155
1110
  ```
1156
1111
 
1157
- #### `ResponsiveAppShellContentProps`
1112
+ #### `AccordionProps`
1158
1113
 
1159
- Props for the {@link ResponsiveAppShellContent} sub-component.
1160
-
1161
- ```typescript
1162
- interface ResponsiveAppShellContentProps {
1163
- /** The routed page content — typically an `<Outlet />`. */
1164
- children: ReactNode
1165
- /** Extra classes on the `<main>`. */
1166
- className?: string
1167
- /** Inline styles on the `<main>` (rarely needed — layout is owned). */
1168
- style?: React.CSSProperties
1169
- /** `data-mol-id` override. Default: `'shell-content'`. */
1170
- dataMolId?: string
1171
- /** `data-testid` override. */
1172
- testId?: string
1173
- }
1174
- ```
1175
-
1176
- #### `ResponsiveAppShellDrawerProps`
1177
-
1178
- Props for the {@link ResponsiveAppShellDrawer} sub-component.
1114
+ Props for the Accordion component.
1179
1115
 
1180
1116
  ```typescript
1181
- interface ResponsiveAppShellDrawerProps {
1117
+ interface AccordionProps<T = string> extends BaseProps {
1182
1118
  /**
1183
- * Nav content — the links, same as (or adapted from) the Sidebar's. Only
1184
- * mounted while the drawer is open on a below-breakpoint viewport.
1119
+ * Accordion items.
1185
1120
  */
1186
- children: ReactNode
1121
+ items: AccordionItem<T>[]
1187
1122
  /**
1188
- * Header row content (typically the brand), rendered next to the close
1189
- * button.
1123
+ * Expanded item(s).
1190
1124
  */
1191
- header?: ReactNode
1125
+ value?: T | T[]
1192
1126
  /**
1193
- * Accessible name for the dialog and its `<nav>` landmark. Defaults to the
1194
- * `appShell.primaryNav` translation (English fallback `"Primary
1195
- * navigation"`).
1127
+ * Default expanded item(s).
1196
1128
  */
1197
- navLabel?: string
1198
- /** Below-nav content (user card, sign-out), after the `<nav>`. */
1199
- footer?: ReactNode
1200
- /** Whether the panel shows a close (X) button. Default: `true`. */
1201
- showCloseButton?: boolean
1202
- /** Extra classes on the dialog panel. */
1203
- className?: string
1204
- /** `data-mol-id` override for the panel. Default: `'shell-drawer'`. */
1205
- dataMolId?: string
1206
- /** `data-mol-id` override for the close button. Default: `'shell-drawer-close'`. */
1207
- closeDataMolId?: string
1208
- /** `data-testid` override for the panel. */
1209
- testId?: string
1210
- }
1211
- ```
1212
-
1213
- #### `ResponsiveAppShellProps`
1214
-
1215
- Props for the {@link ResponsiveAppShell} root.
1216
-
1217
- ```typescript
1218
- interface ResponsiveAppShellProps {
1129
+ defaultValue?: T | T[]
1219
1130
  /**
1220
- * The shell sub-components — `<ResponsiveAppShell.TopBar />`,
1221
- * `<ResponsiveAppShell.Sidebar />`, `<ResponsiveAppShell.Drawer />` and
1222
- * `<ResponsiveAppShell.Content />`.
1131
+ * Change handler.
1223
1132
  */
1224
- children: ReactNode
1133
+ onChange?: (value: T | T[]) => void
1225
1134
  /**
1226
- * Sidebar (and drawer) width: `'sm'` (208px), `'md'` (240px, default),
1227
- * `'lg'` (256px) or an exact pixel number.
1135
+ * Whether multiple items can be expanded.
1228
1136
  */
1229
- sidebarWidth?: ShellSidebarWidth
1230
- /** Extra classes on the root flex-row container. */
1231
- className?: string
1137
+ multiple?: boolean
1232
1138
  /**
1233
- * `data-mol-id` for the root container. Defaults to `'shell-root'`.
1139
+ * Whether items can be collapsed.
1234
1140
  */
1235
- dataMolId?: string
1141
+ collapsible?: boolean
1236
1142
  }
1237
1143
  ```
1238
1144
 
1239
- #### `ResponsiveAppShellSidebarProps`
1145
+ #### `TableColumn`
1240
1146
 
1241
- Props for the {@link ResponsiveAppShellSidebar} sub-component.
1147
+ Table column definition.
1242
1148
 
1243
1149
  ```typescript
1244
- interface ResponsiveAppShellSidebarProps {
1150
+ interface TableColumn<T> {
1245
1151
  /**
1246
- * Nav content — the links (they land inside the sidebar's `<nav>` landmark;
1247
- * pass `aria-current="page"` from the active one yourself). Typically the
1248
- * SAME element variable the Drawer receives, so only the mounted
1249
- * breakpoint's copy ever instantiates.
1152
+ * Column key (data property).
1250
1153
  */
1251
- children: ReactNode
1154
+ key: keyof T | string
1252
1155
  /**
1253
- * Accessible name for the sidebar's `<nav>` landmark. Defaults to the
1254
- * `appShell.primaryNav` translation (English fallback `"Primary
1255
- * navigation"`).
1156
+ * Column header.
1256
1157
  */
1257
- navLabel?: string
1158
+ header: Children
1258
1159
  /**
1259
- * Below-nav content (user card, quick action) — rendered after the `<nav>`
1260
- * in a bordered footer row, so it does not sit inside the nav landmark.
1160
+ * Custom cell renderer.
1261
1161
  */
1262
- footer?: ReactNode
1263
- /** Extra classes on the `<aside>`. */
1264
- className?: string
1265
- /** `data-mol-id` override. Default: `'shell-sidebar'`. */
1266
- dataMolId?: string
1267
- /** `data-testid` override. */
1268
- testId?: string
1269
- }
1270
- ```
1271
-
1272
- #### `ResponsiveAppShellTopBarProps`
1273
-
1274
- Props for the {@link ResponsiveAppShellTopBar} sub-component.
1275
-
1276
- ```typescript
1277
- interface ResponsiveAppShellTopBarProps {
1162
+ render?: (value: unknown, row: T, index: number) => Children
1278
1163
  /**
1279
- * Brand/title node, rendered after the menu trigger — typically a
1280
- * `<Link to="/…">` with the app name or logo. The shell wraps it in a
1281
- * flex row; style the node itself.
1164
+ * Column width.
1282
1165
  */
1283
- brand?: ReactNode
1284
- /** Trailing actions — user menu, theme toggle, search, etc. */
1285
- actions?: ReactNode
1166
+ width?: string | number
1286
1167
  /**
1287
- * Render the top bar only below the breakpoint (mount-conditioned, not
1288
- * CSS-hidden — no dead DOM on desktop). For apps whose desktop shell has
1289
- * no top bar (e.g. the sidebar carries everything). Default: `false`
1290
- * (top bar on every breakpoint).
1168
+ * Whether the column is sortable.
1291
1169
  */
1292
- mobileOnly?: boolean
1293
- /** Extra classes on the `<header>`. */
1294
- className?: string
1295
- /** `data-mol-id` override. Default: `'shell-topbar'`. */
1296
- dataMolId?: string
1297
- /** `data-testid` override. */
1298
- testId?: string
1299
- }
1300
- ```
1301
-
1302
- #### `SelectElementProps`
1303
-
1304
- Base props for select elements.
1305
-
1306
- ```typescript
1307
- interface SelectElementProps extends HTMLElementProps {
1308
- name?: string
1309
- required?: boolean
1310
- autoFocus?: boolean
1311
- multiple?: boolean
1312
- onChange?: ChangeEventHandler
1170
+ sortable?: boolean
1171
+ /**
1172
+ * Text alignment.
1173
+ */
1174
+ align?: 'left' | 'center' | 'right'
1313
1175
  }
1314
1176
  ```
1315
1177
 
1316
- #### `SelectOption`
1178
+ #### `TableProps`
1317
1179
 
1318
- A single option in a Select dropdown.
1180
+ Props for the Table component.
1319
1181
 
1320
1182
  ```typescript
1321
- interface SelectOption<T = string> {
1322
- /**
1323
- * Option value.
1324
- */
1325
- value: T
1183
+ interface TableProps<T> extends HTMLElementProps {
1326
1184
  /**
1327
- * Display label.
1185
+ * Table data.
1328
1186
  */
1329
- label: string
1187
+ data: T[]
1330
1188
  /**
1331
- * Whether the option is disabled.
1189
+ * Column definitions.
1332
1190
  */
1333
- disabled?: boolean
1191
+ columns: TableColumn<T>[]
1334
1192
  /**
1335
- * Option group (for grouped selects).
1193
+ * Row key extractor.
1336
1194
  */
1337
- group?: string
1338
- }
1339
- ```
1340
-
1341
- #### `SelectProps`
1342
-
1343
- Props for the Select dropdown component (single or multi-select).
1344
-
1345
- ```typescript
1346
- interface SelectProps<T = string> extends SelectElementProps {
1195
+ rowKey?: keyof T | ((row: T) => string | number)
1347
1196
  /**
1348
- * Select options.
1197
+ * Whether to show borders.
1349
1198
  */
1350
- options: SelectOption<T>[]
1199
+ bordered?: boolean
1351
1200
  /**
1352
- * Current value.
1201
+ * Whether rows are striped.
1353
1202
  */
1354
- value?: T
1203
+ striped?: boolean
1355
1204
  /**
1356
- * Change handler (with typed value).
1205
+ * Whether rows are hoverable.
1357
1206
  */
1358
- onValueChange?: (value: T) => void
1207
+ hoverable?: boolean
1359
1208
  /**
1360
- * Select size.
1209
+ * Table size.
1361
1210
  */
1362
1211
  size?: Size
1363
1212
  /**
1364
- * Label text.
1213
+ * Empty state content.
1365
1214
  */
1366
- label?: string
1215
+ emptyContent?: Children
1367
1216
  /**
1368
- * Placeholder text.
1217
+ * Loading state.
1369
1218
  */
1370
- placeholder?: string
1219
+ loading?: boolean
1371
1220
  /**
1372
- * Error message.
1221
+ * Sort configuration.
1373
1222
  */
1374
- error?: string
1223
+ sort?: {
1224
+ key: string
1225
+ direction: 'asc' | 'desc'
1226
+ }
1375
1227
  /**
1376
- * Hint/help text.
1228
+ * Sort change handler.
1377
1229
  */
1378
- hint?: string
1230
+ onSort?: (key: string, direction: 'asc' | 'desc') => void
1379
1231
  /**
1380
- * Whether to allow clearing the selection.
1232
+ * Row click handler.
1381
1233
  */
1382
- clearable?: boolean
1234
+ onRowClick?: (row: T, index: number) => void
1383
1235
  }
1384
1236
  ```
1385
1237
 
1386
- #### `SeparatorProps`
1238
+ #### `PaginationProps`
1387
1239
 
1388
- Props for the Separator component.
1240
+ Props for the Pagination component (page navigation with current page, total, page size, and change callbacks).
1389
1241
 
1390
1242
  ```typescript
1391
- interface SeparatorProps extends BaseProps {
1243
+ interface PaginationProps extends BaseProps {
1392
1244
  /**
1393
- * Orientation of the separator.
1245
+ * Current page (1-indexed).
1394
1246
  */
1395
- orientation?: 'horizontal' | 'vertical'
1396
-
1247
+ page: number
1397
1248
  /**
1398
- * Decorative separators are purely visual.
1249
+ * Total number of pages.
1399
1250
  */
1400
- decorative?: boolean
1401
- }
1402
- ```
1403
-
1404
- #### `SidebarUserCardProps`
1405
-
1406
- Props for the {@link SidebarUserCard} component.
1407
-
1408
- Extends standard `<button>` attributes so callers can pass extra
1409
- `data-*`, `aria-*`, `style`, or event handler props onto the trigger
1410
- button without forking. The named props below cover the common
1411
- customization points.
1412
-
1413
- ```typescript
1414
- interface SidebarUserCardProps extends Omit<
1415
- ButtonHTMLAttributes<HTMLButtonElement>,
1416
- 'aria-label' | 'onClick' | 'children'
1417
- > {
1251
+ totalPages: number
1418
1252
  /**
1419
- * Panel content shown inside the drawer/modal (typically the app's
1420
- * `SettingsPanel`). It can dismiss the drawer by calling
1421
- * `usePanelClose()` — no `onClose` prop-threading required.
1253
+ * Page change handler.
1422
1254
  */
1423
- children: ReactNode
1255
+ onChange: (page: number) => void
1424
1256
  /**
1425
- * Display name override. When omitted, falls back to
1426
- * `useAuth().user?.name`, then `email`, then a guest label.
1257
+ * Number of sibling pages to show.
1427
1258
  */
1428
- name?: string
1259
+ siblings?: number
1429
1260
  /**
1430
- * Secondary line under the name (role, plan, status, etc.).
1431
- * Apps typically pass something like `t('sidebar.memberStatus', {}, { defaultValue: 'Premium Member' })`.
1432
- * When omitted and the auth user has an email, the email is shown instead.
1261
+ * Number of boundary pages to show.
1433
1262
  */
1434
- secondaryLine?: string
1435
- /** Optional avatar image URL — overrides `useAuth().user?.avatarUrl`. */
1436
- avatarUrl?: string
1437
- /** i18n key for the trigger button's aria-label. Default: `'sidebarUserCard.open'`. */
1438
- ariaLabelKey?: string
1439
- /** Fallback aria-label if the i18n key is missing. Default: `'Open account menu'`. */
1440
- ariaLabelDefault?: string
1263
+ boundaries?: number
1441
1264
  /**
1442
- * `data-mol-id` for the trigger button. Defaults to
1443
- * `'sidebar-user-card'`. Pass a different value (e.g. `'user-menu'`)
1444
- * to disambiguate or align with an app's existing e2e selectors.
1265
+ * Pagination size.
1445
1266
  */
1446
- dataMolId?: string
1267
+ size?: Size
1268
+ /**
1269
+ * Whether to show first/last buttons.
1270
+ */
1271
+ showFirstLast?: boolean
1272
+ /**
1273
+ * Whether to show previous/next buttons.
1274
+ */
1275
+ showPrevNext?: boolean
1276
+ /**
1277
+ * Accessible labels for pagination controls.
1278
+ */
1279
+ labels?: {
1280
+ nav?: string
1281
+ first?: string
1282
+ previous?: string
1283
+ next?: string
1284
+ last?: string
1285
+ goToPage?: (page: number) => string
1286
+ }
1447
1287
  }
1448
1288
  ```
1449
1289
 
@@ -1476,290 +1316,530 @@ interface SkeletonProps extends BaseProps {
1476
1316
  }
1477
1317
  ```
1478
1318
 
1479
- #### `SpacerProps`
1319
+ #### `AuthGuardProps`
1480
1320
 
1481
- Props for the Spacer layout component (adds whitespace between elements).
1321
+ Props for {@link AuthGuard}.
1322
+
1323
+ All props are optional — the default behavior (loading tag → redirect
1324
+ to `/login` → `<Outlet />`) matches the bare-bones guard apps were
1325
+ shipping locally. The props let apps customize per-call without
1326
+ forking the component.
1482
1327
 
1483
1328
  ```typescript
1484
- interface SpacerProps extends BaseProps {
1329
+ interface AuthGuardProps {
1485
1330
  /**
1486
- * Space size.
1331
+ * Element rendered while `useAuth().state.initialized` is `false`.
1332
+ * Overrides the default `<div data-mol-id="auth-guard-loading">…</div>`
1333
+ * loading tag — pass a full-page spinner or any other UI you want
1334
+ * during the auth bootstrap window.
1487
1335
  */
1488
- size?: Size | string | number
1336
+ loadingFallback?: ReactNode
1489
1337
  /**
1490
- * Whether the spacer is horizontal.
1338
+ * i18n key used for the default loading-tag text. Defaults to
1339
+ * `'common.loading'`. Ignored when `loadingFallback` is set.
1491
1340
  */
1492
- horizontal?: boolean
1493
- }
1494
- ```
1495
-
1496
- #### `SpinnerProps`
1497
-
1498
- Props for the Spinner/loading indicator component.
1499
-
1500
- ```typescript
1501
- interface SpinnerProps extends BaseProps {
1341
+ loadingKey?: string
1502
1342
  /**
1503
- * Spinner size.
1343
+ * Fallback text if the i18n key is missing. Defaults to `'Loading...'`
1344
+ * (the project-canonical ASCII glyph — Phase C of the locale
1345
+ * canonicalization plan). Ignored when `loadingFallback` is set.
1504
1346
  */
1505
- size?: Size
1347
+ loadingDefault?: string
1506
1348
  /**
1507
- * Spinner color.
1349
+ * Path to redirect to when the user is not authenticated. Defaults to
1350
+ * `'/login'`. The current `useLocation()` is preserved as `state.from`
1351
+ * for post-login restoration.
1508
1352
  */
1509
- color?: ColorVariant | string
1353
+ loginPath?: string
1510
1354
  /**
1511
- * Loading label (for accessibility).
1355
+ * Callback invoked when `isAuthenticated` transitions to `true` (or is
1356
+ * already `true` on first render). Useful for one-shot per-app
1357
+ * bootstrap effects (e.g. seeding fixture data). The caller is
1358
+ * responsible for idempotency if the auth state can flip back and
1359
+ * forth — this fires on every transition.
1512
1360
  */
1513
- label?: string
1361
+ onAuthenticated?: () => void
1514
1362
  /**
1515
- * Spinner thickness.
1363
+ * Children to render when authenticated. Defaults to `<Outlet />`,
1364
+ * which is the React Router pattern this guard is designed for.
1516
1365
  */
1517
- thickness?: number
1366
+ children?: ReactNode
1518
1367
  }
1519
1368
  ```
1520
1369
 
1521
- #### `SwitchProps`
1370
+ #### `ConfirmButtonProps`
1522
1371
 
1523
- Props for the Switch/Toggle component.
1372
+ Props for {@link ConfirmButton}.
1524
1373
 
1525
1374
  ```typescript
1526
- interface SwitchProps extends InputElementProps {
1527
- /**
1528
- * Switch label.
1529
- */
1530
- label?: Children
1531
- /**
1532
- * Whether the switch is on.
1533
- */
1534
- checked?: boolean
1375
+ interface ConfirmButtonProps extends Omit<
1376
+ ButtonProps,
1377
+ 'onClick' | 'children' | 'onConfirm' | keyof React.DOMAttributes<HTMLButtonElement>
1378
+ > {
1535
1379
  /**
1536
- * Switch size.
1380
+ * Fired ONLY on the confirming (second) click — i.e. when the action
1381
+ * actually commits. Errors should be handled by the caller (render an
1382
+ * inline banner); while the returned promise is pending the button stays
1383
+ * in its confirming state and ignores further clicks.
1537
1384
  */
1538
- size?: Size
1385
+ onConfirm: () => void | Promise<void>
1539
1386
  /**
1540
- * Color when on.
1387
+ * Visible label in the idle (armed-to-be-clicked) state.
1541
1388
  */
1542
- color?: ColorVariant
1543
- }
1544
- ```
1545
-
1546
- #### `TabItem`
1547
-
1548
- A single tab in a Tabs component.
1549
-
1550
- ```typescript
1551
- interface TabItem<T = string> {
1389
+ label: React.ReactNode
1552
1390
  /**
1553
- * Tab value/id.
1391
+ * Visible label in the armed state — the "click again to confirm" step.
1392
+ * Defaults to a localized "Confirm?" via the i18n bond.
1554
1393
  */
1555
- value: T
1394
+ confirmLabel?: React.ReactNode
1556
1395
  /**
1557
- * Tab label.
1396
+ * Visible label while {@link onConfirm} is pending. Defaults to a
1397
+ * localized "Working…".
1558
1398
  */
1559
- label: Children
1399
+ pendingLabel?: React.ReactNode
1560
1400
  /**
1561
- * Tab content.
1401
+ * Seconds before the armed state auto-disarms back to idle. Low-stakes
1402
+ * actions tolerate a long window; destructive ones should keep it short.
1403
+ * `0` disables auto-disarm (not recommended — a stray click then leaves
1404
+ * the button armed forever).
1562
1405
  */
1563
- content?: Children
1406
+ disarmSeconds?: number
1564
1407
  /**
1565
- * Whether the tab is disabled.
1408
+ * Disable the control entirely (idle and confirming states).
1566
1409
  */
1567
1410
  disabled?: boolean
1568
1411
  /**
1569
- * Icon to display.
1412
+ * data-mol-id for the button (all states share one element).
1570
1413
  */
1571
- icon?: Children
1414
+ testId?: string
1572
1415
  }
1573
1416
  ```
1574
1417
 
1575
- #### `TableColumn`
1418
+ #### `ConfirmDialogProps`
1576
1419
 
1577
- Table column definition.
1420
+ Props for {@link ConfirmDialog}.
1578
1421
 
1579
1422
  ```typescript
1580
- interface TableColumn<T> {
1581
- /**
1582
- * Column key (data property).
1583
- */
1584
- key: keyof T | string
1585
- /**
1586
- * Column header.
1587
- */
1588
- header: Children
1589
- /**
1590
- * Custom cell renderer.
1591
- */
1592
- render?: (value: unknown, row: T, index: number) => Children
1593
- /**
1594
- * Column width.
1595
- */
1596
- width?: string | number
1597
- /**
1598
- * Whether the column is sortable.
1599
- */
1600
- sortable?: boolean
1601
- /**
1602
- * Text alignment.
1603
- */
1604
- align?: 'left' | 'center' | 'right'
1423
+ interface ConfirmDialogProps {
1424
+ /** Whether the dialog is open. */
1425
+ open: boolean
1426
+ /** Called when the dialog should close (escape, backdrop, cancel). */
1427
+ onClose: () => void
1428
+ /** Title. */
1429
+ title: ReactNode
1430
+ /** Body / warning text. */
1431
+ description: ReactNode
1432
+ /** Confirm-button label (e.g. "Delete"). */
1433
+ confirmLabel?: ReactNode
1434
+ /** Cancel-button label. */
1435
+ cancelLabel?: ReactNode
1436
+ /** Called when the user confirms. */
1437
+ onConfirm: () => void | Promise<void>
1438
+ /** Is the confirm action destructive? Defaults to true. */
1439
+ destructive?: boolean
1440
+ /** Extra body content between description and footer. */
1441
+ children?: ReactNode
1442
+ /** Disables both buttons. Set it yourself while your `onConfirm` promise is pending — the dialog does not track it and does not auto-close. */
1443
+ loading?: boolean
1605
1444
  }
1606
1445
  ```
1607
1446
 
1608
- #### `TableProps`
1447
+ #### `IconProps`
1609
1448
 
1610
- Props for the Table component.
1449
+ Props for {@link Icon}.
1450
+
1451
+ Extends `SVGProps<SVGSVGElement>` so callers can pass any SVG / HTML
1452
+ attribute the underlying `<svg>` accepts, including `data-mol-id`,
1453
+ `aria-*`, `role`, event handlers, `style`, etc. — without the
1454
+ component needing to enumerate them.
1611
1455
 
1612
1456
  ```typescript
1613
- interface TableProps<T> extends HTMLElementProps {
1614
- /**
1615
- * Table data.
1616
- */
1617
- data: T[]
1618
- /**
1619
- * Column definitions.
1620
- */
1621
- columns: TableColumn<T>[]
1622
- /**
1623
- * Row key extractor.
1624
- */
1625
- rowKey?: keyof T | ((row: T) => string | number)
1626
- /**
1627
- * Whether to show borders.
1628
- */
1629
- bordered?: boolean
1630
- /**
1631
- * Whether rows are striped.
1632
- */
1633
- striped?: boolean
1634
- /**
1635
- * Whether rows are hoverable.
1636
- */
1637
- hoverable?: boolean
1457
+ interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'width' | 'height' | 'viewBox' | 'fill'> {
1638
1458
  /**
1639
- * Table size.
1459
+ * Name of the glyph to look up in the bonded icon set. Typed as
1460
+ * {@link IconName} so a typo fails the type-check instead of throwing at
1461
+ * render time; sets with extra glyphs augment `CustomIconNames` in
1462
+ * `@molecule/app-icons` to widen it.
1640
1463
  */
1641
- size?: Size
1642
- /**
1643
- * Empty state content.
1464
+ name: IconName
1465
+ /** Width and height of the rendered SVG in pixels. Defaults to 20. */
1466
+ size?: number
1467
+ /** Class name forwarded to the root `<svg>`. */
1468
+ className?: string
1469
+ }
1470
+ ```
1471
+
1472
+ #### `LanguagePickerProps`
1473
+
1474
+ Props for {@link LanguagePicker}.
1475
+
1476
+ Extends standard `<button>` attributes so callers can pass any extra
1477
+ `data-*`, `aria-*`, event handler, or `style` prop without forking the
1478
+ component. The named props let apps customize the trigger label, icon,
1479
+ size, and modal heading without spreading attribute concerns.
1480
+
1481
+ ```typescript
1482
+ interface LanguagePickerProps extends Omit<
1483
+ ButtonHTMLAttributes<HTMLButtonElement>,
1484
+ 'aria-label' | 'onClick' | 'type' | 'children'
1485
+ > {
1486
+ /** i18n key for the trigger button label / aria-label. Defaults to `'footer.language'`. */
1487
+ labelKey?: string
1488
+ /** Fallback label if the i18n key is missing. Defaults to `'Language'`. */
1489
+ labelDefault?: string
1490
+ /** i18n key for the modal title. Defaults to `'languagePicker.modalTitle'`. */
1491
+ modalTitleKey?: string
1492
+ /** Fallback modal title if the i18n key is missing. Defaults to `'Choose language'`. */
1493
+ modalTitleDefault?: string
1494
+ /** Icon glyph to render before the current locale name. Defaults to `'globe'`. */
1495
+ icon?: IconName
1496
+ /** Pixel size for the rendered icon. Defaults to `16`. */
1497
+ iconSize?: number
1498
+ /**
1499
+ * What to render inside the trigger button.
1500
+ *
1501
+ * - `'name'` — globe icon + current locale's native name (e.g. `🌐 English`).
1502
+ * This is the default; matches the molecule-dev footer pattern.
1503
+ * - `'code'` — globe icon + lowercase locale code (e.g. `🌐 en`). Useful in
1504
+ * tight chrome where the long native name (e.g. "Bahasa Indonesia") would wrap.
1505
+ * - `'icon'` — globe icon only. Useful in icon-bar headers.
1644
1506
  */
1645
- emptyContent?: Children
1507
+ display?: 'name' | 'code' | 'icon'
1508
+ /** Optional className appended to the trigger button. */
1509
+ className?: string
1510
+ /** Optional render override: receives `{ open }` and replaces the default trigger. */
1511
+ renderTrigger?: (open: () => void) => ReactNode
1512
+ }
1513
+ ```
1514
+
1515
+ #### `LoadErrorBannerProps`
1516
+
1517
+ Props for {@link LoadErrorBanner}. Rest props are forwarded to the root
1518
+ `<div>`, so they are typed as its attributes.
1519
+
1520
+ ```typescript
1521
+ interface LoadErrorBannerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
1522
+ /** The primary error line (e.g. the localized "Could not load X."). */
1523
+ message: React.ReactNode
1524
+ /** Secondary line under the message (status code, hint). */
1525
+ detail?: React.ReactNode
1646
1526
  /**
1647
- * Loading state.
1527
+ * Called when Retry is clicked. Return the promise; while pending, the
1528
+ * button is disabled and shows "Retrying…". Omit for a display-only
1529
+ * banner.
1648
1530
  */
1649
- loading?: boolean
1531
+ onRetry?: () => void | Promise<void>
1532
+ /** Retry-button label. Falls back to a localized "Retry". */
1533
+ retryLabel?: React.ReactNode
1534
+ /** `data-mol-id` for the retry button (default `load-error-retry`). */
1535
+ retryMolId?: string
1536
+ /** Extra content between message and retry row (e.g. an error code). */
1537
+ children?: React.ReactNode
1538
+ }
1539
+ ```
1540
+
1541
+ #### `PanelCloseProviderProps`
1542
+
1543
+ Props for {@link PanelCloseProvider}.
1544
+
1545
+ ```typescript
1546
+ interface PanelCloseProviderProps {
1547
+ /** Callback that dismisses the enclosing drawer/modal. */
1548
+ close: () => void
1549
+ /** Panel content that may call {@link usePanelClose}. */
1550
+ children: ReactNode
1551
+ }
1552
+ ```
1553
+
1554
+ #### `ProgressProps`
1555
+
1556
+ Props for the Progress component.
1557
+
1558
+ ```typescript
1559
+ interface ProgressProps extends BaseProps {
1650
1560
  /**
1651
- * Sort configuration.
1561
+ * Progress value (0-100).
1652
1562
  */
1653
- sort?: {
1654
- key: string
1655
- direction: 'asc' | 'desc'
1656
- }
1563
+ value: number
1564
+
1657
1565
  /**
1658
- * Sort change handler.
1566
+ * Maximum value.
1659
1567
  */
1660
- onSort?: (key: string, direction: 'asc' | 'desc') => void
1568
+ max?: number
1569
+
1661
1570
  /**
1662
- * Row click handler.
1571
+ * Progress size.
1663
1572
  */
1664
- onRowClick?: (row: T, index: number) => void
1573
+ size?: Size
1574
+
1575
+ /**
1576
+ * Progress color.
1577
+ */
1578
+ color?: ColorVariant
1579
+
1580
+ /**
1581
+ * Whether to show the value label.
1582
+ */
1583
+ showValue?: boolean
1584
+
1585
+ /**
1586
+ * Accessible label.
1587
+ */
1588
+ label?: string
1589
+
1590
+ /**
1591
+ * Whether the progress is indeterminate.
1592
+ */
1593
+ indeterminate?: boolean
1665
1594
  }
1666
1595
  ```
1667
1596
 
1668
- #### `TabsProps`
1597
+ #### `PromptDialogProps`
1669
1598
 
1670
- Props for the Tabs component (switchable tabbed content panels).
1599
+ Props for {@link PromptDialog}.
1671
1600
 
1672
1601
  ```typescript
1673
- interface TabsProps<T = string> extends BaseProps {
1602
+ interface PromptDialogProps {
1603
+ /** Whether the dialog is open. */
1604
+ open: boolean
1605
+ /** Called when the dialog should close (escape, backdrop, cancel, or a fulfilled {@link PromptDialogProps.onSubmit}). */
1606
+ onClose: () => void
1607
+ /** Dialog heading. */
1608
+ title: React.ReactNode
1609
+ /** Explanatory line under the title. */
1610
+ description?: React.ReactNode
1611
+ /** Input placeholder. */
1612
+ placeholder?: string
1613
+ /** Value the input starts from every time the dialog opens. */
1614
+ initialValue?: string
1674
1615
  /**
1675
- * Tab items.
1616
+ * Called with the entered value on confirm. Resolve to close the dialog;
1617
+ * reject or throw to keep it open (show the error via {@link PromptDialogProps.children}).
1676
1618
  */
1677
- items: TabItem<T>[]
1619
+ onSubmit: (value: string) => void | Promise<void>
1620
+ /** Confirm-button label. Falls back to a localized "Confirm". */
1621
+ confirmLabel?: React.ReactNode
1622
+ /** Cancel-button label. Falls back to a localized "Cancel". */
1623
+ cancelLabel?: React.ReactNode
1624
+ /** Render the confirm button in the error color (e.g. an irreversible reset). */
1625
+ destructive?: boolean
1626
+ /** `data-mol-id` for the text input. */
1627
+ inputMolId?: string
1628
+ /** `data-mol-id` for the confirm button. */
1629
+ confirmMolId?: string
1630
+ /** `data-mol-id` for the cancel button. */
1631
+ cancelMolId?: string
1632
+ /** Extra body content under the input (error banners, hints). */
1633
+ children?: React.ReactNode
1634
+ }
1635
+ ```
1636
+
1637
+ #### `ResponsiveAppShellProps`
1638
+
1639
+ Props for the {@link ResponsiveAppShell} root.
1640
+
1641
+ ```typescript
1642
+ interface ResponsiveAppShellProps {
1678
1643
  /**
1679
- * Current active tab.
1644
+ * The shell sub-components — `<ResponsiveAppShell.TopBar />`,
1645
+ * `<ResponsiveAppShell.Sidebar />`, `<ResponsiveAppShell.Drawer />` and
1646
+ * `<ResponsiveAppShell.Content />`.
1680
1647
  */
1681
- value?: T
1648
+ children: ReactNode
1682
1649
  /**
1683
- * Default active tab.
1650
+ * Sidebar (and drawer) width: `'sm'` (208px), `'md'` (240px, default),
1651
+ * `'lg'` (256px) or an exact pixel number.
1684
1652
  */
1685
- defaultValue?: T
1653
+ sidebarWidth?: ShellSidebarWidth
1654
+ /** Extra classes on the root flex-row container. */
1655
+ className?: string
1686
1656
  /**
1687
- * Change handler.
1657
+ * `data-mol-id` for the root container. Defaults to `'shell-root'`.
1688
1658
  */
1689
- onChange?: (value: T) => void
1659
+ dataMolId?: string
1660
+ }
1661
+ ```
1662
+
1663
+ #### `ResponsiveAppShellTopBarProps`
1664
+
1665
+ Props for the {@link ResponsiveAppShellTopBar} sub-component.
1666
+
1667
+ ```typescript
1668
+ interface ResponsiveAppShellTopBarProps {
1690
1669
  /**
1691
- * Tab variant.
1670
+ * Brand/title node, rendered after the menu trigger — typically a
1671
+ * `<Link to="/…">` with the app name or logo. The shell wraps it in a
1672
+ * flex row; style the node itself.
1692
1673
  */
1693
- variant?: 'line' | 'enclosed' | 'soft-rounded' | 'solid-rounded'
1674
+ brand?: ReactNode
1675
+ /** Trailing actions — user menu, theme toggle, search, etc. */
1676
+ actions?: ReactNode
1694
1677
  /**
1695
- * Tab size.
1678
+ * Render the top bar only below the breakpoint (mount-conditioned, not
1679
+ * CSS-hidden — no dead DOM on desktop). For apps whose desktop shell has
1680
+ * no top bar (e.g. the sidebar carries everything). Default: `false`
1681
+ * (top bar on every breakpoint).
1696
1682
  */
1697
- size?: Size
1683
+ mobileOnly?: boolean
1684
+ /** Extra classes on the `<header>`. */
1685
+ className?: string
1686
+ /** `data-mol-id` override. Default: `'shell-topbar'`. */
1687
+ dataMolId?: string
1688
+ /** `data-testid` override. */
1689
+ testId?: string
1690
+ }
1691
+ ```
1692
+
1693
+ #### `ResponsiveAppShellSidebarProps`
1694
+
1695
+ Props for the {@link ResponsiveAppShellSidebar} sub-component.
1696
+
1697
+ ```typescript
1698
+ interface ResponsiveAppShellSidebarProps {
1698
1699
  /**
1699
- * Whether tabs are fitted (take full width).
1700
+ * Nav content — the links (they land inside the sidebar's `<nav>` landmark;
1701
+ * pass `aria-current="page"` from the active one yourself). Typically the
1702
+ * SAME element variable the Drawer receives, so only the mounted
1703
+ * breakpoint's copy ever instantiates.
1700
1704
  */
1701
- fitted?: boolean
1705
+ children: ReactNode
1706
+ /**
1707
+ * Accessible name for the sidebar's `<nav>` landmark. Defaults to the
1708
+ * `appShell.primaryNav` translation (English fallback `"Primary
1709
+ * navigation"`).
1710
+ */
1711
+ navLabel?: string
1712
+ /**
1713
+ * Below-nav content (user card, quick action) — rendered after the `<nav>`
1714
+ * in a bordered footer row, so it does not sit inside the nav landmark.
1715
+ */
1716
+ footer?: ReactNode
1717
+ /** Extra classes on the `<aside>`. */
1718
+ className?: string
1719
+ /** `data-mol-id` override. Default: `'shell-sidebar'`. */
1720
+ dataMolId?: string
1721
+ /** `data-testid` override. */
1722
+ testId?: string
1702
1723
  }
1703
1724
  ```
1704
1725
 
1705
- #### `TextareaElementProps`
1726
+ #### `ResponsiveAppShellDrawerProps`
1706
1727
 
1707
- Base props for textarea elements.
1728
+ Props for the {@link ResponsiveAppShellDrawer} sub-component.
1708
1729
 
1709
1730
  ```typescript
1710
- interface TextareaElementProps extends HTMLElementProps {
1711
- name?: string
1712
- value?: string
1713
- defaultValue?: string
1714
- placeholder?: string
1715
- required?: boolean
1716
- readOnly?: boolean
1717
- autoFocus?: boolean
1718
- rows?: number
1719
- cols?: number
1720
- maxLength?: number
1721
- minLength?: number
1722
- wrap?: 'hard' | 'soft' | 'off'
1723
- onChange?: ChangeEventHandler
1724
- onInput?: FormEventHandler
1731
+ interface ResponsiveAppShellDrawerProps {
1732
+ /**
1733
+ * Nav content — the links, same as (or adapted from) the Sidebar's. Only
1734
+ * mounted while the drawer is open on a below-breakpoint viewport.
1735
+ */
1736
+ children: ReactNode
1737
+ /**
1738
+ * Header row content (typically the brand), rendered next to the close
1739
+ * button.
1740
+ */
1741
+ header?: ReactNode
1742
+ /**
1743
+ * Accessible name for the dialog and its `<nav>` landmark. Defaults to the
1744
+ * `appShell.primaryNav` translation (English fallback `"Primary
1745
+ * navigation"`).
1746
+ */
1747
+ navLabel?: string
1748
+ /** Below-nav content (user card, sign-out), after the `<nav>`. */
1749
+ footer?: ReactNode
1750
+ /** Whether the panel shows a close (X) button. Default: `true`. */
1751
+ showCloseButton?: boolean
1752
+ /** Extra classes on the dialog panel. */
1753
+ className?: string
1754
+ /** `data-mol-id` override for the panel. Default: `'shell-drawer'`. */
1755
+ dataMolId?: string
1756
+ /** `data-mol-id` override for the close button. Default: `'shell-drawer-close'`. */
1757
+ closeDataMolId?: string
1758
+ /** `data-testid` override for the panel. */
1759
+ testId?: string
1760
+ }
1761
+ ```
1762
+
1763
+ #### `ResponsiveAppShellContentProps`
1764
+
1765
+ Props for the {@link ResponsiveAppShellContent} sub-component.
1766
+
1767
+ ```typescript
1768
+ interface ResponsiveAppShellContentProps {
1769
+ /** The routed page content — typically an `<Outlet />`. */
1770
+ children: ReactNode
1771
+ /** Extra classes on the `<main>`. */
1772
+ className?: string
1773
+ /** Inline styles on the `<main>` (rarely needed — layout is owned). */
1774
+ style?: React.CSSProperties
1775
+ /** `data-mol-id` override. Default: `'shell-content'`. */
1776
+ dataMolId?: string
1777
+ /** `data-testid` override. */
1778
+ testId?: string
1725
1779
  }
1726
1780
  ```
1727
1781
 
1728
- #### `TextareaProps`
1782
+ #### `SeparatorProps`
1729
1783
 
1730
- Props for the Textarea component.
1784
+ Props for the Separator component.
1731
1785
 
1732
1786
  ```typescript
1733
- interface TextareaProps extends TextareaElementProps {
1734
- /**
1735
- * Text size tier (see {@link TextareaClassOptions.size}) — pass the same
1736
- * `size` as a neighboring Input for identical font sizes.
1737
- */
1738
- size?: Size
1787
+ interface SeparatorProps extends BaseProps {
1739
1788
  /**
1740
- * Label text.
1789
+ * Orientation of the separator.
1741
1790
  */
1742
- label?: string
1791
+ orientation?: 'horizontal' | 'vertical'
1792
+
1743
1793
  /**
1744
- * Error message.
1794
+ * Decorative separators are purely visual.
1745
1795
  */
1746
- error?: string
1796
+ decorative?: boolean
1797
+ }
1798
+ ```
1799
+
1800
+ #### `SidebarUserCardProps`
1801
+
1802
+ Props for the {@link SidebarUserCard} component.
1803
+
1804
+ Extends standard `<button>` attributes so callers can pass extra
1805
+ `data-*`, `aria-*`, `style`, or event handler props onto the trigger
1806
+ button without forking. The named props below cover the common
1807
+ customization points.
1808
+
1809
+ ```typescript
1810
+ interface SidebarUserCardProps extends Omit<
1811
+ ButtonHTMLAttributes<HTMLButtonElement>,
1812
+ 'aria-label' | 'onClick' | 'children'
1813
+ > {
1747
1814
  /**
1748
- * Hint/help text.
1815
+ * Panel content shown inside the drawer/modal (typically the app's
1816
+ * `SettingsPanel`). It can dismiss the drawer by calling
1817
+ * `usePanelClose()` — no `onClose` prop-threading required.
1749
1818
  */
1750
- hint?: string
1819
+ children: ReactNode
1751
1820
  /**
1752
- * Whether the textarea auto-resizes.
1821
+ * Display name override. When omitted, falls back to
1822
+ * `useAuth().user?.name`, then `email`, then a guest label.
1753
1823
  */
1754
- autoResize?: boolean
1824
+ name?: string
1755
1825
  /**
1756
- * Minimum number of rows.
1826
+ * Secondary line under the name (role, plan, status, etc.).
1827
+ * Apps typically pass something like `t('sidebar.memberStatus', {}, { defaultValue: 'Premium Member' })`.
1828
+ * When omitted and the auth user has an email, the email is shown instead.
1757
1829
  */
1758
- minRows?: number
1830
+ secondaryLine?: string
1831
+ /** Optional avatar image URL — overrides `useAuth().user?.avatarUrl`. */
1832
+ avatarUrl?: string
1833
+ /** i18n key for the trigger button's aria-label. Default: `'sidebarUserCard.open'`. */
1834
+ ariaLabelKey?: string
1835
+ /** Fallback aria-label if the i18n key is missing. Default: `'Open account menu'`. */
1836
+ ariaLabelDefault?: string
1759
1837
  /**
1760
- * Maximum number of rows.
1838
+ * `data-mol-id` for the trigger button. Defaults to
1839
+ * `'sidebar-user-card'`. Pass a different value (e.g. `'user-menu'`)
1840
+ * to disambiguate or align with an app's existing e2e selectors.
1761
1841
  */
1762
- maxRows?: number
1842
+ dataMolId?: string
1763
1843
  }
1764
1844
  ```
1765
1845
 
@@ -1790,101 +1870,58 @@ interface ThemeToggleProps extends Omit<
1790
1870
  }
1791
1871
  ```
1792
1872
 
1793
- #### `ToastProps`
1794
-
1795
- Props for the Toast/notification component.
1796
-
1797
- ```typescript
1798
- interface ToastProps extends HTMLElementProps {
1799
- /**
1800
- * Toast content.
1801
- */
1802
- children?: Children
1803
- /**
1804
- * Toast title.
1805
- */
1806
- title?: string
1807
- /**
1808
- * Toast description.
1809
- */
1810
- description?: string
1811
- /**
1812
- * Toast status/type.
1813
- */
1814
- status?: ColorVariant
1815
- /**
1816
- * Duration in milliseconds (0 for persistent).
1817
- */
1818
- duration?: number
1819
- /**
1820
- * Whether the toast is dismissible.
1821
- */
1822
- dismissible?: boolean
1823
- /**
1824
- * Called when dismissed.
1825
- */
1826
- onDismiss?: () => void
1827
- /**
1828
- * Toast position.
1829
- */
1830
- position?: 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
1831
- /**
1832
- * Accessible label for the close button.
1833
- * @default 'Close'
1834
- */
1835
- closeLabel?: string
1836
- }
1837
- ```
1873
+ #### `UserMenuProps`
1838
1874
 
1839
- #### `TooltipProps`
1875
+ Props for {@link UserMenu}.
1840
1876
 
1841
- Props for the Tooltip component (hover/focus popover with informational text).
1877
+ The inner trigger is a Molecule `<Button>` whose own prop set
1878
+ conflicts with raw `ButtonHTMLAttributes` (color, value, size enums),
1879
+ so this component exposes a curated set of customization points
1880
+ instead of extending HTML attributes. For one-off extra attributes,
1881
+ pass them via `dataMolId` / `className` / `style` props.
1842
1882
 
1843
1883
  ```typescript
1844
- interface TooltipProps extends HTMLElementProps {
1845
- /**
1846
- * Tooltip content.
1847
- */
1848
- content: Children
1884
+ interface UserMenuProps {
1849
1885
  /**
1850
- * Element that triggers the tooltip.
1886
+ * Panel content shown inside the drawer/modal (typically the app's
1887
+ * `SettingsPanel`). It can dismiss the drawer by calling
1888
+ * `usePanelClose()` — no `onClose` prop-threading required.
1851
1889
  */
1852
- children: Children
1890
+ children: ReactNode
1853
1891
  /**
1854
- * Tooltip placement.
1892
+ * i18n key for the trigger button's aria-label. Defaults to
1893
+ * `'userMenu.open'`. Apps whose existing locales use a different key
1894
+ * (e.g. `'userMenu.openButton'`) can override.
1855
1895
  */
1856
- placement?: TooltipPlacement
1896
+ ariaLabelKey?: string
1897
+ /** Fallback aria-label if the i18n key is missing. Defaults to `"Open user menu"`. */
1898
+ ariaLabelDefault?: string
1899
+ /** Icon name for the trigger button. Defaults to `'user'`. */
1900
+ triggerIcon?: IconName
1901
+ /** Pixel size for the trigger icon. Defaults to 20. */
1902
+ triggerIconSize?: number
1857
1903
  /**
1858
- * Delay before showing (ms).
1904
+ * `data-mol-id` for the trigger button. Defaults to `'user-menu'`.
1905
+ * Pass an explicit value to disambiguate when the same page mounts
1906
+ * more than one UserMenu.
1859
1907
  */
1860
- delay?: number
1908
+ dataMolId?: string
1909
+ /** Extra className composed onto the trigger button. */
1910
+ className?: string
1911
+ /** Whether the trigger button is disabled. */
1912
+ disabled?: boolean
1861
1913
  /**
1862
- * Whether the tooltip has an arrow.
1914
+ * Override the trigger button's click handler. When provided, called
1915
+ * instead of opening the panel — hosts use this to intercept the click
1916
+ * (e.g. to open an auth modal for guest users).
1863
1917
  */
1864
- hasArrow?: boolean
1865
- }
1866
- ```
1867
-
1868
- #### `UserMenuPopoverPanelProps`
1869
-
1870
- Props for {@link UserMenuPopoverPanel}.
1871
-
1872
- ```typescript
1873
- interface UserMenuPopoverPanelProps {
1918
+ onClick?: () => void
1874
1919
  /**
1875
- * Panel body — typically a set of `<Link>` nav items and a
1876
- * `<UserMenuPopoverSignOut />`. Rendered inside a `<nav>` below the
1877
- * built-in identity header.
1920
+ * Which side of the viewport the panel opens from. Defaults to
1921
+ * `'right'`. Apps whose sidebar trigger sits on the left should pass
1922
+ * `'left'` so the panel opens adjacent to the trigger.
1878
1923
  */
1879
- children: ReactNode
1880
- /** Extra className composed onto the absolute-positioned panel. */
1881
- className?: string
1882
- /** i18n key for the panel's aria-label. Default: `'userMenu.panelLabel'`. */
1883
- ariaLabelKey?: string
1884
- /** Fallback aria-label if the i18n key is missing. Default: `'Account menu'`. */
1885
- ariaLabelDefault?: string
1886
- /** `data-mol-id` for the panel. Default: `'user-menu-panel'`. */
1887
- dataMolId?: string
1924
+ side?: 'left' | 'right'
1888
1925
  }
1889
1926
  ```
1890
1927
 
@@ -1909,23 +1946,6 @@ interface UserMenuPopoverProps {
1909
1946
  }
1910
1947
  ```
1911
1948
 
1912
- #### `UserMenuPopoverSignOutProps`
1913
-
1914
- Props for {@link UserMenuPopoverSignOut}.
1915
-
1916
- ```typescript
1917
- interface UserMenuPopoverSignOutProps {
1918
- /** i18n key for the button label. Default: `'userMenu.signOut'`. */
1919
- labelKey?: string
1920
- /** Fallback label if the i18n key is missing. Default: `'Sign out'`. */
1921
- labelDefault?: string
1922
- /** `data-mol-id` for the button. Default: `'user-menu-sign-out'`. */
1923
- dataMolId?: string
1924
- /** Extra className composed onto the button. */
1925
- className?: string
1926
- }
1927
- ```
1928
-
1929
1949
  #### `UserMenuPopoverTriggerProps`
1930
1950
 
1931
1951
  Props for {@link UserMenuPopoverTrigger}.
@@ -1933,89 +1953,58 @@ Props for {@link UserMenuPopoverTrigger}.
1933
1953
  ```typescript
1934
1954
  interface UserMenuPopoverTriggerProps {
1935
1955
  /** i18n key for the trigger's aria-label. Default: `'userMenu.open'`. */
1936
- ariaLabelKey?: string
1937
- /** Fallback aria-label if the i18n key is missing. Default: `'Open user menu'`. */
1938
- ariaLabelDefault?: string
1939
- /** `data-mol-id` for the trigger button. Default: `'user-menu'`. */
1940
- dataMolId?: string
1941
- /** Extra className composed onto the trigger button. */
1942
- className?: string
1943
- }
1944
- ```
1945
-
1946
- #### `UserMenuProps`
1947
-
1948
- Props for {@link UserMenu}.
1949
-
1950
- The inner trigger is a Molecule `<Button>` whose own prop set
1951
- conflicts with raw `ButtonHTMLAttributes` (color, value, size enums),
1952
- so this component exposes a curated set of customization points
1953
- instead of extending HTML attributes. For one-off extra attributes,
1954
- pass them via `dataMolId` / `className` / `style` props.
1955
-
1956
- ```typescript
1957
- interface UserMenuProps {
1958
- /**
1959
- * Panel content shown inside the drawer/modal (typically the app's
1960
- * `SettingsPanel`). It can dismiss the drawer by calling
1961
- * `usePanelClose()` — no `onClose` prop-threading required.
1962
- */
1963
- children: ReactNode
1964
- /**
1965
- * i18n key for the trigger button's aria-label. Defaults to
1966
- * `'userMenu.open'`. Apps whose existing locales use a different key
1967
- * (e.g. `'userMenu.openButton'`) can override.
1968
- */
1969
- ariaLabelKey?: string
1970
- /** Fallback aria-label if the i18n key is missing. Defaults to `"Open user menu"`. */
1971
- ariaLabelDefault?: string
1972
- /** Icon name for the trigger button. Defaults to `'user'`. */
1973
- triggerIcon?: IconName
1974
- /** Pixel size for the trigger icon. Defaults to 20. */
1975
- triggerIconSize?: number
1976
- /**
1977
- * `data-mol-id` for the trigger button. Defaults to `'user-menu'`.
1978
- * Pass an explicit value to disambiguate when the same page mounts
1979
- * more than one UserMenu.
1980
- */
1956
+ ariaLabelKey?: string
1957
+ /** Fallback aria-label if the i18n key is missing. Default: `'Open user menu'`. */
1958
+ ariaLabelDefault?: string
1959
+ /** `data-mol-id` for the trigger button. Default: `'user-menu'`. */
1981
1960
  dataMolId?: string
1982
1961
  /** Extra className composed onto the trigger button. */
1983
1962
  className?: string
1984
- /** Whether the trigger button is disabled. */
1985
- disabled?: boolean
1986
- /**
1987
- * Override the trigger button's click handler. When provided, called
1988
- * instead of opening the panel — hosts use this to intercept the click
1989
- * (e.g. to open an auth modal for guest users).
1990
- */
1991
- onClick?: () => void
1992
- /**
1993
- * Which side of the viewport the panel opens from. Defaults to
1994
- * `'right'`. Apps whose sidebar trigger sits on the left should pass
1995
- * `'left'` so the panel opens adjacent to the trigger.
1996
- */
1997
- side?: 'left' | 'right'
1998
1963
  }
1999
1964
  ```
2000
1965
 
2001
- ### Types
2002
-
2003
- #### `ButtonVariant`
1966
+ #### `UserMenuPopoverPanelProps`
2004
1967
 
2005
- Button visual variant styles.
1968
+ Props for {@link UserMenuPopoverPanel}.
2006
1969
 
2007
1970
  ```typescript
2008
- type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'link'
1971
+ interface UserMenuPopoverPanelProps {
1972
+ /**
1973
+ * Panel body — typically a set of `<Link>` nav items and a
1974
+ * `<UserMenuPopoverSignOut />`. Rendered inside a `<nav>` below the
1975
+ * built-in identity header.
1976
+ */
1977
+ children: ReactNode
1978
+ /** Extra className composed onto the absolute-positioned panel. */
1979
+ className?: string
1980
+ /** i18n key for the panel's aria-label. Default: `'userMenu.panelLabel'`. */
1981
+ ariaLabelKey?: string
1982
+ /** Fallback aria-label if the i18n key is missing. Default: `'Account menu'`. */
1983
+ ariaLabelDefault?: string
1984
+ /** `data-mol-id` for the panel. Default: `'user-menu-panel'`. */
1985
+ dataMolId?: string
1986
+ }
2009
1987
  ```
2010
1988
 
2011
- #### `ChangeEventHandler`
1989
+ #### `UserMenuPopoverSignOutProps`
2012
1990
 
2013
- Framework-agnostic change event handler.
1991
+ Props for {@link UserMenuPopoverSignOut}.
2014
1992
 
2015
1993
  ```typescript
2016
- type ChangeEventHandler = EventHandler<Event>
1994
+ interface UserMenuPopoverSignOutProps {
1995
+ /** i18n key for the button label. Default: `'userMenu.signOut'`. */
1996
+ labelKey?: string
1997
+ /** Fallback label if the i18n key is missing. Default: `'Sign out'`. */
1998
+ labelDefault?: string
1999
+ /** `data-mol-id` for the button. Default: `'user-menu-sign-out'`. */
2000
+ dataMolId?: string
2001
+ /** Extra className composed onto the button. */
2002
+ className?: string
2003
+ }
2017
2004
  ```
2018
2005
 
2006
+ ### Types
2007
+
2019
2008
  #### `Children`
2020
2009
 
2021
2010
  Framework-agnostic child content.
@@ -2025,20 +2014,30 @@ Use `unknown` to allow any framework's node type (ReactNode, VNode, etc.).
2025
2014
  type Children = unknown
2026
2015
  ```
2027
2016
 
2028
- #### `ColorVariant`
2017
+ #### `EventHandler`
2029
2018
 
2030
- Semantic color variants used across components for status indication.
2019
+ Framework-agnostic event handler.
2031
2020
 
2032
2021
  ```typescript
2033
- type ColorVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info'
2022
+ type EventHandler<E = Event> = (event: E) => void
2034
2023
  ```
2035
2024
 
2036
- #### `EventHandler`
2025
+ - `event` — The DOM event object.
2037
2026
 
2038
- Framework-agnostic event handler.
2027
+ #### `MouseEventHandler`
2028
+
2029
+ Framework-agnostic mouse event handler.
2039
2030
 
2040
2031
  ```typescript
2041
- type EventHandler<E = Event> = (event: E) => void
2032
+ type MouseEventHandler = EventHandler<MouseEvent>
2033
+ ```
2034
+
2035
+ #### `KeyboardEventHandler`
2036
+
2037
+ Framework-agnostic keyboard event handler.
2038
+
2039
+ ```typescript
2040
+ type KeyboardEventHandler = EventHandler<KeyboardEvent>
2042
2041
  ```
2043
2042
 
2044
2043
  #### `FocusEventHandler`
@@ -2049,6 +2048,14 @@ Framework-agnostic focus event handler.
2049
2048
  type FocusEventHandler = EventHandler<FocusEvent>
2050
2049
  ```
2051
2050
 
2051
+ #### `ChangeEventHandler`
2052
+
2053
+ Framework-agnostic change event handler.
2054
+
2055
+ ```typescript
2056
+ type ChangeEventHandler = EventHandler<Event>
2057
+ ```
2058
+
2052
2059
  #### `FormEventHandler`
2053
2060
 
2054
2061
  Framework-agnostic form event handler.
@@ -2057,6 +2064,30 @@ Framework-agnostic form event handler.
2057
2064
  type FormEventHandler = EventHandler<Event>
2058
2065
  ```
2059
2066
 
2067
+ #### `Size`
2068
+
2069
+ Standard size scale used across all molecule UI components (buttons, inputs, badges, etc.).
2070
+
2071
+ ```typescript
2072
+ type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
2073
+ ```
2074
+
2075
+ #### `ColorVariant`
2076
+
2077
+ Semantic color variants used across components for status indication.
2078
+
2079
+ ```typescript
2080
+ type ColorVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info'
2081
+ ```
2082
+
2083
+ #### `ButtonVariant`
2084
+
2085
+ Button visual variant styles.
2086
+
2087
+ ```typescript
2088
+ type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'link'
2089
+ ```
2090
+
2060
2091
  #### `InputType`
2061
2092
 
2062
2093
  Allowed HTML input type attribute values for the Input component.
@@ -2075,14 +2106,6 @@ type InputType =
2075
2106
  | 'datetime-local'
2076
2107
  ```
2077
2108
 
2078
- #### `KeyboardEventHandler`
2079
-
2080
- Framework-agnostic keyboard event handler.
2081
-
2082
- ```typescript
2083
- type KeyboardEventHandler = EventHandler<KeyboardEvent>
2084
- ```
2085
-
2086
2109
  #### `ModalSize`
2087
2110
 
2088
2111
  Modal size variants including full-screen.
@@ -2091,12 +2114,13 @@ Modal size variants including full-screen.
2091
2114
  type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'
2092
2115
  ```
2093
2116
 
2094
- #### `MouseEventHandler`
2117
+ #### `TooltipPlacement`
2095
2118
 
2096
- Framework-agnostic mouse event handler.
2119
+ Position where a tooltip renders relative to its trigger element (top, bottom, left, right, and corner variants).
2097
2120
 
2098
2121
  ```typescript
2099
- type MouseEventHandler = EventHandler<MouseEvent>
2122
+ type TooltipPlacement =
2123
+ 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end'
2100
2124
  ```
2101
2125
 
2102
2126
  #### `ShellSidebarWidth`
@@ -2112,23 +2136,6 @@ as a token" case).
2112
2136
  type ShellSidebarWidth = 'sm' | 'md' | 'lg' | number
2113
2137
  ```
2114
2138
 
2115
- #### `Size`
2116
-
2117
- Standard size scale used across all molecule UI components (buttons, inputs, badges, etc.).
2118
-
2119
- ```typescript
2120
- type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
2121
- ```
2122
-
2123
- #### `TooltipPlacement`
2124
-
2125
- Position where a tooltip renders relative to its trigger element (top, bottom, left, right, and corner variants).
2126
-
2127
- ```typescript
2128
- type TooltipPlacement =
2129
- 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end'
2130
- ```
2131
-
2132
2139
  ### Functions
2133
2140
 
2134
2141
  #### `AuthGuard(props)`
@@ -2159,18 +2166,6 @@ function AuthGuard({
2159
2166
 
2160
2167
  - `props` — {@link AuthGuardProps}
2161
2168
 
2162
- #### `cn(inputs)`
2163
-
2164
- Merge class name strings, filtering out falsy values (undefined, null, false).
2165
-
2166
- ```typescript
2167
- function cn(inputs?: (string | false | null | undefined)[]): string
2168
- ```
2169
-
2170
- - `inputs` — Class name strings or falsy values to be filtered out.
2171
-
2172
- **Returns:** A single space-separated class string.
2173
-
2174
2169
  #### `ConfirmDialog(props)`
2175
2170
 
2176
2171
  Are-you-sure-style confirmation modal for destructive actions.
@@ -2293,6 +2288,19 @@ function PanelCloseProvider({ close, children }: PanelCloseProviderProps): JSX.E
2293
2288
 
2294
2289
  **Returns:** The children wrapped in the close-context provider.
2295
2290
 
2291
+ #### `usePanelClose()`
2292
+
2293
+ Returns the callback that dismisses the drawer the current panel is
2294
+ mounted in. Safe to call anywhere — returns a no-op when no enclosing
2295
+ `UserMenu` / `SidebarUserCard` provides one (e.g. a `SettingsPanel`
2296
+ rendered as a standalone page).
2297
+
2298
+ ```typescript
2299
+ function usePanelClose(): () => void
2300
+ ```
2301
+
2302
+ **Returns:** A function that closes the enclosing drawer, or a no-op.
2303
+
2296
2304
  #### `PromptDialog(props)`
2297
2305
 
2298
2306
  Modal that asks the user for a short string, replacing `window.prompt`.
@@ -2318,54 +2326,50 @@ function PromptDialog({
2318
2326
 
2319
2327
  - `props` — Component props (see {@link PromptDialogProps}).
2320
2328
 
2321
- #### `ResponsiveAppShellContent(props)`
2329
+ #### `useIsDesktop(query)`
2322
2330
 
2323
- The shell's `<main>` landmark. Grows to fill the row and may shrink below
2324
- its content width, so page-level `overflow-x` strips scroll inside the
2325
- page instead of widening the whole shell.
2331
+ Whether the viewport is at least 768px wide (the fleet's `md` breakpoint).
2332
+
2333
+ Reads `window.matchMedia` synchronously in the state initializer, so the
2334
+ FIRST client render is already correct — no mobile-then-desktop swap frame
2335
+ (this is time-tracking's QA-hardened version; the naive
2336
+ "state starts false, sync in an effect" variant mounts the wrong shell for
2337
+ one frame and double-fetches route data on desktop). Subscribes to
2338
+ `change` events afterwards. Guarded for non-browser environments
2339
+ (SSR/node): returns `false` (the mobile shell) when no `window.matchMedia`
2340
+ exists.
2326
2341
 
2327
2342
  ```typescript
2328
- function ResponsiveAppShellContent({
2329
- children,
2330
- className,
2331
- style,
2332
- dataMolId = 'shell-content',
2333
- testId,
2334
- }: ResponsiveAppShellContentProps): JSX.Element
2343
+ function useIsDesktop(query?: string): boolean
2335
2344
  ```
2336
2345
 
2337
- - `props` — The page content and class/id overrides.
2346
+ - `query` — Override the media query. Defaults to `(min-width: 768px)`.
2338
2347
 
2339
- **Returns:** The main landmark.
2348
+ **Returns:** Whether the desktop shell should mount.
2340
2349
 
2341
- #### `ResponsiveAppShellDrawer(props)`
2350
+ #### `ResponsiveAppShellTopBar(props)`
2342
2351
 
2343
- The mobile nav drawer (<768px): a left-anchored dialog panel over a
2344
- backdrop, opened by the menu trigger the TopBar renders.
2352
+ The shell's `<header>` landmark: menu trigger (mobile, when a Drawer is
2353
+ composed), brand, and trailing actions.
2345
2354
 
2346
- Implements the WAI-ARIA APG dialog pattern — focus moves into the panel on
2347
- open and returns to the previously focused element (the trigger) on close,
2348
- Tab is trapped inside, Escape and backdrop clicks close, body scroll locks
2349
- (reference-counted with any stacked `<Modal>`), and the shell closes it on
2350
- SPA navigation. Renders nothing on desktop or while closed.
2355
+ Sticks to the top of the viewport by default. The menu trigger renders
2356
+ only while a `ResponsiveAppShell.Drawer` is part of the composition AND
2357
+ the viewport is below the breakpoint.
2351
2358
 
2352
2359
  ```typescript
2353
- function ResponsiveAppShellDrawer({
2354
- children,
2355
- header,
2356
- navLabel,
2357
- footer,
2358
- showCloseButton = true,
2360
+ function ResponsiveAppShellTopBar({
2361
+ brand,
2362
+ actions,
2363
+ mobileOnly = false,
2359
2364
  className,
2360
- dataMolId = 'shell-drawer',
2361
- closeDataMolId = 'shell-drawer-close',
2365
+ dataMolId = 'shell-topbar',
2362
2366
  testId,
2363
- }: ResponsiveAppShellDrawerProps): JSX.Element | null
2367
+ }: ResponsiveAppShellTopBarProps): JSX.Element | null
2364
2368
  ```
2365
2369
 
2366
- - `props` — Nav children, header/nav-label/footer slots, close-button and class/id overrides.
2370
+ - `props` — Brand/actions slots, `mobileOnly`, and class/id overrides.
2367
2371
 
2368
- **Returns:** The drawer portal while open on mobile, otherwise `null`.
2372
+ **Returns:** The header landmark, or `null` when `mobileOnly` is set on desktop.
2369
2373
 
2370
2374
  #### `ResponsiveAppShellSidebar(props)`
2371
2375
 
@@ -2390,29 +2394,54 @@ function ResponsiveAppShellSidebar({
2390
2394
 
2391
2395
  **Returns:** The sidebar landmarks, or `null` below the breakpoint.
2392
2396
 
2393
- #### `ResponsiveAppShellTopBar(props)`
2397
+ #### `ResponsiveAppShellDrawer(props)`
2398
+
2399
+ The mobile nav drawer (<768px): a left-anchored dialog panel over a
2400
+ backdrop, opened by the menu trigger the TopBar renders.
2401
+
2402
+ Implements the WAI-ARIA APG dialog pattern — focus moves into the panel on
2403
+ open and returns to the previously focused element (the trigger) on close,
2404
+ Tab is trapped inside, Escape and backdrop clicks close, body scroll locks
2405
+ (reference-counted with any stacked `<Modal>`), and the shell closes it on
2406
+ SPA navigation. Renders nothing on desktop or while closed.
2407
+
2408
+ ```typescript
2409
+ function ResponsiveAppShellDrawer({
2410
+ children,
2411
+ header,
2412
+ navLabel,
2413
+ footer,
2414
+ showCloseButton = true,
2415
+ className,
2416
+ dataMolId = 'shell-drawer',
2417
+ closeDataMolId = 'shell-drawer-close',
2418
+ testId,
2419
+ }: ResponsiveAppShellDrawerProps): JSX.Element | null
2420
+ ```
2421
+
2422
+ - `props` — Nav children, header/nav-label/footer slots, close-button and class/id overrides.
2423
+
2424
+ **Returns:** The drawer portal while open on mobile, otherwise `null`.
2394
2425
 
2395
- The shell's `<header>` landmark: menu trigger (mobile, when a Drawer is
2396
- composed), brand, and trailing actions.
2426
+ #### `ResponsiveAppShellContent(props)`
2397
2427
 
2398
- Sticks to the top of the viewport by default. The menu trigger renders
2399
- only while a `ResponsiveAppShell.Drawer` is part of the composition AND
2400
- the viewport is below the breakpoint.
2428
+ The shell's `<main>` landmark. Grows to fill the row and may shrink below
2429
+ its content width, so page-level `overflow-x` strips scroll inside the
2430
+ page instead of widening the whole shell.
2401
2431
 
2402
2432
  ```typescript
2403
- function ResponsiveAppShellTopBar({
2404
- brand,
2405
- actions,
2406
- mobileOnly = false,
2433
+ function ResponsiveAppShellContent({
2434
+ children,
2407
2435
  className,
2408
- dataMolId = 'shell-topbar',
2436
+ style,
2437
+ dataMolId = 'shell-content',
2409
2438
  testId,
2410
- }: ResponsiveAppShellTopBarProps): JSX.Element | null
2439
+ }: ResponsiveAppShellContentProps): JSX.Element
2411
2440
  ```
2412
2441
 
2413
- - `props` — Brand/actions slots, `mobileOnly`, and class/id overrides.
2442
+ - `props` — The page content and class/id overrides.
2414
2443
 
2415
- **Returns:** The header landmark, or `null` when `mobileOnly` is set on desktop.
2444
+ **Returns:** The main landmark.
2416
2445
 
2417
2446
  #### `SidebarUserCard(props)`
2418
2447
 
@@ -2474,6 +2503,16 @@ function ThemeToggle({
2474
2503
 
2475
2504
  - `props` — {@link ThemeToggleProps}
2476
2505
 
2506
+ #### `useToast()`
2507
+
2508
+ Hook to access the toast context for adding and removing toasts.
2509
+
2510
+ ```typescript
2511
+ function useToast(): ToastContextValue
2512
+ ```
2513
+
2514
+ **Returns:** The toast context value with toast management methods.
2515
+
2477
2516
  #### `ToastProvider(props)`
2478
2517
 
2479
2518
  Provider component that manages global toast state.
@@ -2491,40 +2530,6 @@ function ToastProvider({
2491
2530
 
2492
2531
  **Returns:** The rendered provider with toast container.
2493
2532
 
2494
- #### `useIsDesktop(query)`
2495
-
2496
- Whether the viewport is at least 768px wide (the fleet's `md` breakpoint).
2497
-
2498
- Reads `window.matchMedia` synchronously in the state initializer, so the
2499
- FIRST client render is already correct — no mobile-then-desktop swap frame
2500
- (this is time-tracking's QA-hardened version; the naive
2501
- "state starts false, sync in an effect" variant mounts the wrong shell for
2502
- one frame and double-fetches route data on desktop). Subscribes to
2503
- `change` events afterwards. Guarded for non-browser environments
2504
- (SSR/node): returns `false` (the mobile shell) when no `window.matchMedia`
2505
- exists.
2506
-
2507
- ```typescript
2508
- function useIsDesktop(query?: string): boolean
2509
- ```
2510
-
2511
- - `query` — Override the media query. Defaults to `(min-width: 768px)`.
2512
-
2513
- **Returns:** Whether the desktop shell should mount.
2514
-
2515
- #### `usePanelClose()`
2516
-
2517
- Returns the callback that dismisses the drawer the current panel is
2518
- mounted in. Safe to call anywhere — returns a no-op when no enclosing
2519
- `UserMenu` / `SidebarUserCard` provides one (e.g. a `SettingsPanel`
2520
- rendered as a standalone page).
2521
-
2522
- ```typescript
2523
- function usePanelClose(): () => void
2524
- ```
2525
-
2526
- **Returns:** A function that closes the enclosing drawer, or a no-op.
2527
-
2528
2533
  #### `UserMenu(props)`
2529
2534
 
2530
2535
  Avatar-style trigger that opens the app's settings panel in a drawer.
@@ -2570,6 +2575,24 @@ function UserMenuPopover({
2570
2575
 
2571
2576
  **Returns:** The relative-positioned popover container.
2572
2577
 
2578
+ #### `UserMenuPopoverTrigger(props)`
2579
+
2580
+ The trigger button: an initials avatar plus the account name and email,
2581
+ styled as a full-width sidebar card. Toggles the popover panel.
2582
+
2583
+ ```typescript
2584
+ function UserMenuPopoverTrigger({
2585
+ ariaLabelKey = 'userMenu.open',
2586
+ ariaLabelDefault = 'Open user menu',
2587
+ dataMolId = 'user-menu',
2588
+ className,
2589
+ }: UserMenuPopoverTriggerProps): React.JSX.Element
2590
+ ```
2591
+
2592
+ - `props` — aria-label overrides, `data-mol-id`, and className.
2593
+
2594
+ **Returns:** The popover trigger button.
2595
+
2573
2596
  #### `UserMenuPopoverPanel(props)`
2574
2597
 
2575
2598
  The popover panel: an absolutely-positioned card with a built-in
@@ -2597,6 +2620,19 @@ function UserMenuPopoverPanel({
2597
2620
 
2598
2621
  **Returns:** The popover panel, or `null` when closed.
2599
2622
 
2623
+ #### `useUserMenuPopoverClose()`
2624
+
2625
+ Returns a callback that closes the enclosing `UserMenuPopover`. Useful
2626
+ for nav-item `onClick` handlers that should dismiss the popover even
2627
+ when they don't change the route. Returns a no-op when called outside
2628
+ a `UserMenuPopover`.
2629
+
2630
+ ```typescript
2631
+ function useUserMenuPopoverClose(): () => void
2632
+ ```
2633
+
2634
+ **Returns:** A function that closes the popover.
2635
+
2600
2636
  #### `UserMenuPopoverSignOut(props)`
2601
2637
 
2602
2638
  The sign-out nav item — closes the popover and calls `auth.logout()`.
@@ -2615,46 +2651,17 @@ function UserMenuPopoverSignOut({
2615
2651
 
2616
2652
  **Returns:** The sign-out button.
2617
2653
 
2618
- #### `UserMenuPopoverTrigger(props)`
2619
-
2620
- The trigger button: an initials avatar plus the account name and email,
2621
- styled as a full-width sidebar card. Toggles the popover panel.
2622
-
2623
- ```typescript
2624
- function UserMenuPopoverTrigger({
2625
- ariaLabelKey = 'userMenu.open',
2626
- ariaLabelDefault = 'Open user menu',
2627
- dataMolId = 'user-menu',
2628
- className,
2629
- }: UserMenuPopoverTriggerProps): React.JSX.Element
2630
- ```
2631
-
2632
- - `props` — aria-label overrides, `data-mol-id`, and className.
2633
-
2634
- **Returns:** The popover trigger button.
2635
-
2636
- #### `useToast()`
2654
+ #### `cn(inputs)`
2637
2655
 
2638
- Hook to access the toast context for adding and removing toasts.
2656
+ Merge class name strings, filtering out falsy values (undefined, null, false).
2639
2657
 
2640
2658
  ```typescript
2641
- function useToast(): ToastContextValue
2659
+ function cn(inputs?: (string | false | null | undefined)[]): string
2642
2660
  ```
2643
2661
 
2644
- **Returns:** The toast context value with toast management methods.
2645
-
2646
- #### `useUserMenuPopoverClose()`
2647
-
2648
- Returns a callback that closes the enclosing `UserMenuPopover`. Useful
2649
- for nav-item `onClick` handlers that should dismiss the popover even
2650
- when they don't change the route. Returns a no-op when called outside
2651
- a `UserMenuPopover`.
2652
-
2653
- ```typescript
2654
- function useUserMenuPopoverClose(): () => void
2655
- ```
2662
+ - `inputs` — Class name strings or falsy values to be filtered out.
2656
2663
 
2657
- **Returns:** A function that closes the popover.
2664
+ **Returns:** A single space-separated class string.
2658
2665
 
2659
2666
  ### Constants
2660
2667
 
@@ -2724,53 +2731,53 @@ const Card: React.ForwardRefExoticComponent<
2724
2731
  >
2725
2732
  ```
2726
2733
 
2727
- #### `CardContent`
2734
+ #### `CardHeader`
2728
2735
 
2729
- Card content component.
2736
+ Card header component.
2730
2737
 
2731
2738
  ```typescript
2732
- const CardContent: React.ForwardRefExoticComponent<
2739
+ const CardHeader: React.ForwardRefExoticComponent<
2733
2740
  React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>
2734
2741
  >
2735
2742
  ```
2736
2743
 
2737
- #### `CardDescription`
2744
+ #### `CardTitle`
2738
2745
 
2739
- Card description component.
2746
+ Card title component.
2740
2747
 
2741
2748
  ```typescript
2742
- const CardDescription: React.ForwardRefExoticComponent<
2743
- React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>
2749
+ const CardTitle: React.ForwardRefExoticComponent<
2750
+ React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLHeadingElement>
2744
2751
  >
2745
2752
  ```
2746
2753
 
2747
- #### `CardFooter`
2754
+ #### `CardDescription`
2748
2755
 
2749
- Card footer component.
2756
+ Card description component.
2750
2757
 
2751
2758
  ```typescript
2752
- const CardFooter: React.ForwardRefExoticComponent<
2753
- React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>
2759
+ const CardDescription: React.ForwardRefExoticComponent<
2760
+ React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>
2754
2761
  >
2755
2762
  ```
2756
2763
 
2757
- #### `CardHeader`
2764
+ #### `CardContent`
2758
2765
 
2759
- Card header component.
2766
+ Card content component.
2760
2767
 
2761
2768
  ```typescript
2762
- const CardHeader: React.ForwardRefExoticComponent<
2769
+ const CardContent: React.ForwardRefExoticComponent<
2763
2770
  React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>
2764
2771
  >
2765
2772
  ```
2766
2773
 
2767
- #### `CardTitle`
2774
+ #### `CardFooter`
2768
2775
 
2769
- Card title component.
2776
+ Card footer component.
2770
2777
 
2771
2778
  ```typescript
2772
- const CardTitle: React.ForwardRefExoticComponent<
2773
- React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLHeadingElement>
2779
+ const CardFooter: React.ForwardRefExoticComponent<
2780
+ React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>
2774
2781
  >
2775
2782
  ```
2776
2783
 
@@ -2802,16 +2809,6 @@ const ConfirmButton: React.ForwardRefExoticComponent<
2802
2809
  >
2803
2810
  ```
2804
2811
 
2805
- #### `Container`
2806
-
2807
- Container component.
2808
-
2809
- ```typescript
2810
- const Container: React.ForwardRefExoticComponent<
2811
- ContainerProps & React.RefAttributes<HTMLDivElement>
2812
- >
2813
- ```
2814
-
2815
2812
  #### `Dropdown`
2816
2813
 
2817
2814
  Dropdown component.
@@ -2868,14 +2865,6 @@ const EmptyState: React.ForwardRefExoticComponent<
2868
2865
  >
2869
2866
  ```
2870
2867
 
2871
- #### `Flex`
2872
-
2873
- Flex container component.
2874
-
2875
- ```typescript
2876
- const Flex: React.ForwardRefExoticComponent<FlexProps & React.RefAttributes<HTMLDivElement>>
2877
- ```
2878
-
2879
2868
  #### `FloatingInput`
2880
2869
 
2881
2870
  An input with a floating label.
@@ -2917,12 +2906,16 @@ const FormField: React.ForwardRefExoticComponent<
2917
2906
  >
2918
2907
  ```
2919
2908
 
2920
- #### `Grid`
2909
+ #### `Label`
2921
2910
 
2922
- Grid container component.
2911
+ Label component.
2923
2912
 
2924
2913
  ```typescript
2925
- const Grid: React.ForwardRefExoticComponent<GridProps & React.RefAttributes<HTMLDivElement>>
2914
+ const Label: React.ForwardRefExoticComponent<
2915
+ React.LabelHTMLAttributes<HTMLLabelElement> & {
2916
+ required?: boolean
2917
+ } & React.RefAttributes<HTMLLabelElement>
2918
+ >
2926
2919
  ```
2927
2920
 
2928
2921
  #### `Input`
@@ -2933,18 +2926,40 @@ Input component.
2933
2926
  const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>
2934
2927
  ```
2935
2928
 
2936
- #### `Label`
2929
+ #### `Container`
2937
2930
 
2938
- Label component.
2931
+ Container component.
2939
2932
 
2940
2933
  ```typescript
2941
- const Label: React.ForwardRefExoticComponent<
2942
- React.LabelHTMLAttributes<HTMLLabelElement> & {
2943
- required?: boolean
2944
- } & React.RefAttributes<HTMLLabelElement>
2934
+ const Container: React.ForwardRefExoticComponent<
2935
+ ContainerProps & React.RefAttributes<HTMLDivElement>
2945
2936
  >
2946
2937
  ```
2947
2938
 
2939
+ #### `Flex`
2940
+
2941
+ Flex container component.
2942
+
2943
+ ```typescript
2944
+ const Flex: React.ForwardRefExoticComponent<FlexProps & React.RefAttributes<HTMLDivElement>>
2945
+ ```
2946
+
2947
+ #### `Grid`
2948
+
2949
+ Grid container component.
2950
+
2951
+ ```typescript
2952
+ const Grid: React.ForwardRefExoticComponent<GridProps & React.RefAttributes<HTMLDivElement>>
2953
+ ```
2954
+
2955
+ #### `Spacer`
2956
+
2957
+ Spacer component.
2958
+
2959
+ ```typescript
2960
+ const Spacer: React.ForwardRefExoticComponent<SpacerProps & React.RefAttributes<HTMLDivElement>>
2961
+ ```
2962
+
2948
2963
  #### `Modal`
2949
2964
 
2950
2965
  Modal component.
@@ -3073,16 +3088,6 @@ Skeleton component.
3073
3088
  const Skeleton: React.ForwardRefExoticComponent<SkeletonProps & React.RefAttributes<HTMLDivElement>>
3074
3089
  ```
3075
3090
 
3076
- #### `SkeletonCircle`
3077
-
3078
- Skeleton circle (avatar placeholder).
3079
-
3080
- ```typescript
3081
- const SkeletonCircle: React.ForwardRefExoticComponent<
3082
- { size?: number; className?: string } & React.RefAttributes<HTMLDivElement>
3083
- >
3084
- ```
3085
-
3086
3091
  #### `SkeletonText`
3087
3092
 
3088
3093
  Skeleton text line.
@@ -3093,12 +3098,14 @@ const SkeletonText: React.ForwardRefExoticComponent<
3093
3098
  >
3094
3099
  ```
3095
3100
 
3096
- #### `Spacer`
3101
+ #### `SkeletonCircle`
3097
3102
 
3098
- Spacer component.
3103
+ Skeleton circle (avatar placeholder).
3099
3104
 
3100
3105
  ```typescript
3101
- const Spacer: React.ForwardRefExoticComponent<SpacerProps & React.RefAttributes<HTMLDivElement>>
3106
+ const SkeletonCircle: React.ForwardRefExoticComponent<
3107
+ { size?: number; className?: string } & React.RefAttributes<HTMLDivElement>
3108
+ >
3102
3109
  ```
3103
3110
 
3104
3111
  #### `Spinner`
@@ -3176,6 +3183,8 @@ const ToastContainer: React.ForwardRefExoticComponent<
3176
3183
  >
3177
3184
  ```
3178
3185
 
3186
+ **Returns:** The rendered toast container element.
3187
+
3179
3188
  #### `Tooltip`
3180
3189
 
3181
3190
  Tooltip component.