@moldable-ai/ui 0.2.26 → 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 (186) 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 +15 -3
  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 -10
  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/date-field.d.ts +1 -1
  24. package/dist/components/ui/dropdown-menu.js +6 -6
  25. package/dist/components/ui/icon-button.d.ts +1 -1
  26. package/dist/components/ui/icon-button.d.ts.map +1 -1
  27. package/dist/components/ui/icon-button.js +1 -0
  28. package/dist/components/ui/index.d.ts +1 -0
  29. package/dist/components/ui/index.d.ts.map +1 -1
  30. package/dist/components/ui/index.js +1 -0
  31. package/dist/components/ui/input-group.d.ts +2 -2
  32. package/dist/components/ui/input.d.ts +6 -2
  33. package/dist/components/ui/input.d.ts.map +1 -1
  34. package/dist/components/ui/input.js +2 -2
  35. package/dist/components/ui/material.d.ts +8 -3
  36. package/dist/components/ui/material.d.ts.map +1 -1
  37. package/dist/components/ui/material.js +2 -2
  38. package/dist/components/ui/search-field.d.ts +3 -1
  39. package/dist/components/ui/search-field.d.ts.map +1 -1
  40. package/dist/components/ui/search-field.js +16 -6
  41. package/dist/components/ui/segmented-control.d.ts +3 -1
  42. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  43. package/dist/components/ui/segmented-control.js +23 -17
  44. package/dist/components/ui/select.js +2 -2
  45. package/dist/components/ui/sidebar.d.ts +5 -3
  46. package/dist/components/ui/sidebar.d.ts.map +1 -1
  47. package/dist/components/ui/sidebar.js +53 -15
  48. package/dist/components/ui/status.d.ts +1 -1
  49. package/dist/components/ui/status.js +1 -1
  50. package/dist/components/ui/switch.d.ts +3 -1
  51. package/dist/components/ui/switch.d.ts.map +1 -1
  52. package/dist/components/ui/switch.js +3 -2
  53. package/dist/components/ui/toolbar-controls.d.ts +49 -0
  54. package/dist/components/ui/toolbar-controls.d.ts.map +1 -0
  55. package/dist/components/ui/toolbar-controls.js +47 -0
  56. package/dist/components/ui/toolbar.d.ts +5 -4
  57. package/dist/components/ui/toolbar.d.ts.map +1 -1
  58. package/dist/components/ui/toolbar.js +3 -3
  59. package/dist/index.d.ts +2 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -1
  62. package/dist/lib/frame-lifecycle.d.ts +1 -0
  63. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  64. package/dist/lib/frame-lifecycle.js +7 -0
  65. package/dist/lib/theme.d.ts +39 -5
  66. package/dist/lib/theme.d.ts.map +1 -1
  67. package/dist/lib/theme.js +105 -17
  68. package/dist/tokens/contracts.d.ts +79 -0
  69. package/dist/tokens/contracts.d.ts.map +1 -0
  70. package/dist/tokens/contracts.js +57 -0
  71. package/dist/tokens/index.d.ts +2 -0
  72. package/dist/tokens/index.d.ts.map +1 -0
  73. package/dist/tokens/index.js +1 -0
  74. package/docs/README.md +2 -0
  75. package/docs/adaptive-materials.md +14 -9
  76. package/docs/design-tokens.md +10 -6
  77. package/docs/foundations.md +3 -3
  78. package/docs/macos-design-audit.md +144 -0
  79. package/docs/native-integration.md +4 -4
  80. package/docs/platform-architecture.md +189 -0
  81. package/docs/standalone-app-windows.md +37 -0
  82. package/docs/visual-regression.md +6 -8
  83. package/package.json +13 -1
  84. package/src/components/ui/accordion.md +46 -0
  85. package/src/components/ui/alert.md +12 -4
  86. package/src/components/ui/app-frame.md +8 -5
  87. package/src/components/ui/aspect-ratio.md +37 -0
  88. package/src/components/ui/avatar.md +48 -0
  89. package/src/components/ui/breadcrumb.md +52 -0
  90. package/src/components/ui/button-group.md +43 -0
  91. package/src/components/ui/button.md +20 -4
  92. package/src/components/ui/calendar.md +42 -0
  93. package/src/components/ui/card.md +43 -0
  94. package/src/components/ui/carousel.md +47 -0
  95. package/src/components/ui/chart.md +52 -0
  96. package/src/components/ui/checkbox.md +36 -0
  97. package/src/components/ui/collapsible.md +2 -3
  98. package/src/components/ui/color-well.md +14 -4
  99. package/src/components/ui/combobox.md +3 -3
  100. package/src/components/ui/confirm-dialog.md +5 -4
  101. package/src/components/ui/context-menu.md +3 -2
  102. package/src/components/ui/date-field.md +6 -1
  103. package/src/components/ui/date-picker.md +6 -3
  104. package/src/components/ui/dialog.md +14 -12
  105. package/src/components/ui/drawer.md +58 -0
  106. package/src/components/ui/error-boundary-view.md +1 -1
  107. package/src/components/ui/field.md +2 -1
  108. package/src/components/ui/form.md +71 -0
  109. package/src/components/ui/grid.md +4 -3
  110. package/src/components/ui/icon-button.md +4 -1
  111. package/src/components/ui/input-group.md +49 -0
  112. package/src/components/ui/input-otp.md +56 -0
  113. package/src/components/ui/input.md +40 -0
  114. package/src/components/ui/inspector.md +47 -7
  115. package/src/components/ui/item.md +5 -3
  116. package/src/components/ui/kbd.md +30 -0
  117. package/src/components/ui/label.md +29 -0
  118. package/src/components/ui/list.md +2 -2
  119. package/src/components/ui/material.md +25 -12
  120. package/src/components/ui/menu-items.md +60 -0
  121. package/src/components/ui/menubar.md +58 -0
  122. package/src/components/ui/message-scroller-primitive.md +70 -0
  123. package/src/components/ui/message-scroller.md +86 -0
  124. package/src/components/ui/navigation-button-group.md +4 -3
  125. package/src/components/ui/navigation-menu.md +52 -0
  126. package/src/components/ui/number-input.md +8 -0
  127. package/src/components/ui/pagination.md +51 -0
  128. package/src/components/ui/panel.md +6 -0
  129. package/src/components/ui/popover.md +10 -7
  130. package/src/components/ui/progress.md +31 -0
  131. package/src/components/ui/radio-group.md +36 -0
  132. package/src/components/ui/resizable.md +2 -1
  133. package/src/components/ui/scroll-area.md +3 -1
  134. package/src/components/ui/search-field.md +6 -1
  135. package/src/components/ui/segmented-control.md +6 -1
  136. package/src/components/ui/separator.md +35 -0
  137. package/src/components/ui/sidebar.md +20 -3
  138. package/src/components/ui/skeleton.md +36 -0
  139. package/src/components/ui/slider.md +58 -0
  140. package/src/components/ui/spinner.md +41 -0
  141. package/src/components/ui/split-view.md +8 -4
  142. package/src/components/ui/switch.md +44 -0
  143. package/src/components/ui/table.md +7 -7
  144. package/src/components/ui/tabs.md +3 -1
  145. package/src/components/ui/textarea.md +41 -0
  146. package/src/components/ui/toggle-group.md +2 -2
  147. package/src/components/ui/toggle.md +9 -9
  148. package/src/components/ui/toolbar-controls.md +83 -0
  149. package/src/components/ui/toolbar.md +12 -6
  150. package/src/styles/index.css +199 -107
  151. package/src/tokens/contracts.ts +131 -0
  152. package/src/tokens/index.css +6 -0
  153. package/src/tokens/index.ts +23 -0
  154. package/src/tokens/macos-26/README.md +37 -0
  155. package/src/tokens/macos-26/aliases.css +53 -0
  156. package/src/tokens/macos-26/compatibility.css +7 -0
  157. package/src/tokens/macos-26/index.css +3 -0
  158. package/src/tokens/macos-26/source/provenance.generated.json +28 -0
  159. package/src/tokens/macos-26/source/sidebar.generated.css +29 -0
  160. package/src/tokens/macos-26/source.css +7 -0
  161. package/src/tokens/macos-26/web-adaptations.json +25 -0
  162. package/src/tokens/macos-26/web-adaptations.md +15 -0
  163. package/src/tokens/macos-27/accessibility.css +11 -0
  164. package/src/tokens/macos-27/aliases.css +304 -0
  165. package/src/tokens/macos-27/compatibility.css +212 -0
  166. package/src/tokens/macos-27/index.css +15 -0
  167. package/src/tokens/macos-27/recipes.css +23 -0
  168. package/src/tokens/macos-27/source/chrome.generated.css +338 -0
  169. package/src/tokens/macos-27/source/colors.generated.css +140 -0
  170. package/src/tokens/macos-27/source/context.generated.css +18 -0
  171. package/src/tokens/macos-27/source/controls.generated.css +764 -0
  172. package/src/tokens/macos-27/source/effects.generated.css +68 -0
  173. package/src/tokens/macos-27/source/kit.generated.css +50 -0
  174. package/src/tokens/macos-27/source/overlays-status.generated.css +931 -0
  175. package/src/tokens/macos-27/source/paint-styles.generated.css +81 -0
  176. package/src/tokens/macos-27/source/provenance.generated.json +62 -0
  177. package/src/tokens/macos-27/source/sizes.generated.css +385 -0
  178. package/src/tokens/macos-27/source/source.generated.json +554 -0
  179. package/src/tokens/macos-27/source/text-styles.generated.css +116 -0
  180. package/src/tokens/macos-27/source.css +17 -0
  181. package/src/tokens/macos-27/web-adaptations.css +98 -0
  182. package/src/tokens/macos-27/web-adaptations.json +163 -0
  183. package/src/tokens/macos-27/web-adaptations.md +117 -0
  184. package/src/tokens/moldable/brand.css +48 -0
  185. package/src/tokens/moldable/index.css +1 -0
  186. package/src/tokens/platform-contract.test.ts +290 -0
@@ -0,0 +1,43 @@
1
+ # Card
2
+
3
+ `Card` groups related content on one bounded surface. Its optional anatomy
4
+ keeps a title, description, trailing action, content, and footer aligned.
5
+
6
+ ```tsx
7
+ import {
8
+ Card,
9
+ CardAction,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from '@moldable-ai/ui'
15
+
16
+ ;<Card surface="raised">
17
+ <CardHeader>
18
+ <CardTitle>Project brief</CardTitle>
19
+ <CardDescription>Updated five minutes ago</CardDescription>
20
+ <CardAction>{/* compact action */}</CardAction>
21
+ </CardHeader>
22
+ <CardContent>{/* related content */}</CardContent>
23
+ </Card>
24
+ ```
25
+
26
+ ## API
27
+
28
+ - `Card` accepts native `div` props and `surface`: `default`, `raised`,
29
+ `recessed`, or `plain`. The default is `default`.
30
+ - `default` uses the card fill, border, and shadow; `raised` uses the raised
31
+ surface; `recessed` uses the inset surface; `plain` provides structure
32
+ without chrome.
33
+ - `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`,
34
+ and `CardFooter` accept native `div` props.
35
+ - `CardProps` and `CardSurface` are exported.
36
+ - Adding `border-b` to the header or `border-t` to the footer also enables
37
+ their matching separator spacing.
38
+
39
+ Cards do not add region, heading, or button semantics. Use a real heading
40
+ inside `CardTitle` when the title participates in the document outline, and
41
+ keep actions as separate buttons or links. Do not make a whole card clickable
42
+ by attaching button behavior to its `div`; use an explicit interactive element
43
+ with a visible focus state.
@@ -0,0 +1,47 @@
1
+ # Carousel
2
+
3
+ An Embla-backed sequence of peer slides with optional previous and next
4
+ controls.
5
+
6
+ ```tsx
7
+ import {
8
+ Carousel,
9
+ CarouselContent,
10
+ CarouselItem,
11
+ CarouselNext,
12
+ CarouselPrevious,
13
+ } from '@moldable-ai/ui'
14
+
15
+ ;<Carousel aria-label="Recent projects" className="mx-auto w-full max-w-lg">
16
+ <CarouselContent>
17
+ {projects.map((project) => (
18
+ <CarouselItem key={project.id}>{project.name}</CarouselItem>
19
+ ))}
20
+ </CarouselContent>
21
+ <CarouselPrevious />
22
+ <CarouselNext />
23
+ </Carousel>
24
+ ```
25
+
26
+ ## API
27
+
28
+ - `Carousel` accepts native `div` props plus `orientation`, `opts`, `plugins`,
29
+ and `setApi`. `orientation` defaults to `horizontal` and determines Embla's
30
+ axis, overriding `opts.axis`.
31
+ - `setApi` receives the initialized `CarouselApi`, which is also exported.
32
+ - `CarouselContent` owns the clipped viewport and flex track.
33
+ - `CarouselItem` is one full-basis slide by default. Override its basis through
34
+ `className` to show multiple slides.
35
+ - `CarouselPrevious` and `CarouselNext` accept shared `Button` props. They
36
+ default to `variant="outline"` and `size="icon"` and disable themselves at
37
+ their respective scroll boundary.
38
+
39
+ The root exposes carousel region semantics and each item exposes slide group
40
+ semantics; provide the root with `aria-label` or `aria-labelledby`. Left and
41
+ Right Arrow scroll from anywhere inside the root in both orientations. Up and
42
+ Down Arrow handling is not implemented, so prefer a horizontal carousel when
43
+ keyboard direction must match its visual axis.
44
+
45
+ Keep slide order stable, make important content available without hover, and
46
+ avoid placing text-editing controls inside the carousel because captured
47
+ Left/Right Arrow keys scroll the sequence.
@@ -0,0 +1,52 @@
1
+ # Chart
2
+
3
+ Theme-aware wrappers for Recharts. `ChartContainer` supplies responsive sizing,
4
+ semantic chart styling, and config-driven labels, icons, and CSS color
5
+ variables.
6
+
7
+ ```tsx
8
+ import {
9
+ ChartContainer,
10
+ ChartTooltip,
11
+ ChartTooltipContent,
12
+ type ChartConfig,
13
+ } from '@moldable-ai/ui'
14
+ import { Bar, BarChart, XAxis } from 'recharts'
15
+
16
+ const config = {
17
+ completed: { label: 'Completed', color: 'var(--chart-1)' },
18
+ } satisfies ChartConfig
19
+
20
+ <ChartContainer config={config} className="h-64 w-full">
21
+ <BarChart accessibilityLayer data={data}>
22
+ <XAxis dataKey="day" />
23
+ <ChartTooltip content={<ChartTooltipContent />} />
24
+ <Bar dataKey="completed" fill="var(--color-completed)" />
25
+ </BarChart>
26
+ </ChartContainer>
27
+ ```
28
+
29
+ ## API
30
+
31
+ - `ChartConfig` maps each series key to an optional `label` and `icon`, plus
32
+ either one `color` or both `theme.light` and `theme.dark` colors.
33
+ - `ChartContainer` accepts native `div` props, required `config`, one Recharts
34
+ chart child, and an optional chart-scoping `id`. That `id` seeds the
35
+ generated `data-chart` value; it is not forwarded as the element's HTML
36
+ `id`. The container wraps its child in `ResponsiveContainer`, so give it a
37
+ definite width and height.
38
+ - Configured colors become `--color-{series}` variables scoped to that chart.
39
+ - `ChartTooltip` and `ChartLegend` are the Recharts primitives.
40
+ - `ChartTooltipContent` supports `indicator="dot" | "line" | "dashed"`,
41
+ `hideLabel`, `hideIndicator`, `nameKey`, and `labelKey`. It consumes the
42
+ Recharts `active`, `payload`, `label`, `labelFormatter`, `formatter`, and
43
+ `color` inputs supplied by `ChartTooltip`.
44
+ - `ChartLegendContent` supports `hideIcon`, `nameKey`, `payload`, and
45
+ `verticalAlign`.
46
+ - `ChartStyle` is exported for advanced composition; `ChartContainer` already
47
+ renders it.
48
+
49
+ Enable Recharts' `accessibilityLayer` on the chart primitive. Use literal
50
+ series labels, semantic color variables, and another cue when color carries
51
+ meaning. For charts whose exact values matter, provide a nearby textual
52
+ summary or table rather than relying on a pointer tooltip.
@@ -0,0 +1,36 @@
1
+ # Checkbox
2
+
3
+ A binary or mixed-state form control built on Radix Checkbox.
4
+
5
+ ```tsx
6
+ import { Checkbox } from '@moldable-ai/ui'
7
+
8
+ ;<label className="flex items-center gap-2">
9
+ <Checkbox
10
+ checked={enabled}
11
+ onCheckedChange={(checked) => setEnabled(checked === true)}
12
+ />
13
+ Enable notifications
14
+ </label>
15
+ ```
16
+
17
+ ## API and behavior
18
+
19
+ - Accepts Radix Checkbox root props, including controlled `checked` and
20
+ `onCheckedChange`, uncontrolled `defaultChecked`, `disabled`, `required`,
21
+ `name`, `value`, and `form`.
22
+ - `checked` and `defaultChecked` support `true`, `false`, or
23
+ `'indeterminate'`. Activating an indeterminate checkbox moves it to checked.
24
+ - `controlSize` is optional and accepts `mini`, `small`, `medium`, `large`, or
25
+ `xl`; when omitted, theme defaults determine its dimensions.
26
+ - The check and indeterminate dash indicators are included.
27
+
28
+ Associate every checkbox with visible label text through a wrapping `label` or
29
+ matching `htmlFor` and `id`. Space toggles the focused control; Enter is
30
+ intentionally prevented. Radix exposes the mixed value as
31
+ `aria-checked="mixed"` and preserves native form participation.
32
+
33
+ Use a checkbox for an independently selectable choice. Use `RadioGroup` for
34
+ one choice from a set and `Switch` for a settings-style state that takes effect
35
+ immediately. Do not rely on the checkmark or accent color as the only
36
+ explanation of the choice.
@@ -41,9 +41,8 @@ Radix. Space and Enter toggle it. The chevron is decorative.
41
41
 
42
42
  ## Motion
43
43
 
44
- Motion is brief and uses the shared reduced-motion behavior. Set
45
- `motion="none"` for content where movement would distract or when a parent owns
46
- the transition.
44
+ The section content animates by default. Set `motion="none"` when movement
45
+ would distract or when a parent owns the transition.
47
46
 
48
47
  ## Don't
49
48
 
@@ -1,11 +1,21 @@
1
1
  # ColorWell
2
2
 
3
- Compact color selection or read-only swatch.
3
+ Compact native color selection or a read-only swatch.
4
4
 
5
5
  ```tsx
6
6
  <ColorWell aria-label="Project color" value={color} onValueChange={setColor} />
7
7
  ```
8
8
 
9
- Use the browser color input as the renderer fallback. `readOnly` presents the
10
- value without implying interactivity. Never make color the only representation
11
- of a status or category.
9
+ ## API
10
+
11
+ - `value` accepts a native color-input value (normally `#rrggbb`) or `null`; an
12
+ empty value displays a checkerboard swatch.
13
+ - `onValueChange` receives the native color input value.
14
+ - `size` is `sm`, `default`, or `lg` and defaults to `default`.
15
+ - `readOnly` defaults to `false`. When true, the component renders a
16
+ non-interactive image role instead of an input.
17
+ - The accessible label defaults to “Choose color”; provide a specific label in
18
+ app UI.
19
+
20
+ The interactive form uses the browser's native color input. Never make color
21
+ the only representation of a status or category.
@@ -4,9 +4,9 @@ Searchable single- or multi-value selection for longer option sets.
4
4
 
5
5
  ## When to use
6
6
 
7
- Use `Combobox` when users need to filter options, create values, or manage
8
- multiple selections. Use `Select` for a short fixed list, `Command` for actions,
9
- and `Input` for unconstrained text.
7
+ Use `Combobox` when users need to filter a longer option set or manage multiple
8
+ selections. Use `Select` for a short fixed list, `Command` for actions, and
9
+ `Input` for unconstrained text.
10
10
 
11
11
  ## Anatomy
12
12
 
@@ -71,10 +71,11 @@ colors or custom dialog elevation.
71
71
  ## Keyboard and accessibility
72
72
 
73
73
  The component inherits alert-dialog semantics and focus containment. The
74
- dialog content receives initial focus; Enter activates the marked primary
75
- action when appropriate, and pending state prevents dismissal. Write a
76
- specific title and describe the consequence, not merely “Are you sure?”.
77
- Error copy is announced with `role="alert"`.
74
+ dialog content receives initial focus. Command+Enter activates the confirm
75
+ action on macOS and iOS-family runtimes; Control+Enter does so elsewhere.
76
+ Plain Enter keeps the focused control's native behavior, and pending state
77
+ prevents dismissal. Write a specific title and describe the consequence, not
78
+ merely “Are you sure?”. Error copy is announced with `role="alert"`.
78
79
 
79
80
  ## Do
80
81
 
@@ -119,10 +119,11 @@ command palette as well as the context menu.
119
119
 
120
120
  ## Platform behavior
121
121
 
122
- With `items`, a user-initiated context-menu gesture automatically opens the
122
+ With `items`, a `contextmenu` event such as a secondary click opens the
123
123
  host-native menu when `nativeMenu` is advertised. The pointer position is its
124
124
  anchor, `selectedId` is delivered to `onSelectItem`, and dismissal is a no-op.
125
- No portable menu is rendered while that native request is open.
125
+ No portable menu is rendered while that native request is open. Radix's
126
+ touch/pen long press opens the portable menu directly.
126
127
 
127
128
  When the service is not advertised, including web and iOS, the same items
128
129
  automatically render through the existing Radix anatomy. If advertised
@@ -29,4 +29,9 @@ Values use the native timezone-free `YYYY-MM-DD` form. Use `value`/`onValueChang
29
29
  - Use a calendar popover only for workflows that require richer range, availability, or event selection.
30
30
  - Do not replace the native picker to imitate a desktop control.
31
31
 
32
- A host may later provide a native date capability. Keep that behind an explicit cross-client contract and preserve this web implementation as the portable fallback.
32
+ `DateField` itself is renderer-owned and does not call the host. A call site
33
+ that deliberately prefers the host picker can use `useHostDateTimePicker()` or
34
+ `pickDateTimeWithHost('date', ...)`, then keep `DateField` as its portable
35
+ fallback. A `status: 'fallback'` result does not open renderer UI for the
36
+ caller. See
37
+ [`host-date-time-picker.md`](../../lib/host-date-time-picker.md).
@@ -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.