@moldable-ai/ui 0.2.29 → 0.2.31

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 (181) hide show
  1. package/dist/catalog/component-catalog.d.ts +9 -2
  2. package/dist/catalog/component-catalog.d.ts.map +1 -1
  3. package/dist/catalog/component-catalog.js +11 -7
  4. package/dist/catalog/core-control-stories.d.ts.map +1 -1
  5. package/dist/catalog/core-control-stories.js +1 -1
  6. package/dist/catalog/core-foundation-stories.d.ts.map +1 -1
  7. package/dist/catalog/core-foundation-stories.js +5 -5
  8. package/dist/components/ui/app-frame.d.ts +20 -1
  9. package/dist/components/ui/app-frame.d.ts.map +1 -1
  10. package/dist/components/ui/app-frame.js +37 -7
  11. package/dist/components/ui/badge.d.ts +1 -1
  12. package/dist/components/ui/badge.js +1 -1
  13. package/dist/components/ui/button.d.ts +5 -3
  14. package/dist/components/ui/button.d.ts.map +1 -1
  15. package/dist/components/ui/button.js +39 -11
  16. package/dist/components/ui/card.d.ts +12 -2
  17. package/dist/components/ui/card.d.ts.map +1 -1
  18. package/dist/components/ui/card.js +3 -2
  19. package/dist/components/ui/checkbox.d.ts +4 -1
  20. package/dist/components/ui/checkbox.d.ts.map +1 -1
  21. package/dist/components/ui/checkbox.js +4 -4
  22. package/dist/components/ui/context-menu.js +6 -6
  23. package/dist/components/ui/dropdown-menu.js +6 -6
  24. package/dist/components/ui/icon-button.d.ts +1 -1
  25. package/dist/components/ui/icon-button.d.ts.map +1 -1
  26. package/dist/components/ui/icon-button.js +1 -0
  27. package/dist/components/ui/input.d.ts +6 -2
  28. package/dist/components/ui/input.d.ts.map +1 -1
  29. package/dist/components/ui/input.js +2 -2
  30. package/dist/components/ui/material.d.ts +8 -3
  31. package/dist/components/ui/material.d.ts.map +1 -1
  32. package/dist/components/ui/material.js +2 -2
  33. package/dist/components/ui/search-field.d.ts +3 -1
  34. package/dist/components/ui/search-field.d.ts.map +1 -1
  35. package/dist/components/ui/search-field.js +16 -6
  36. package/dist/components/ui/segmented-control.d.ts +3 -1
  37. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  38. package/dist/components/ui/segmented-control.js +23 -17
  39. package/dist/components/ui/select.js +2 -2
  40. package/dist/components/ui/sidebar.d.ts +5 -3
  41. package/dist/components/ui/sidebar.d.ts.map +1 -1
  42. package/dist/components/ui/sidebar.js +53 -15
  43. package/dist/components/ui/status.d.ts +1 -1
  44. package/dist/components/ui/status.js +1 -1
  45. package/dist/components/ui/switch.d.ts +3 -1
  46. package/dist/components/ui/switch.d.ts.map +1 -1
  47. package/dist/components/ui/switch.js +3 -2
  48. package/dist/components/ui/toolbar-controls.d.ts +3 -1
  49. package/dist/components/ui/toolbar-controls.d.ts.map +1 -1
  50. package/dist/components/ui/toolbar-controls.js +14 -7
  51. package/dist/components/ui/toolbar.d.ts +5 -4
  52. package/dist/components/ui/toolbar.d.ts.map +1 -1
  53. package/dist/components/ui/toolbar.js +3 -3
  54. package/dist/index.d.ts +2 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +3 -1
  57. package/dist/lib/frame-lifecycle.d.ts +1 -0
  58. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  59. package/dist/lib/frame-lifecycle.js +7 -0
  60. package/dist/lib/theme.d.ts +39 -5
  61. package/dist/lib/theme.d.ts.map +1 -1
  62. package/dist/lib/theme.js +105 -17
  63. package/dist/tokens/contracts.d.ts +79 -0
  64. package/dist/tokens/contracts.d.ts.map +1 -0
  65. package/dist/tokens/contracts.js +57 -0
  66. package/dist/tokens/index.d.ts +2 -0
  67. package/dist/tokens/index.d.ts.map +1 -0
  68. package/dist/tokens/index.js +1 -0
  69. package/docs/README.md +2 -0
  70. package/docs/adaptive-materials.md +14 -9
  71. package/docs/design-tokens.md +10 -6
  72. package/docs/foundations.md +3 -3
  73. package/docs/macos-design-audit.md +144 -0
  74. package/docs/native-integration.md +4 -4
  75. package/docs/platform-architecture.md +189 -0
  76. package/docs/standalone-app-windows.md +37 -0
  77. package/docs/visual-regression.md +6 -8
  78. package/package.json +13 -1
  79. package/src/components/ui/accordion.md +46 -0
  80. package/src/components/ui/alert.md +12 -4
  81. package/src/components/ui/app-frame.md +8 -5
  82. package/src/components/ui/aspect-ratio.md +37 -0
  83. package/src/components/ui/avatar.md +48 -0
  84. package/src/components/ui/breadcrumb.md +52 -0
  85. package/src/components/ui/button-group.md +43 -0
  86. package/src/components/ui/button.md +20 -4
  87. package/src/components/ui/calendar.md +42 -0
  88. package/src/components/ui/card.md +43 -0
  89. package/src/components/ui/carousel.md +47 -0
  90. package/src/components/ui/chart.md +52 -0
  91. package/src/components/ui/checkbox.md +36 -0
  92. package/src/components/ui/collapsible.md +2 -3
  93. package/src/components/ui/color-well.md +14 -4
  94. package/src/components/ui/combobox.md +3 -3
  95. package/src/components/ui/confirm-dialog.md +5 -4
  96. package/src/components/ui/context-menu.md +3 -2
  97. package/src/components/ui/date-field.md +6 -1
  98. package/src/components/ui/date-picker.md +6 -3
  99. package/src/components/ui/dialog.md +14 -12
  100. package/src/components/ui/drawer.md +58 -0
  101. package/src/components/ui/error-boundary-view.md +1 -1
  102. package/src/components/ui/field.md +2 -1
  103. package/src/components/ui/form.md +71 -0
  104. package/src/components/ui/grid.md +4 -3
  105. package/src/components/ui/icon-button.md +4 -1
  106. package/src/components/ui/input-group.md +49 -0
  107. package/src/components/ui/input-otp.md +56 -0
  108. package/src/components/ui/input.md +40 -0
  109. package/src/components/ui/inspector.md +47 -7
  110. package/src/components/ui/item.md +5 -3
  111. package/src/components/ui/kbd.md +30 -0
  112. package/src/components/ui/label.md +29 -0
  113. package/src/components/ui/list.md +2 -2
  114. package/src/components/ui/material.md +25 -12
  115. package/src/components/ui/menu-items.md +60 -0
  116. package/src/components/ui/menubar.md +58 -0
  117. package/src/components/ui/message-scroller-primitive.md +70 -0
  118. package/src/components/ui/message-scroller.md +86 -0
  119. package/src/components/ui/navigation-button-group.md +4 -3
  120. package/src/components/ui/navigation-menu.md +52 -0
  121. package/src/components/ui/number-input.md +8 -0
  122. package/src/components/ui/pagination.md +51 -0
  123. package/src/components/ui/panel.md +6 -0
  124. package/src/components/ui/popover.md +10 -7
  125. package/src/components/ui/progress.md +31 -0
  126. package/src/components/ui/radio-group.md +36 -0
  127. package/src/components/ui/resizable.md +2 -1
  128. package/src/components/ui/scroll-area.md +3 -1
  129. package/src/components/ui/search-field.md +6 -1
  130. package/src/components/ui/segmented-control.md +6 -1
  131. package/src/components/ui/separator.md +35 -0
  132. package/src/components/ui/sidebar.md +20 -3
  133. package/src/components/ui/skeleton.md +36 -0
  134. package/src/components/ui/slider.md +58 -0
  135. package/src/components/ui/spinner.md +41 -0
  136. package/src/components/ui/split-view.md +8 -4
  137. package/src/components/ui/switch.md +44 -0
  138. package/src/components/ui/table.md +7 -7
  139. package/src/components/ui/tabs.md +3 -1
  140. package/src/components/ui/textarea.md +41 -0
  141. package/src/components/ui/toggle-group.md +2 -2
  142. package/src/components/ui/toggle.md +9 -9
  143. package/src/components/ui/toolbar-controls.md +17 -9
  144. package/src/components/ui/toolbar.md +6 -6
  145. package/src/styles/index.css +171 -117
  146. package/src/tokens/contracts.ts +131 -0
  147. package/src/tokens/index.css +6 -0
  148. package/src/tokens/index.ts +23 -0
  149. package/src/tokens/macos-26/README.md +37 -0
  150. package/src/tokens/macos-26/aliases.css +53 -0
  151. package/src/tokens/macos-26/compatibility.css +7 -0
  152. package/src/tokens/macos-26/index.css +3 -0
  153. package/src/tokens/macos-26/source/provenance.generated.json +28 -0
  154. package/src/tokens/macos-26/source/sidebar.generated.css +29 -0
  155. package/src/tokens/macos-26/source.css +7 -0
  156. package/src/tokens/macos-26/web-adaptations.json +25 -0
  157. package/src/tokens/macos-26/web-adaptations.md +15 -0
  158. package/src/tokens/macos-27/accessibility.css +11 -0
  159. package/src/tokens/macos-27/aliases.css +304 -0
  160. package/src/tokens/macos-27/compatibility.css +212 -0
  161. package/src/tokens/macos-27/index.css +15 -0
  162. package/src/tokens/macos-27/recipes.css +23 -0
  163. package/src/tokens/macos-27/source/chrome.generated.css +338 -0
  164. package/src/tokens/macos-27/source/colors.generated.css +140 -0
  165. package/src/tokens/macos-27/source/context.generated.css +18 -0
  166. package/src/tokens/macos-27/source/controls.generated.css +764 -0
  167. package/src/tokens/macos-27/source/effects.generated.css +68 -0
  168. package/src/tokens/macos-27/source/kit.generated.css +50 -0
  169. package/src/tokens/macos-27/source/overlays-status.generated.css +931 -0
  170. package/src/tokens/macos-27/source/paint-styles.generated.css +81 -0
  171. package/src/tokens/macos-27/source/provenance.generated.json +62 -0
  172. package/src/tokens/macos-27/source/sizes.generated.css +385 -0
  173. package/src/tokens/macos-27/source/source.generated.json +554 -0
  174. package/src/tokens/macos-27/source/text-styles.generated.css +116 -0
  175. package/src/tokens/macos-27/source.css +17 -0
  176. package/src/tokens/macos-27/web-adaptations.css +98 -0
  177. package/src/tokens/macos-27/web-adaptations.json +163 -0
  178. package/src/tokens/macos-27/web-adaptations.md +117 -0
  179. package/src/tokens/moldable/brand.css +48 -0
  180. package/src/tokens/moldable/index.css +1 -0
  181. package/src/tokens/platform-contract.test.ts +290 -0
@@ -44,9 +44,12 @@ focus returns to the trigger.
44
44
 
45
45
  ## Platform behavior
46
46
 
47
- This component is portable and uses no host API. A future native date or time
48
- control must be introduced through a shared host capability with this
49
- renderer-owned control as its fallback.
47
+ This component is renderer-owned and does not call the host. A call site that
48
+ deliberately prefers the host picker can use `useHostDateTimePicker()` or
49
+ `pickDateTimeWithHost('date', ...)`, then keep `DatePicker` as its portable
50
+ fallback. A `status: 'fallback'` result does not open renderer UI for the
51
+ caller. See
52
+ [`host-date-time-picker.md`](../../lib/host-date-time-picker.md).
50
53
 
51
54
  ## Don't
52
55
 
@@ -41,19 +41,21 @@ temporary control overlays where seeing the underlying context is useful.
41
41
  Use `DialogAction` for footer commands that need the standard async and keyboard
42
42
  contracts. Its `loading` prop forwards to `Button`, preventing duplicate
43
43
  activation while retaining the label width and accessible name. Keep the dialog
44
- controlled, show inline field errors, and close only after success. Disable the
45
- whole form only when edits during submission would be unsafe. Use
46
- `ConfirmDialog` when confirmation is the entire workflow.
44
+ controlled when an async result must own closure, show inline field errors, and
45
+ close only after success. Disable the whole form only when edits during
46
+ submission would be unsafe. Use `ConfirmDialog` when confirmation is the entire
47
+ workflow.
47
48
 
48
49
  Set `primary` on at most one `DialogAction` to opt into the default-action
49
- shortcut. Command+Enter activates it on macOS; Control+Enter does so on other
50
- platforms. The shortcut works from anywhere in the dialog unless a focused
51
- control consumes the key event with `preventDefault()`, or the primary action is
52
- loading or disabled. Plain Enter keeps the focused control's native behavior.
53
- Use `Kbd` to show the shortcut when it improves discovery; render the
54
- platform-appropriate modifier in app code.
50
+ shortcut. Command+Enter activates it on macOS and iOS-family runtimes;
51
+ Control+Enter does so elsewhere. The shortcut works from anywhere in the dialog
52
+ unless a focused control consumes the key event with `preventDefault()`, or the
53
+ primary action is loading or disabled. Plain Enter keeps the focused control's
54
+ native behavior. Use `Kbd` to show the shortcut when it improves discovery;
55
+ render the platform-appropriate modifier in app code.
55
56
 
56
57
  Every dialog needs a `DialogTitle`. Add `DialogDescription` when the outcome or
57
- scope is not obvious. Escape, outside dismissal, initial focus, and focus return
58
- come from the Radix primitive; do not replace them with manual document event
59
- handlers.
58
+ scope is not obvious. `DialogContent` focuses its own container on open by
59
+ default; pass `onOpenAutoFocus` for a deliberate alternative. Escape, outside
60
+ dismissal, and focus return come from the Radix primitive; do not replace them
61
+ with manual document event handlers.
@@ -0,0 +1,58 @@
1
+ # Drawer
2
+
3
+ ## Purpose
4
+
5
+ `Drawer` presents a modal surface that enters from a screen edge and can be
6
+ dismissed by dragging, pressing Escape, or using a close action. It wraps Vaul
7
+ and defaults to the bottom edge.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import {
13
+ Button,
14
+ Drawer,
15
+ DrawerClose,
16
+ DrawerContent,
17
+ DrawerDescription,
18
+ DrawerFooter,
19
+ DrawerHeader,
20
+ DrawerTitle,
21
+ DrawerTrigger,
22
+ } from '@moldable-ai/ui'
23
+
24
+ ;<Drawer>
25
+ <DrawerTrigger asChild>
26
+ <Button>Open upload queue</Button>
27
+ </DrawerTrigger>
28
+ <DrawerContent>
29
+ <DrawerHeader>
30
+ <DrawerTitle>Upload queue</DrawerTitle>
31
+ <DrawerDescription>Two files are ready to sync.</DrawerDescription>
32
+ </DrawerHeader>
33
+ <DrawerFooter>
34
+ <Button>Upload all</Button>
35
+ <DrawerClose asChild>
36
+ <Button variant="outline">Cancel</Button>
37
+ </DrawerClose>
38
+ </DrawerFooter>
39
+ </DrawerContent>
40
+ </Drawer>
41
+ ```
42
+
43
+ `Drawer` accepts the Vaul root props, including controlled `open` and
44
+ `onOpenChange`, `direction="top" | "bottom" | "left" | "right"`, snap points,
45
+ and dismissal options. `DrawerContent` supplies the portal and overlay. Bottom
46
+ drawers also show the built-in drag indicator.
47
+
48
+ ## Quality bar
49
+
50
+ - Always include `DrawerTitle`; add `DrawerDescription` when the title does not
51
+ fully explain the task.
52
+ - Keep the primary action and an explicit close action in `DrawerFooter`.
53
+ - Use `asChild` when a shared button should be the trigger or close control.
54
+ - Keep long drawer bodies independently scrollable and preserve focus
55
+ management from the primitive.
56
+ - Use `Dialog` for a centered decision and `Sheet` for a persistent side panel.
57
+ - Do not disable outside-click or Escape dismissal without another obvious way
58
+ to close the drawer.
@@ -25,7 +25,7 @@ nested cards.
25
25
  ## Usage
26
26
 
27
27
  ```tsx
28
- import { ErrorBoundaryView } from '@moldable-ai/ui'
28
+ import { Button, ErrorBoundaryView } from '@moldable-ai/ui'
29
29
 
30
30
  function PaneError() {
31
31
  return (
@@ -58,7 +58,8 @@ Use `orientation="horizontal"` for compact checkbox/radio rows and `orientation=
58
58
  - Give every control a visible label or an accessible name.
59
59
  - Connect labels and descriptions with `htmlFor`, `id`, and `aria-describedby`.
60
60
  - Set both `data-invalid` on `Field` and `aria-invalid` on the control.
61
- - Use real `disabled` state and reflect it on the field group when styling needs it.
61
+ - Use real `disabled` state and set `data-disabled="true"` on `Field` when its
62
+ label or title should also appear disabled.
62
63
  - Place validation directly after the affected control.
63
64
  - Group related checkbox or radio controls in `FieldSet` with `FieldLegend`.
64
65
 
@@ -0,0 +1,71 @@
1
+ # Form
2
+
3
+ ## Purpose
4
+
5
+ The form adapter connects React Hook Form state to labels, controls,
6
+ descriptions, and validation messages. `Form` is `FormProvider`; the native
7
+ `form` element remains part of the composition.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import { useForm } from 'react-hook-form'
13
+ import {
14
+ Button,
15
+ Form,
16
+ FormControl,
17
+ FormDescription,
18
+ FormField,
19
+ FormItem,
20
+ FormLabel,
21
+ FormMessage,
22
+ Input,
23
+ } from '@moldable-ai/ui'
24
+
25
+ type Values = { project: string }
26
+
27
+ function ProjectForm() {
28
+ const form = useForm<Values>({ defaultValues: { project: '' } })
29
+
30
+ return (
31
+ <Form {...form}>
32
+ <form onSubmit={form.handleSubmit(saveProject)}>
33
+ <FormField
34
+ control={form.control}
35
+ name="project"
36
+ rules={{ required: 'Enter a project name.' }}
37
+ render={({ field }) => (
38
+ <FormItem>
39
+ <FormLabel>Project name</FormLabel>
40
+ <FormControl>
41
+ <Input {...field} />
42
+ </FormControl>
43
+ <FormDescription>Shown in the app titlebar.</FormDescription>
44
+ <FormMessage />
45
+ </FormItem>
46
+ )}
47
+ />
48
+ <Button type="submit">Save</Button>
49
+ </form>
50
+ </Form>
51
+ )
52
+ }
53
+ ```
54
+
55
+ `FormField` forwards React Hook Form `Controller` props. Within its
56
+ `FormItem`, `FormLabel` targets the slotted `FormControl`; the control receives
57
+ `id`, `aria-invalid`, and description/message references. `FormMessage` shows
58
+ the current field error message, falls back to its children, and renders
59
+ nothing when both are absent. `useFormField` exposes the generated IDs and
60
+ field state to custom parts.
61
+
62
+ ## Quality bar
63
+
64
+ - Put `FormControl`, `FormLabel`, `FormDescription`, and `FormMessage` inside
65
+ the same `FormField` and `FormItem`.
66
+ - Give `FormControl` one interactive child that can accept the injected
67
+ accessibility props.
68
+ - Keep validation messages specific and place them beside the affected field.
69
+ - Use a native `form` with `onSubmit`; `Form` alone does not submit anything.
70
+ - Prefer the simpler `Field` primitives when React Hook Form state is not
71
+ needed.
@@ -61,9 +61,10 @@ Prefer changing this value at deliberate window breakpoints. A smaller window sh
61
61
  - Home and End move to the first and last available tile in the current row.
62
62
  - Control/Command + Home and End move to the first and last available tile in
63
63
  the entire grid.
64
- - Space changes selection.
65
- - Enter changes selection and invokes `onAction`.
66
- - A single pointer click changes selection. A double click invokes `onAction`.
64
+ - Space changes selection when selection is enabled.
65
+ - Enter changes selection when enabled and invokes `onAction`.
66
+ - A single pointer click changes selection when selection is enabled. A double
67
+ click invokes `onAction`.
67
68
  - Disabled tiles are skipped.
68
69
  - Buttons and links inside a tile preserve their own pointer behavior.
69
70
  - When the grid first mounts, the first selected, enabled tile receives the
@@ -22,7 +22,10 @@ import { IconButton } from '@moldable-ai/ui'
22
22
  </IconButton>
23
23
  ```
24
24
 
25
- `size` is `xs`, `sm`, `default`, or `lg`. `shape` is `rounded` or `circle`. `loading` disables the control, exposes `aria-busy`, and replaces the icon with a progress indicator. `tooltip` may be `true` to reuse the label as a native hint or a specific string.
25
+ `size` is `xs`, `sm`, `default`, `lg`, or `xl`. `shape` is `rounded` or
26
+ `circle`. `loading` disables the control, exposes `aria-busy`, and replaces the
27
+ icon with a progress indicator. `tooltip` may be `true` to reuse the label as
28
+ a native hint or a specific string.
26
29
 
27
30
  ## Quality bar
28
31
 
@@ -0,0 +1,49 @@
1
+ # Input Group
2
+
3
+ ## Purpose
4
+
5
+ `InputGroup` combines one text control with inline or stacked context and
6
+ actions inside a single bordered surface.
7
+
8
+ ## Usage
9
+
10
+ ```tsx
11
+ import {
12
+ InputGroup,
13
+ InputGroupAddon,
14
+ InputGroupButton,
15
+ InputGroupInput,
16
+ InputGroupText,
17
+ } from '@moldable-ai/ui'
18
+
19
+ ;<InputGroup>
20
+ <InputGroupAddon>
21
+ <InputGroupText>https://</InputGroupText>
22
+ </InputGroupAddon>
23
+ <InputGroupInput aria-label="Workspace address" />
24
+ <InputGroupAddon align="inline-end">
25
+ <InputGroupButton aria-label="Copy address">Copy</InputGroupButton>
26
+ </InputGroupAddon>
27
+ </InputGroup>
28
+ ```
29
+
30
+ Use `InputGroupInput` for a native input or `InputGroupTextarea` for multiline
31
+ text. `InputGroupAddon` defaults to `inline-start` and also supports
32
+ `inline-end`, `block-start`, and `block-end`. Clicking a non-button inline
33
+ addon focuses the contained input. `InputGroupButton` defaults to
34
+ `type="button"`, `variant="ghost"`, and `size="xs"`; its sizes are `xs`, `sm`,
35
+ `icon-xs`, and `icon-sm`.
36
+
37
+ ## Quality bar
38
+
39
+ - Label the actual input or textarea; the group and its adornments do not
40
+ provide its accessible name.
41
+ - Mark decorative icons `aria-hidden` and give icon-only buttons an
42
+ `aria-label`.
43
+ - Put interactive controls in `InputGroupButton`, not in a clickable addon.
44
+ - Set `aria-invalid` on the input or textarea so the group shows its error
45
+ state.
46
+ - When disabling the control, add `data-disabled="true"` to `InputGroup` if
47
+ its addons should also appear disabled.
48
+ - Keep addons short. Use `block-start` or `block-end` for context that cannot
49
+ fit inline.
@@ -0,0 +1,56 @@
1
+ # Input OTP
2
+
3
+ ## Purpose
4
+
5
+ `InputOTP` presents a one-time code as visual slots while retaining one
6
+ keyboard- and paste-friendly input through the `input-otp` primitive.
7
+
8
+ ## Usage
9
+
10
+ ```tsx
11
+ import {
12
+ InputOTP,
13
+ InputOTPGroup,
14
+ InputOTPSeparator,
15
+ InputOTPSlot,
16
+ } from '@moldable-ai/ui'
17
+
18
+ ;<InputOTP
19
+ aria-label="Verification code"
20
+ maxLength={6}
21
+ value={code}
22
+ onChange={setCode}
23
+ >
24
+ <InputOTPGroup>
25
+ {[0, 1, 2].map((index) => (
26
+ <InputOTPSlot key={index} index={index} />
27
+ ))}
28
+ </InputOTPGroup>
29
+ <InputOTPSeparator />
30
+ <InputOTPGroup>
31
+ {[3, 4, 5].map((index) => (
32
+ <InputOTPSlot key={index} index={index} />
33
+ ))}
34
+ </InputOTPGroup>
35
+ </InputOTP>
36
+ ```
37
+
38
+ `maxLength` defines the code length and the rendered slot indexes must match
39
+ it. Native input props and `input-otp` options such as `pattern`,
40
+ `pasteTransformer`, `onComplete`, and `pushPasswordManagerStrategy` pass
41
+ through. `containerClassName` styles the visible slot container; `className`
42
+ targets the underlying input. `InputOTPSeparator` renders a separator role
43
+ with the shared minus icon.
44
+
45
+ ## Quality bar
46
+
47
+ - Give the root a visible label or an accessible name.
48
+ - Match every `InputOTPSlot` index to a valid position below `maxLength`.
49
+ - Use `pattern` when the code format is restricted; do not reject valid pasted
50
+ codes silently.
51
+ - Let users paste and use password managers or system code autofill; add an
52
+ appropriate `autoComplete` value for the workflow.
53
+ - Report expiration, resend status, and server errors outside the slots in
54
+ text, not by color alone.
55
+ - Do not submit solely because the last character was entered unless the
56
+ result is safe, reversible, and clearly communicated.
@@ -0,0 +1,40 @@
1
+ # Input
2
+
3
+ ## Purpose
4
+
5
+ `Input` is the shared styled native input. It preserves native input types,
6
+ editing behavior, constraints, and form participation while applying semantic
7
+ control tokens.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import { Input, Label } from '@moldable-ai/ui'
13
+
14
+ ;<div className="grid gap-2">
15
+ <Label htmlFor="workspace-name">Workspace name</Label>
16
+ <Input
17
+ id="workspace-name"
18
+ name="workspaceName"
19
+ autoComplete="organization"
20
+ required
21
+ />
22
+ </div>
23
+ ```
24
+
25
+ All native input props pass through. `controlSize` accepts `mini`, `small`,
26
+ `medium`, `large`, or `xl`; when omitted, the input inherits the active theme
27
+ size. File inputs receive matching typography and file-button treatment.
28
+
29
+ ## Quality bar
30
+
31
+ - Associate every input with a visible label or an accessible name.
32
+ - Choose the correct native `type`, `name`, `autoComplete`, `inputMode`, and
33
+ constraints for the data.
34
+ - Set `aria-invalid` when invalid and connect supporting or error text with
35
+ `aria-describedby`.
36
+ - Use `disabled` only for unavailable controls; use `readOnly` for values that
37
+ should remain selectable.
38
+ - Keep placeholder text as a short hint, never as the only label.
39
+ - Use `Textarea`, `SearchField`, `DateField`, or `InputGroup` when their
40
+ behavior matches the task more closely.
@@ -1,11 +1,51 @@
1
1
  # Inspector
2
2
 
3
- Secondary property controls for the current selection.
3
+ ## Purpose
4
4
 
5
- Compose `Inspector`, `InspectorHeader`, `InspectorContent`,
6
- `InspectorSection`, and aligned `InspectorRow` pairs. Controls should usually
7
- use compact sizes.
5
+ The inspector primitives compose a secondary property pane for the current
6
+ selection. The root is an `aside`; its header stays fixed while
7
+ `InspectorContent` scrolls and includes bottom chat clearance.
8
8
 
9
- The inspector must remain subordinate to the primary pane. Collapse it when
10
- width is constrained, group related properties, and avoid placing primary
11
- workflow actions inside it.
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import {
13
+ Input,
14
+ Inspector,
15
+ InspectorContent,
16
+ InspectorHeader,
17
+ InspectorLabel,
18
+ InspectorRow,
19
+ InspectorSection,
20
+ InspectorValue,
21
+ } from '@moldable-ai/ui'
22
+
23
+ ;<Inspector>
24
+ <InspectorHeader>Inspector</InspectorHeader>
25
+ <InspectorContent>
26
+ <InspectorSection title="Appearance">
27
+ <InspectorRow>
28
+ <InspectorLabel htmlFor="document-title">Title</InspectorLabel>
29
+ <InspectorValue>
30
+ <Input id="document-title" controlSize="small" />
31
+ </InspectorValue>
32
+ </InspectorRow>
33
+ </InspectorSection>
34
+ </InspectorContent>
35
+ </Inspector>
36
+ ```
37
+
38
+ `InspectorSection` accepts optional `title` and `actions`; it omits its header
39
+ when neither is present. `InspectorRow` aligns a label and value in two
40
+ columns. `InspectorLabel` renders a compact native label, and
41
+ `InspectorValue` contains the control or read-only value.
42
+
43
+ ## Quality bar
44
+
45
+ - Keep the inspector subordinate to the primary pane and collapse it when
46
+ width is constrained.
47
+ - Group related properties into short sections and use section actions only
48
+ for local operations.
49
+ - Connect each `InspectorLabel` to its control with `htmlFor` and `id`.
50
+ - Use compact control sizes and keep unpredictable values truncatable.
51
+ - Keep primary workflow actions out of the inspector.
@@ -23,7 +23,7 @@ import {
23
23
  } from '@moldable-ai/ui'
24
24
 
25
25
  ;<ItemGroup>
26
- <Item size="sm">
26
+ <Item role="listitem" size="sm">
27
27
  <ItemMedia variant="icon">
28
28
  <FileText />
29
29
  </ItemMedia>
@@ -53,8 +53,10 @@ Use `asChild` when the whole item is a link or another interactive primitive. Av
53
53
 
54
54
  ## API summary
55
55
 
56
- - `ItemGroup`: `role="list"` container.
57
- - `Item`: `variant="default"`, `"outline"`, or `"muted"`; `size="default"` or `"sm"`; optional `asChild`.
56
+ - `ItemGroup`: `role="list"` container. Give each direct `Item`
57
+ `role="listitem"`.
58
+ - `Item`: `variant="default"`, `"outline"`, or `"muted"`;
59
+ `size="default"` or `"sm"`; optional `asChild`.
58
60
  - `ItemMedia`: `variant="default"`, `"icon"`, or `"image"`.
59
61
  - `ItemContent`: flexible title/description column.
60
62
  - `ItemTitle`, `ItemDescription`: primary and secondary text.
@@ -0,0 +1,30 @@
1
+ # Kbd
2
+
3
+ ## Purpose
4
+
5
+ `Kbd` displays a keyboard key or shortcut hint. `KbdGroup` lays out several
6
+ keys as one shortcut. These components are visual labels only; they do not
7
+ register keyboard handlers.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import { Kbd, KbdGroup } from '@moldable-ai/ui'
13
+
14
+ ;<KbdGroup aria-label="Command K">
15
+ <Kbd>⌘</Kbd>
16
+ <Kbd>K</Kbd>
17
+ </KbdGroup>
18
+ ```
19
+
20
+ Both components accept native `<kbd>` props and `className`. `Kbd` applies the
21
+ keycap treatment and sizes unclassified SVG children; `KbdGroup` supplies only
22
+ an inline flex layout.
23
+
24
+ ## Guidance
25
+
26
+ - Show shortcuts only when the command really supports them.
27
+ - Keep key names brief and use the same notation throughout the app.
28
+ - Add an accessible label when modifier glyphs may not be announced clearly.
29
+ - Keep `Kbd` non-interactive; place click behavior on a real button or link.
30
+ - Do not use shortcut hints as the only way to discover an action.
@@ -0,0 +1,29 @@
1
+ # Label
2
+
3
+ ## Purpose
4
+
5
+ `Label` gives a form control a visible, clickable name. It wraps the Radix
6
+ label primitive and accepts its props.
7
+
8
+ ## Usage
9
+
10
+ ```tsx
11
+ import { Input, Label } from '@moldable-ai/ui'
12
+
13
+ ;<div className="space-y-2">
14
+ <Label htmlFor="project-name">Project name</Label>
15
+ <Input id="project-name" name="projectName" />
16
+ </div>
17
+ ```
18
+
19
+ ## Guidance
20
+
21
+ - Pair `htmlFor` with a unique control `id`, or nest the control when that is
22
+ the clearer native relationship.
23
+ - Use a short noun phrase and keep instructions or validation outside the
24
+ label.
25
+ - Keep a visible label for text fields; a placeholder is not a replacement.
26
+ - Mark optional or required state consistently and expose it to assistive
27
+ technology on the control.
28
+ - The built-in styles respond to disabled peer or group state; set the actual
29
+ control's `disabled` attribute as well.
@@ -81,8 +81,8 @@ const [selected, setSelected] = useState<ReadonlySet<string>>(
81
81
 
82
82
  - Arrow Up/Down moves through a vertical list; Arrow Left/Right moves through a horizontal list.
83
83
  - Home and End move to the first and last available item.
84
- - Space changes selection.
85
- - Enter changes selection and invokes `onAction`.
84
+ - Space changes selection when selection is enabled.
85
+ - Enter changes selection when enabled and invokes `onAction`.
86
86
  - A single pointer click changes selection. A double click invokes `onAction`.
87
87
  - Disabled items are skipped during keyboard navigation and cannot be selected or activated.
88
88
  - Interactive descendants such as buttons and links keep their own pointer behavior. They do not select or activate the containing item.
@@ -1,13 +1,14 @@
1
1
  # Material
2
2
 
3
- Adaptive material for navigation and control chrome.
3
+ `Material` provides an adaptive sampled layer for navigation and control
4
+ chrome. `MaterialGroup` gives a related control cluster one shared layer.
4
5
 
5
6
  ```tsx
6
- <Material asChild variant="regular" elevation="none">
7
+ <Material asChild variant="medium" elevation="none">
7
8
  <Toolbar>{/* context and actions */}</Toolbar>
8
9
  </Material>
9
10
 
10
- <MaterialGroup variant="regular" shape="capsule" spacing="compact">
11
+ <MaterialGroup variant="thin" shape="capsule" spacing="compact">
11
12
  <IconButton label="Previous" variant="ghost">
12
13
  <ChevronLeft />
13
14
  </IconButton>
@@ -19,15 +20,25 @@ Adaptive material for navigation and control chrome.
19
20
 
20
21
  ## Variants
21
22
 
22
- - `regular` provides a frosted, legible control or navigation layer.
23
- - `clear` is less frosted and is only for compact controls floating over
24
- photos, maps, video, or another richly colored canvas.
23
+ - Standard thicknesses are `ultra-thick`, `thick`, `medium`, `thin`, and
24
+ `ultra-thin`, ordered from most filled to most transparent. They all use the
25
+ standard material blur; thickness controls translucency.
26
+ - `regular` is a compatibility alias for `medium` and remains the default.
27
+ - `clear` is a compatibility alias for `ultra-thin`.
25
28
 
26
- Both variants start with an opaque fallback. Backdrop sampling is enabled only
27
- when the renderer supports it. Reduced transparency and forced colors keep the
28
- fallback opaque.
29
+ Every variant starts with an opaque fallback. Backdrop sampling is enabled only
30
+ when the renderer supports it.
29
31
 
30
- ## Grouping
32
+ ## Shape, elevation, and grouping
33
+
34
+ - `shape` is `rounded`, `capsule`, or `circle`; it defaults to `rounded`.
35
+ - `elevation` is `none`, `panel`, or `overlay`; it defaults to `none` and
36
+ controls shadow independently of thickness.
37
+ - `interactive` adds hover and press feedback when the material surface itself
38
+ is the control.
39
+ - `asChild` applies `Material` to its single child through a Radix slot.
40
+ - `MaterialGroup` supports `spacing="compact" | "default" | "comfortable"`
41
+ and defaults to `default`.
31
42
 
32
43
  `MaterialGroup` creates one sampled layer for a related cluster. Put ordinary
33
44
  ghost or transparent controls inside it. Do not wrap each child in another
@@ -42,8 +53,10 @@ do not read as one group.
42
53
  temporary control layers.
43
54
  - Keep documents, calendars, tables, forms, cards, sheets, and primary content
44
55
  canvases opaque.
45
- - Prefer `regular`. A `clear` surface needs stable contrast across every part
46
- of the media behind it.
56
+ - Prefer a named standard thickness when the hierarchy is known. Use the
57
+ compatibility aliases only where their existing semantics are intentional.
58
+ - Thin and ultra-thin surfaces need stable contrast across every part of the
59
+ content behind them.
47
60
  - Use `interactive` only when the material surface itself is the control.
48
61
  - Do not animate blur or saturation. Shared motion tokens provide restrained
49
62
  press feedback and remove it when reduced motion is requested.
@@ -0,0 +1,60 @@
1
+ # Menu Items
2
+
3
+ ## Purpose
4
+
5
+ `menu-items.tsx` is the internal adapter shared by `DropdownMenu` and
6
+ `ContextMenu` when their declarative `items` API is used. It coordinates the
7
+ host-native request and portable fallback, formats shortcut labels, renders
8
+ portable row contents, and removes conflicting authored content.
9
+
10
+ App code should use `DropdownMenu` or `ContextMenu`, not import these helpers
11
+ directly.
12
+
13
+ ## Declarative usage
14
+
15
+ ```tsx
16
+ import { Button, DropdownMenu, DropdownMenuTrigger } from '@moldable-ai/ui'
17
+
18
+ ;<DropdownMenu
19
+ items={[
20
+ {
21
+ id: 'rename',
22
+ label: 'Rename',
23
+ shortcut: { key: 'r', modifiers: ['cmd'] },
24
+ },
25
+ { id: 'divider', kind: 'separator' },
26
+ { id: 'delete', label: 'Move to trash', destructive: true },
27
+ ]}
28
+ onSelectItem={(id) => runCommand(id)}
29
+ >
30
+ <DropdownMenuTrigger asChild>
31
+ <Button
32
+ type="button"
33
+ variant="outline"
34
+ size="sm"
35
+ className="cursor-pointer"
36
+ >
37
+ Actions
38
+ </Button>
39
+ </DropdownMenuTrigger>
40
+ </DropdownMenu>
41
+ ```
42
+
43
+ ## Internal contract
44
+
45
+ - `useDeclarativeMenuState` supports controlled or uncontrolled portable-open
46
+ state. While a native request is active, `nativeOpen` is true and
47
+ `portableOpen` is forced false.
48
+ - A completed native request passes `selectedId` to `onSelectItem`.
49
+ Cancellation does nothing. A fallback result or request failure opens the
50
+ portable menu unless the caller supplies its own `onFallback`.
51
+ - `formatMenuShortcut` renders modifiers in array order as `⌘`, `⇧`, `⌥`, and
52
+ `⌃`, then uppercases a one-character key. It is display metadata only.
53
+ - `MenuItemContents` renders an optional Lucide icon, the label, and a `Kbd`
54
+ shortcut. Separators are handled by the owning menu.
55
+ - Content detection and removal recurse through fragments. When declarative
56
+ items and authored content are both provided, the owning menu reports the
57
+ conflict in development and declarative items win.
58
+
59
+ Keep item IDs stable and labels concise. Do not execute a command from shortcut
60
+ metadata, and do not mix declarative items with authored menu content.