@phreshos/react-ui 0.1.36 → 0.1.37

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 (201) hide show
  1. package/README.md +147 -167
  2. package/dist/accordion.d.ts +7 -6
  3. package/dist/accordion.js +5 -5
  4. package/dist/alert-dialog.js +3 -1
  5. package/dist/alert.d.ts +17 -0
  6. package/dist/alert.js +30 -0
  7. package/dist/app-layout.d.ts +13 -0
  8. package/dist/app-layout.js +84 -0
  9. package/dist/button.d.ts +12 -24
  10. package/dist/button.js +34 -39
  11. package/dist/calendar-layout.d.ts +9 -5
  12. package/dist/calendar-layout.js +67 -78
  13. package/dist/calendar-trigger.d.ts +4 -4
  14. package/dist/calendar-trigger.js +7 -29
  15. package/dist/calendar.d.ts +2 -2
  16. package/dist/calendar.js +6 -5
  17. package/dist/checkbox.d.ts +4 -4
  18. package/dist/checkbox.js +16 -6
  19. package/dist/color-area.d.ts +20 -0
  20. package/dist/color-area.js +30 -0
  21. package/dist/color-field.d.ts +17 -0
  22. package/dist/color-field.js +17 -0
  23. package/dist/color-picker.d.ts +24 -0
  24. package/dist/color-picker.js +30 -0
  25. package/dist/color-slider.d.ts +19 -0
  26. package/dist/color-slider.js +29 -0
  27. package/dist/color-swatch-picker.d.ts +23 -0
  28. package/dist/color-swatch-picker.js +36 -0
  29. package/dist/color-swatch.d.ts +18 -0
  30. package/dist/color-swatch.js +20 -0
  31. package/dist/combo-box.d.ts +18 -10
  32. package/dist/combo-box.js +43 -58
  33. package/dist/context-menu.d.ts +14 -10
  34. package/dist/context-menu.js +7 -6
  35. package/dist/control/color-thumb.d.ts +13 -0
  36. package/dist/control/color-thumb.js +33 -0
  37. package/dist/control/color-value.d.ts +6 -0
  38. package/dist/control/color-value.js +7 -0
  39. package/dist/control/control.d.ts +60 -0
  40. package/dist/control/control.js +71 -0
  41. package/dist/control/field-button.d.ts +13 -0
  42. package/dist/control/field-button.js +28 -0
  43. package/dist/control/field.d.ts +20 -0
  44. package/dist/control/field.js +34 -0
  45. package/dist/control/icon.d.ts +11 -0
  46. package/dist/control/icon.js +8 -0
  47. package/dist/control/item.d.ts +34 -0
  48. package/dist/control/item.js +53 -0
  49. package/dist/control/open-state.d.ts +25 -0
  50. package/dist/control/open-state.js +8 -0
  51. package/dist/control/selection-track.d.ts +39 -0
  52. package/dist/control/selection-track.js +122 -0
  53. package/dist/control/selection.d.ts +44 -0
  54. package/dist/{selection.js → control/selection.js} +16 -0
  55. package/dist/control/surface-render.d.ts +10 -0
  56. package/dist/control/surface-render.js +12 -0
  57. package/dist/control/text-control.d.ts +32 -0
  58. package/dist/control/text-control.js +31 -0
  59. package/dist/control/toggle.d.ts +32 -0
  60. package/dist/control/toggle.js +80 -0
  61. package/dist/date-control.d.ts +9 -9
  62. package/dist/date-control.js +31 -34
  63. package/dist/date-field.d.ts +7 -6
  64. package/dist/date-field.js +8 -6
  65. package/dist/date-picker.d.ts +13 -6
  66. package/dist/date-picker.js +9 -6
  67. package/dist/date-range-picker.d.ts +15 -6
  68. package/dist/date-range-picker.js +12 -9
  69. package/dist/dialog.d.ts +22 -8
  70. package/dist/dialog.js +37 -28
  71. package/dist/disclosure.d.ts +8 -7
  72. package/dist/disclosure.js +31 -52
  73. package/dist/drop-zone.d.ts +18 -0
  74. package/dist/drop-zone.js +32 -0
  75. package/dist/dropdown-menu.d.ts +7 -4
  76. package/dist/dropdown-menu.js +5 -4
  77. package/dist/fieldset.d.ts +13 -0
  78. package/dist/fieldset.js +26 -0
  79. package/dist/file-trigger.d.ts +13 -0
  80. package/dist/file-trigger.js +6 -0
  81. package/dist/flex.d.ts +1 -1
  82. package/dist/flex.js +5 -4
  83. package/dist/foundation/appearance.d.ts +83 -0
  84. package/dist/foundation/appearance.js +38 -0
  85. package/dist/{aria-direction.js → foundation/aria-direction.js} +3 -1
  86. package/dist/foundation/color.d.ts +61 -0
  87. package/dist/foundation/color.js +247 -0
  88. package/dist/{layout.d.ts → foundation/layout.d.ts} +6 -4
  89. package/dist/{layout.js → foundation/layout.js} +7 -5
  90. package/dist/foundation/motion-style.d.ts +9 -0
  91. package/dist/foundation/motion-style.js +50 -0
  92. package/dist/foundation/preferences.d.ts +15 -0
  93. package/dist/{ui-provider.d.ts → foundation/provider.d.ts} +3 -5
  94. package/dist/{ui-provider.js → foundation/provider.js} +14 -26
  95. package/dist/{radius.d.ts → foundation/radius.d.ts} +6 -3
  96. package/dist/foundation/radius.js +11 -0
  97. package/dist/foundation/spacing.d.ts +11 -0
  98. package/dist/foundation/spacing.js +4 -0
  99. package/dist/foundation/visual.d.ts +42 -0
  100. package/dist/{appearance-context.js → foundation/visual.js} +41 -27
  101. package/dist/grid.d.ts +1 -1
  102. package/dist/grid.js +5 -4
  103. package/dist/icons/main.d.ts +5 -3
  104. package/dist/icons/main.js +5 -3
  105. package/dist/input.d.ts +2 -2
  106. package/dist/input.js +9 -7
  107. package/dist/list-box.d.ts +24 -19
  108. package/dist/list-box.js +52 -109
  109. package/dist/main.d.ts +27 -16
  110. package/dist/main.js +22 -9
  111. package/dist/menu.d.ts +28 -32
  112. package/dist/menu.js +47 -93
  113. package/dist/number-field.d.ts +17 -0
  114. package/dist/number-field.js +28 -0
  115. package/dist/panel.d.ts +10 -5
  116. package/dist/panel.js +33 -12
  117. package/dist/popover.d.ts +19 -5
  118. package/dist/popover.js +22 -15
  119. package/dist/progress-bar.d.ts +8 -10
  120. package/dist/progress-bar.js +18 -34
  121. package/dist/radio.d.ts +15 -12
  122. package/dist/radio.js +27 -37
  123. package/dist/range-calendar.d.ts +5 -3
  124. package/dist/range-calendar.js +8 -7
  125. package/dist/scroll-area.d.ts +2 -2
  126. package/dist/scroll-area.js +10 -9
  127. package/dist/segmented-control.d.ts +23 -0
  128. package/dist/segmented-control.js +38 -0
  129. package/dist/select.d.ts +40 -13
  130. package/dist/select.js +63 -18
  131. package/dist/slider.d.ts +7 -3
  132. package/dist/slider.js +51 -50
  133. package/dist/spinner.d.ts +5 -8
  134. package/dist/spinner.js +12 -27
  135. package/dist/surface/grain.d.ts +12 -0
  136. package/dist/surface/grain.js +65 -0
  137. package/dist/{material-options.d.ts → surface/material-options.d.ts} +1 -2
  138. package/dist/{material-options.js → surface/material-options.js} +2 -2
  139. package/dist/surface/paint-class.d.ts +6 -0
  140. package/dist/surface/paint-class.js +36 -0
  141. package/dist/{shadow-options.d.ts → surface/shadow-options.d.ts} +13 -1
  142. package/dist/{shadow-options.js → surface/shadow-options.js} +17 -2
  143. package/dist/surface/surface.d.ts +73 -0
  144. package/dist/surface/surface.js +230 -0
  145. package/dist/switch.d.ts +4 -5
  146. package/dist/switch.js +17 -8
  147. package/dist/table.d.ts +29 -32
  148. package/dist/table.js +68 -106
  149. package/dist/tabs.d.ts +16 -15
  150. package/dist/tabs.js +44 -86
  151. package/dist/textarea.d.ts +2 -2
  152. package/dist/textarea.js +10 -14
  153. package/dist/time-field.d.ts +6 -6
  154. package/dist/time-field.js +8 -6
  155. package/dist/toolbar.d.ts +6 -4
  156. package/dist/toolbar.js +16 -15
  157. package/dist/tooltip.d.ts +13 -4
  158. package/dist/tooltip.js +17 -15
  159. package/dist/tree.d.ts +22 -27
  160. package/dist/tree.js +54 -99
  161. package/dist/window.d.ts +2 -2
  162. package/dist/window.js +18 -20
  163. package/package.json +3 -3
  164. package/dist/appearance-context.d.ts +0 -54
  165. package/dist/appearance.d.ts +0 -11
  166. package/dist/appearance.js +0 -11
  167. package/dist/color.d.ts +0 -69
  168. package/dist/color.js +0 -192
  169. package/dist/control-surface.d.ts +0 -20
  170. package/dist/control-surface.js +0 -11
  171. package/dist/control.d.ts +0 -70
  172. package/dist/control.js +0 -105
  173. package/dist/field-style.d.ts +0 -3
  174. package/dist/field-style.js +0 -12
  175. package/dist/material-paint.d.ts +0 -13
  176. package/dist/material-paint.js +0 -119
  177. package/dist/motion-style.d.ts +0 -13
  178. package/dist/motion-style.js +0 -75
  179. package/dist/preferences.d.ts +0 -12
  180. package/dist/radius.js +0 -11
  181. package/dist/readiness.d.ts +0 -37
  182. package/dist/readiness.js +0 -134
  183. package/dist/selection.d.ts +0 -18
  184. package/dist/spacing.d.ts +0 -8
  185. package/dist/spacing.js +0 -8
  186. package/dist/surface-edge.d.ts +0 -11
  187. package/dist/surface-edge.js +0 -55
  188. package/dist/surface.d.ts +0 -27
  189. package/dist/surface.js +0 -92
  190. package/dist/text-control.d.ts +0 -10
  191. package/dist/text-control.js +0 -0
  192. package/dist/toggle-indicator.d.ts +0 -18
  193. package/dist/toggle-indicator.js +0 -49
  194. /package/dist/{aria-direction.d.ts → foundation/aria-direction.d.ts} +0 -0
  195. /package/dist/{direction.d.ts → foundation/direction.d.ts} +0 -0
  196. /package/dist/{direction.js → foundation/direction.js} +0 -0
  197. /package/dist/{overlay-placement.d.ts → foundation/overlay-placement.d.ts} +0 -0
  198. /package/dist/{overlay-placement.js → foundation/overlay-placement.js} +0 -0
  199. /package/dist/{preferences.js → foundation/preferences.js} +0 -0
  200. /package/dist/{scale.d.ts → foundation/scale.d.ts} +0 -0
  201. /package/dist/{scale.js → foundation/scale.js} +0 -0
package/README.md CHANGED
@@ -37,156 +37,155 @@ import { Button, Surface } from "@phreshos/react-ui"
37
37
  ```
38
38
 
39
39
  Without a provider, components use React UI's `defaultAppearance` and reactively
40
- follow the browser's complete visual preferences. `UIProvider` supplies the
41
- Appearance, Preferences, and direction explicitly established for a React UI
42
- subtree. Each omitted property inherits independently from the nearest provider
43
- or follows its own default resolution. A provider with no properties introduces
44
- no boundary or behavior.
45
- React UI also exports `defaultAppearance` for callers that need the value
46
- explicitly. A complete System Appearance can be passed directly; React UI uses
47
- the visual fields it owns and ignores additional System fields such as
48
- wallpapers.
49
-
50
- React UI is neutral between left-to-right and right-to-left interfaces.
51
- An explicit `UIProvider` direction creates a boundary that participates in
52
- native DOM inheritance without creating a layout box. When direction is
53
- omitted, the provider leaves DOM inheritance unchanged. React UI reads the
54
- nearest explicit direction—or `<html dir>` when there is none—only for
55
- JavaScript behavior and portals that cannot rely on native inheritance.
40
+ follow the browser's visual preferences. `UIProvider` supplies the Appearance,
41
+ Preferences, and direction explicitly established for a subtree. Each omitted
42
+ property inherits independently from the nearest provider. A provider with no
43
+ properties introduces no boundary or behavior. A complete System Appearance can
44
+ be passed directly; React UI uses its visual fields and ignores the rest.
56
45
 
57
- ```tsx
58
- import { UIProvider, Select } from "@phreshos/react-ui"
46
+ React UI is neutral between left-to-right and right-to-left interfaces. An
47
+ explicit `UIProvider` direction creates a boundary that participates in native
48
+ DOM inheritance without creating a layout box. When direction is omitted, React
49
+ UI follows `<html dir>` for JavaScript behavior and portals.
59
50
 
60
- <UIProvider direction="rtl">
61
- <Select label="Choice" options={[{ value: "one", label: "One" }]} />
51
+ ```tsx
52
+ <UIProvider direction="rtl" preferences={{ theme: "dark" }}>
53
+ <Application />
62
54
  </UIProvider>
63
55
  ```
64
56
 
65
- ```tsx
66
- import { UIProvider, Button, useBrowserPreferences } from "@phreshos/react-ui"
67
-
68
- function Example() {
69
- const browser = useBrowserPreferences()
57
+ ## Visual model
70
58
 
71
- return <UIProvider preferences={{ ...browser, theme: "dark" }}>
72
- <Button>Dark subtree</Button>
73
- </UIProvider>
74
- }
75
- ```
59
+ Every component is built from one primitive, `Surface`, and one Material. A
60
+ Surface is where the Material sits relative to its surroundings:
76
61
 
77
- `Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Switch`, and `RadioGroup.Item`
78
- use the shared Surface implementation while retaining their native behavior. Color and material
79
- are independent inputs. `color` accepts an Appearance color and resting level such
80
- as `background:base` or `primary:soft`, or a direct CSS color. Surface defaults
81
- to `background:base`; solid controls default to `default:base`.
82
- Every independently owned base paint is exposed through its component's
83
- `color` prop. Hover, press, focus, selection, and inactive treatments derive
84
- from that base rather than introducing separate color inputs. Surface and solid controls compare
85
- the fill with an adaptive perceptual boundary biased toward lighter text, then
86
- use the Appearance candidate on the opposite side; interaction shades keep that
87
- choice.
88
- The omitted value follows the component's meaning: neutral controls use
89
- `default:base`, while a role with inherent visual meaning may choose another
90
- Appearance color. Every semantic default remains overridable through `color`.
91
- `size` accepts `xsmall`, `small`, `medium` (default), `large`, or `xlarge`;
92
- spacing and radius follow Appearance, while typography remains inherited and
93
- component sizes are relative to the surrounding text. `disabled` prevents activation and focus;
94
- `pending` prevents activation while retaining focus.
62
+ | Depth | Meaning | Treatment |
63
+ | --- | --- | --- |
64
+ | `raised` (default) | Something you act on | A lit rim with a slight inward spill, and the outer Appearance shadow |
65
+ | `flat` | A region level with its surroundings | A softer rim |
66
+ | `recessed` | Something that holds a value you enter | A slightly deeper paint and a dim rim |
67
+
68
+ Every Surface shares one Material edge: its rim catches the same light along
69
+ its top and bottom, so neither edge reads as a bevel, and only faintly along
70
+ its sides, and a very thin dark hairline
71
+ separates it from whatever it sits on. Buttons, selected tabs, thumbs, windows,
72
+ and overlays rise. Text fields, tracks, rails, and wells recess. How deep a
73
+ recess sinks and how much light the rim receives follow the lightness of the
74
+ canvas continuously, so every canvas receives a matching treatment.
95
75
 
96
76
  ```tsx
97
- <Button>Cancel</Button>
98
- <Button color="primary:base" onPress={save}>Save</Button>
99
- <Button color="danger:soft" size="small">Delete</Button>
77
+ <Surface>Raised</Surface>
78
+ <Surface depth="flat">Level</Surface>
79
+ <Surface depth="recessed">Holds a value</Surface>
100
80
  ```
101
81
 
102
- `Surface` is the material- and shadow-owning element. It renders a `div` by default, while
103
- `as` selects another React element and preserves that element's native properties
104
- and ref type. Surface owns its paint, opacity, frost, refraction, grain, edge,
105
- outer shadow, and radius. `radius` accepts a size level, `full`, a
106
- number in pixels, or a CSS radius and defaults to `medium`.
82
+ `Surface` renders a `div` by default; `as` selects another element or
83
+ component. A host applies the `className`, `style`, and `children` it receives
84
+ on one element and forwards its ref to it. Consumer classes and styles always
85
+ take precedence over Surface's own paint.
107
86
 
108
- ```tsx
109
- <Surface color="background:soft" radius="large">Derived values</Surface>
110
- <Surface as="button" type="button" color="#345678" radius={18}>Action</Surface>
111
- <Surface as={Grid} columns={3} gap="medium">Grid content</Surface>
112
- ```
87
+ ### Color
113
88
 
114
- `as` can also select an outside React component. A valid Surface host preserves
115
- the `style`, `dir`, and `children` it receives on one host element and forwards
116
- its ref to that same element. This lets layout components carry the material
117
- without a wrapper. The host retains ownership of its own behavior and layout
118
- properties; Surface retains ownership of material, edge, shadow, radius, and
119
- required geometry.
89
+ `color` accepts an Appearance color (`primary`), an Appearance color at one
90
+ level (`primary:soft`), or a direct CSS color. Levels move a color toward the
91
+ canvas (`subtle`, `soft`) or toward the content color (`strong`, `intense`), so
92
+ each level keeps its meaning in both Themes.
93
+
94
+ `default` is a neutral paint close to the canvas; `primary` is the identity
95
+ color. A component's omitted color follows its meaning: neutral controls use
96
+ `default`, fields recess the `background`, and selection, progress, and on-states
97
+ use `primary`. Every owned paint remains customizable through `color`.
98
+
99
+ Content on a paint uses whichever Appearance content color reads more clearly
100
+ on its resting paint, by perceived lightness contrast. Hover and press move the same base toward the content color; a pressed
101
+ raised Surface settles onto its surroundings. Focus rings in the owner's color,
102
+ or the identity color when the owner is neutral. A focused or invalid value
103
+ holder also claims its hairline. There are no separate state colors.
104
+
105
+ ### Material
120
106
 
121
107
  `MaterialOptions` defines `opacity`, `backdrop`, `grain`, `grainAmount`,
122
- `distortion`, and `saturation`. Every material-bearing component, including
123
- Surface, exposes these values through its `material` prop. Color remains a
124
- separate property. Omission uses the resource-efficient `basic` rendering mode.
125
- `none` ignores Material and paints the resolved color as a normal background;
126
- `basic` adds Material paint, grain, and its edge while remaining solid, with
127
- Material opacity applied only to the grain; `extended` applies that opacity to
128
- the complete Material instead; and `full` adds backdrop, distortion, and
129
- saturation. A `MaterialOptions` object selects full rendering with those
130
- overrides. Effect options accept a scale level or a direct number; opacity
131
- affects Surface paint only, never its content. The material edge is part of the
132
- same Surface rather than a second public entity.
133
-
134
- The modes are progressive: each level preserves the rendering of the previous
135
- one and adds only its own Material capability.
136
-
137
- Surface-based controls expose the same separate `color` and `material` props.
138
- `ShadowOptions` similarly groups `x`, `y`, `blur`, `spread`, and `opacity` under
139
- the `shadow` prop. Omission or `true` follows the active `appearance.shadow`
140
- branch, while `false` removes the shadow. Each option accepts a scale level or
141
- direct number. The shadow is a neutral black
142
- outer shadow. Surface-based controls expose the same `shadow` prop.
143
- For text fields and Select, `material` targets the field or trigger; for Checkbox,
144
- Switch, and RadioGroup.Item, it targets the indicator. RadioGroup supplies material defaults
145
- to its options, and an Item can override them. Shadow follows the same targets and
146
- inheritance path.
108
+ `distortion`, and `saturation`. Every Surface-based component accepts them
109
+ through `material`. Rendering is progressive: `none` paints the resolved color
110
+ only; `basic` (default) adds grain and the edge while remaining solid;
111
+ `extended` applies Material opacity; `full` adds backdrop, saturation, and
112
+ distortion. A `MaterialOptions` object selects full rendering with those
113
+ overrides. Backdrop effects are omitted whenever the final paint is opaque.
114
+
115
+ `shadow` groups `x`, `y`, `blur`, `spread`, and `opacity` for raised Surfaces;
116
+ `false` removes it.
117
+
118
+ ### Radius
119
+
120
+ `radius` accepts a size level, `full`, pixels, or CSS. Two rules derive every
121
+ default from the Appearance radius:
122
+
123
+ 1. Every Surface carries the Appearance radius. Shells and containers such as
124
+ Window, Panel and its content, Dialog, and Popover carry it as it is. A
125
+ control scales it with its height, so the medium control height carries it
126
+ exactly and every size keeps the same shape, wherever it sits: list Items and
127
+ the selected Tab are controls.
128
+ 2. Shapes whose form is their meaning, such as Radio, Switch, Slider, progress
129
+ rails, and scrollbar thumbs, stay fully rounded.
130
+
131
+ ## Component rules
132
+
133
+ Three rules hold across the library, so learning a few components teaches the
134
+ rest.
135
+
136
+ 1. **Everything that owns a paint** accepts `color`, `size`, `radius`,
137
+ `material`, `disabled`, `className`, and `style`.
138
+ 2. **Everything that holds a value is a field**: `label`, `description`,
139
+ `errorMessage`, `invalid`, `required`, `readOnly`, and `value`,
140
+ `defaultValue`, `onChange(value)`.
141
+ 3. **Every collection is composed of Items** identified by a string `id`:
142
+ Select, ComboBox, ListBox, Menu, Tabs, Table, Tree, and Accordion. Actions
143
+ report that identity through `onAction(id)`, and an Item is unavailable
144
+ through its own `disabled`.
147
145
 
148
146
  ```tsx
149
- <Button color="primary:base" material="full">Save</Button>
150
- <Surface material={{ opacity: 0.6, backdrop: 0 }}>Translucent</Surface>
151
- <Input label="Name" radius="large" material={{ grain: "small" }} shadow={{ blur: "small" }} />
147
+ <Button color="primary" onPress={install}>Install</Button>
148
+
149
+ <Input label="Name" value={name} onChange={setName} />
150
+
151
+ <Select label="Program" value={program} onChange={setProgram}>
152
+ <Select.Item id="tilo">Tilo</Select.Item>
153
+ <Select.Item id="lemo">Lemo</Select.Item>
154
+ </Select>
152
155
  ```
153
156
 
154
- `Panel` composes an outer `Surface`, an optional header, and an inset content
155
- `Surface`. Both materials retain Surface defaults; the content inset follows
156
- Appearance spacing. Positioning, modality, and lifecycle belong to the caller.
157
+ `size` accepts `xsmall`, `small`, `medium` (default), `large`, or `xlarge`.
158
+ Spacing and radius follow Appearance; typography is inherited and applied once
159
+ at each component's root. `disabled` prevents activation and focus; `pending`
160
+ prevents activation while retaining focus.
157
161
 
158
162
  Component families use one public path: the family name is the root and parts
159
- that depend on it are properties of that family. For example, use
160
- `Panel.Header` and `Dialog.Title`; those parts are not separate top-level
161
- components. Components with independent contracts remain top-level exports.
163
+ that depend on it are its properties, such as `Panel.Header` and
164
+ `Dialog.Title`.
162
165
 
163
- ```tsx
164
- import { Panel } from "@phreshos/react-ui"
166
+ `Panel` is a frosted shell with an optional header and one content region. The
167
+ content takes the shell's color, raised with the `extended` material and the
168
+ same Appearance radius, sits half the spacing inside the shell, and pads its
169
+ content by the spacing. The header is the same row as a Window header.
165
170
 
171
+ ```tsx
166
172
  <Panel>
167
- <Panel.Header><h2>Title</h2></Panel.Header>
168
- <Panel.Content style={{ padding: 16 }}>Content</Panel.Content>
173
+ <Panel.Header>Connection</Panel.Header>
174
+ <Panel.Content style={{ padding: 12 }}>Connected to the local System.</Panel.Content>
169
175
  </Panel>
170
176
  ```
171
177
 
172
- Native properties and the root ref target the outer Surface. `Panel.Header`
173
- owns the optional leading region, while `Panel.Content` is the independently
174
- configurable inner Surface.
175
-
176
178
  `Window` provides one Surface with a ready header-and-content layout.
177
179
  `Window.Header.Identity` presents the icon and truncating title,
178
- `Window.Header.Center` is optional flexible space, and
179
- `Window.Header.Actions` aligns compact controls at the end. `Window.Content`
180
- fills the remaining area without deciding its overflow behavior. The header
181
- uses the enclosing Surface rather than creating another material layer, and it
182
- can also be composed independently inside any Surface. Its controls are React
183
- UI Buttons; callers connect them to their own window operations. Pointer and
184
- double-click handlers on the header can implement a drag area, while the center
185
- and actions keep their interactions separate.
180
+ `Window.Header.Center` is optional flexible space, and `Window.Header.Actions`
181
+ aligns compact controls at the end. `Window.Content` fills the remaining area
182
+ without deciding its overflow. Its controls are Buttons; callers connect them to
183
+ their own window operations, and `beginMoveGesture` hands an intentional header
184
+ drag to a window host.
186
185
 
187
186
  ```tsx
188
187
  <Window>
189
- <Window.Header active={active} onPointerDown={beginDrag}>
188
+ <Window.Header active={active}>
190
189
  <Window.Header.Identity icon={icon} title={title} />
191
190
  <Window.Header.Actions>
192
191
  <Window.Header.Minimize onPress={minimize} />
@@ -209,8 +208,8 @@ semantic content remains explicit.
209
208
  <DropdownMenu.Trigger>Actions</DropdownMenu.Trigger>
210
209
  <DropdownMenu.Content>
211
210
  <Menu aria-label="Document actions">
212
- <Menu.Item onAction={open}>Open</Menu.Item>
213
- <Menu.Item color="danger:base" onAction={remove}>Delete</Menu.Item>
211
+ <Menu.Item id="open" onAction={open}>Open</Menu.Item>
212
+ <Menu.Item id="delete" onAction={remove}>Delete</Menu.Item>
214
213
  </Menu>
215
214
  </DropdownMenu.Content>
216
215
  </DropdownMenu>
@@ -218,9 +217,9 @@ semantic content remains explicit.
218
217
 
219
218
  `Popover` presents anchored non-modal content. `DropdownMenu` and
220
219
  `ContextMenu` open the same `Menu` contract through different interactions.
221
- Menu is selection-free by default. Selectable menus use string identities
222
- through `value`, `defaultValue`, `onChange`, and `disabledValues`, matching the
223
- other selectable collections; root-level command dispatch uses `onItemAction`.
220
+ Menu is selection-free by default. Selectable menus use the same string
221
+ selection contract as the other collections, and root-level command dispatch
222
+ uses `onAction(id)`.
224
223
  `Dialog` blocks interaction behind it and may be dismissable, while
225
224
  `AlertDialog` requires an explicit decision by default. `Tooltip` supplies a
226
225
  short description on focus or hover. Portal cleanup, focus restoration,
@@ -229,31 +228,11 @@ component-owned.
229
228
 
230
229
  ## Inputs
231
230
 
232
- Every input uses Appearance colors and the same five `size` levels as Button.
233
- `color` selects the fill independently from material. Input, Textarea, Select,
234
- and Button use `default:base` when it is omitted. Invalid
235
- fields use danger instead. Text fields share Surface's glass edge. Interaction shades
236
- derive from the base color's lightness, and text or selection marks use the Appearance
237
- background or foreground that is perceptually opposite the base fill.
238
- That text or mark color stays unchanged across interaction shades.
239
- These are component-owned derivations, not additional Appearance settings.
240
- Theme names never imply particular colors. No component requires a Client or
241
- Server SDK.
242
-
243
- Fields distinguish hover, pointer focus, keyboard focus, and invalid state.
244
- Shared CSS transitions use `appearance.transaction` for colors and corner
245
- radius. The material fill and opacity values transition on the painted layers, not on the
246
- Surface host. Select menus scale from `1.05` to `1` while fading in, and reverse
247
- that motion when exiting, using React Aria's animation lifecycle. Preferences with animations disabled make
248
- these changes immediate without changing the Appearance value.
249
- Blur, distortion, geometry, and Surface host opacity are not transitioned; gradients
250
- and structurally removed effects change directly rather than adding extra
251
- layers or keeping disabled effects alive.
252
-
253
- Motion animates toggle presses, selection marks, and switch travel. CSS transitions
254
- animate paint and the Select chevron. Preferences with animations disabled remove spatial
255
- feedback and make state transitions immediate. Slider values and native input
256
- behavior are never delayed by visual animation.
231
+ Text fields, Select, ComboBox, and the date fields recess the `background` by
232
+ default. Checkbox, Switch, and RadioGroup indicators are recessed beds that rise
233
+ in their color when selected. Slider and ProgressBar fill a recessed rail with
234
+ their color. Motion follows `appearance.transaction`; Preferences with
235
+ animations disabled make every change immediate without changing Appearance.
257
236
 
258
237
  | Component | Value contract | Purpose |
259
238
  | --- | --- | --- |
@@ -268,11 +247,15 @@ behavior are never delayed by visual animation.
268
247
  | `Checkbox` | `checked`, `defaultChecked`, `onChange(boolean)` | Independent selection; `indeterminate` represents a mixed state |
269
248
  | `Switch` | `checked`, `defaultChecked`, `onChange(boolean)` | An on/off setting |
270
249
  | `RadioGroup` / `RadioGroup.Item` | Group `value`, `defaultValue`, `onChange(string)`; Item `value` | One exclusive choice; vertical by default, optionally horizontal |
271
- | `Select` | `value: string \| null`, `defaultValue`, `onChange(string \| null)` | One choice from `options: { value, label, disabled? }[]` |
272
- | `ComboBox` | Selected `value: string \| null` and query `inputValue: string` | One searchable choice from `options: { value, label, disabled? }[]` |
250
+ | `Select` | `value: string \| null`, `defaultValue`, `onChange(string \| null)` | One choice from its `Select.Item`s |
251
+ | `ComboBox` | Selected `value: string \| null` and query `inputValue: string` | One searchable choice from its `ComboBox.Item`s |
273
252
  | `ListBox` | Single string or multiple string-array `value`, `defaultValue`, and `onChange` | A visible selectable collection with composable items and sections |
274
253
  | `Tree` | Optional selection plus controlled or uncontrolled string-array expansion | Nested hierarchical navigation with arbitrary branch depth |
275
254
  | `Slider` | `value`, `defaultValue`, `onChange(number)` | One numeric value; `minValue`, `maxValue`, `step`, and `onChangeEnd`; horizontal by default |
255
+ | `NumberField` | `value: number \| null`, `defaultValue`, `onChange(number \| null)` | A number typed or stepped within `minValue` and `maxValue`, formatted by `formatOptions` |
256
+ | `SegmentedControl` / `SegmentedControl.Item` | `value`, `defaultValue`, `onChange(string)`; Item `id` | One of a few visible options, on the Tabs track |
257
+ | `ColorField`, `ColorSlider`, `ColorArea`, `ColorSwatchPicker` | Hex `value: string`, `defaultValue`, `onChange(string)` | A color typed, moved along one or two channels, or chosen from swatches; inside a `ColorPicker` they edit its color |
258
+ | `DropZone` / `FileTrigger` | `onDrop(File[])` / `onSelect(File[])` | Files dragged onto an area, or chosen from the system file chooser |
276
259
 
277
260
  Use `label` for visible labels, or `aria-label` / `aria-labelledby` for an
278
261
  accessible name without visible text. `description` provides associated help.
@@ -313,14 +296,14 @@ import { parseDate, parseTime } from "@internationalized/date"
313
296
  <RadioGroup.Item label="Grid" value="grid" />
314
297
  <RadioGroup.Item label="List" value="list" />
315
298
  </RadioGroup>
316
- <Select label="Sort" name="sort" options={[
317
- { value: "name", label: "Name" },
318
- { value: "date", label: "Date" }
319
- ]} />
320
- <ComboBox label="Region" name="region" options={[
321
- { value: "eu", label: "Europe" },
322
- { value: "us", label: "United States" }
323
- ]} />
299
+ <Select label="Sort" name="sort">
300
+ <Select.Item id="name">Name</Select.Item>
301
+ <Select.Item id="date">Date</Select.Item>
302
+ </Select>
303
+ <ComboBox label="Region" name="region">
304
+ <ComboBox.Item id="eu">Europe</ComboBox.Item>
305
+ <ComboBox.Item id="us">United States</ComboBox.Item>
306
+ </ComboBox>
324
307
  <ListBox aria-label="Programs" defaultValue="editor">
325
308
  <ListBox.Section id="productivity">
326
309
  <ListBox.Header>Productivity</ListBox.Header>
@@ -360,16 +343,13 @@ accessible disclosure control; `Tree.Collection` recursively renders
360
343
  data-backed child collections.
361
344
 
362
345
  Each control delegates focus, keyboard, form, and selection behavior to its
363
- accessible behavioral primitive rather than recreating those systems. Select's
364
- popup and ComboBox's searchable popup use Surface defaults. Toggle indicators use Motion internally and respect
365
- animation preferences. The preview Program demonstrates each input's
366
- sizes, colors, state, and interaction without overriding its visual defaults.
346
+ accessible behavioral primitive rather than recreating those systems.
367
347
 
368
348
  ## Tabs
369
349
 
370
350
  `Tabs` organizes related peer views under one selected string identity. Its
371
- horizontal list divides the available width equally, while its active indicator
372
- moves continuously between selections.
351
+ list is a recessed track divided into equal columns, and the selected Tab rises
352
+ out of it, moving continuously between selections.
373
353
 
374
354
  ```tsx
375
355
  <Tabs defaultValue="overview">
@@ -402,8 +382,8 @@ the requested column and direction without taking ownership of the data order.
402
382
  onSortChange={setSort}
403
383
  >
404
384
  <Table.Header>
405
- <Table.Column id="name" isRowHeader allowsSorting>Name</Table.Column>
406
- <Table.Column id="state" allowsSorting>State</Table.Column>
385
+ <Table.Column id="name" rowHeader sortable>Name</Table.Column>
386
+ <Table.Column id="state" sortable>State</Table.Column>
407
387
  </Table.Header>
408
388
  <Table.Body>
409
389
  <Table.Row id="editor">
@@ -437,7 +417,7 @@ indeterminate progress omits the current value and presents ongoing activity.
437
417
 
438
418
  The default range is `0` through `100`. `formatOptions` controls the generated
439
419
  value text, while `valueLabel` supplies an explicit visible and accessible
440
- value. Color, size, typography, and motion follow the active React UI contracts.
420
+ value. It fills a recessed rail with `primary` unless `color` says otherwise.
441
421
 
442
422
  ## Development
443
423
 
@@ -1,14 +1,14 @@
1
1
  import type { CSSProperties, ReactElement, ReactNode } from "react";
2
2
  import type { DisclosureGroupProps as AriaDisclosureGroupProps } from "react-aria-components";
3
- import type { ControlColor } from "./control.js";
3
+ import type { Color } from "./foundation/color.js";
4
4
  import { DisclosureContent, DisclosureTrigger, type DisclosureRootProps } from "./disclosure.js";
5
- import { type LayoutGap } from "./layout.js";
6
- import type { RadiusProps } from "./radius.js";
7
- import type { ScaleLevel } from "./scale.js";
5
+ import type { LayoutGap } from "./foundation/layout.js";
6
+ import type { RadiusProps } from "./foundation/radius.js";
7
+ import type { ScaleLevel } from "./foundation/scale.js";
8
8
  type AccordionRootBaseProps = Omit<AriaDisclosureGroupProps, "allowsMultipleExpanded" | "className" | "defaultExpandedKeys" | "expandedKeys" | "isDisabled" | "onExpandedChange" | "style"> & RadiusProps & Readonly<{
9
9
  children?: ReactNode;
10
10
  className?: string;
11
- color?: ControlColor;
11
+ color?: Color;
12
12
  disabled?: boolean;
13
13
  gap?: LayoutGap;
14
14
  size?: ScaleLevel;
@@ -29,7 +29,8 @@ export type AccordionMultipleExpansionProps = Readonly<{
29
29
  export type AccordionRootProps = AccordionRootBaseProps & (AccordionSingleExpansionProps | AccordionMultipleExpansionProps);
30
30
  /** A coordinated group of identified Disclosure items. */
31
31
  export declare const AccordionRoot: import("react").ForwardRefExoticComponent<AccordionRootProps & import("react").RefAttributes<HTMLDivElement>>;
32
- export interface AccordionItemProps extends Omit<DisclosureRootProps, "defaultExpanded" | "expanded" | "onChange"> {
32
+ export interface AccordionItemProps extends Omit<DisclosureRootProps, "defaultExpanded" | "expanded" | "id" | "onExpandedChange"> {
33
+ /** Identity of this Item within its Accordion. */
33
34
  readonly id: string;
34
35
  }
35
36
  /** One identified Disclosure governed by its nearest Accordion. */
package/dist/accordion.js CHANGED
@@ -2,18 +2,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, forwardRef, useContext, useMemo } from "react";
3
3
  import { DisclosureGroup as AriaDisclosureGroup } from "react-aria-components";
4
4
  import { DisclosureContent, DisclosureRoot, DisclosureTrigger } from "./disclosure.js";
5
- import { resolveGap } from "./layout.js";
6
- import { stringKeys } from "./selection.js";
7
- import { useAppearance } from "./ui-provider.js";
5
+ import { resolveSpacing } from "./foundation/spacing.js";
6
+ import { useVisual } from "./foundation/visual.js";
7
+ import { stringKeys } from "./control/selection.js";
8
8
  const AccordionThemeContext = createContext(null);
9
9
  /** A coordinated group of identified Disclosure items. */
10
10
  export const AccordionRoot = forwardRef(function AccordionRoot(properties, ref) {
11
11
  const { children, className, color, defaultValue, disabled = false, gap = "xsmall", multiple = false, onChange, radius = "medium", size = "medium", style, value, ...native } = properties;
12
- const appearance = useAppearance();
12
+ const { spacing } = useVisual();
13
13
  const context = useMemo(() => ({ color, radius, size }), [color, radius, size]);
14
14
  return _jsx(AccordionThemeContext.Provider, { value: context, children: _jsx(AriaDisclosureGroup, { ...native, ...expansionProperties(properties), ref: ref, allowsMultipleExpanded: multiple, className: className, isDisabled: disabled, style: {
15
15
  display: "grid",
16
- gap: resolveGap(gap, appearance),
16
+ gap: resolveSpacing(gap, spacing),
17
17
  width: "100%",
18
18
  minWidth: 0,
19
19
  ...style
@@ -4,8 +4,10 @@ import { DialogBackdrop, DialogBody, DialogClose, DialogContent, DialogDescripti
4
4
  export function AlertDialogRoot(properties) {
5
5
  return _jsx(DialogRoot, { ...properties });
6
6
  }
7
+ // An alert asks for an explicit decision, so neither pressing outside nor
8
+ // Escape dismisses it unless the caller allows that.
7
9
  export const AlertDialogBackdrop = forwardRef(function AlertDialogBackdrop(properties, ref) {
8
- return _jsx(DialogBackdrop, { ...properties, ref: ref, isDismissable: properties.isDismissable ?? false, isKeyboardDismissDisabled: properties.isKeyboardDismissDisabled ?? true });
10
+ return _jsx(DialogBackdrop, { ...properties, ref: ref, dismissable: properties.dismissable ?? false, keyboardDismissable: properties.keyboardDismissable ?? false });
9
11
  });
10
12
  export const AlertDialogContent = forwardRef(function AlertDialogContent(properties, ref) {
11
13
  return _jsx(DialogContent, { ...properties, ref: ref, role: "alertdialog" });
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ import { type Color } from "./foundation/color.js";
3
+ import { type SurfaceProps } from "./surface/surface.js";
4
+ export interface AlertProps extends Omit<SurfaceProps, "color" | "title" | "children"> {
5
+ /** The meaning, as a color role: `info` by default; `warning` and `danger` also interrupt. */
6
+ readonly color?: Color;
7
+ readonly title?: ReactNode;
8
+ /** Replaces the icon of the color's meaning; `false` leaves the Alert without one. */
9
+ readonly icon?: ReactNode | false;
10
+ readonly children?: ReactNode;
11
+ }
12
+ /**
13
+ * A message that stays in place, such as a failed save. It is painted in the
14
+ * subtle level of its color, like a selection, and leads with the icon of its
15
+ * meaning. Warnings and dangers are announced as they appear.
16
+ */
17
+ export declare const Alert: import("react").ForwardRefExoticComponent<Omit<AlertProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
package/dist/alert.js ADDED
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { CircleAlert, CircleCheck, Info, TriangleAlert } from "lucide-react";
4
+ import { controlFontSizes, controlFontWeight, useControlMetrics } from "./control/control.js";
5
+ import { iconProps } from "./control/icon.js";
6
+ import { colorLevel, resolveColor } from "./foundation/color.js";
7
+ import { resolveRadius } from "./foundation/radius.js";
8
+ import { Surface } from "./surface/surface.js";
9
+ const meaningIcons = { danger: CircleAlert, warning: TriangleAlert, success: CircleCheck };
10
+ /**
11
+ * A message that stays in place, such as a failed save. It is painted in the
12
+ * subtle level of its color, like a selection, and leads with the icon of its
13
+ * meaning. Warnings and dangers are announced as they appear.
14
+ */
15
+ export const Alert = forwardRef(function Alert({ color = "info", title, icon, children, radius, style, ...properties }, ref) {
16
+ const metrics = useControlMetrics();
17
+ const { colors } = metrics.visual;
18
+ const urgent = color === "danger" || color === "warning";
19
+ const MeaningIcon = meaningIcons[color] ?? Info;
20
+ return _jsxs(Surface, { ...properties, ref: ref, role: urgent ? "alert" : "status", color: colorLevel(resolveColor(color, colors), "subtle", colors), depth: "flat", radius: radius ?? resolveRadius("medium", metrics.visual.radius), style: {
21
+ display: "grid",
22
+ gridTemplateColumns: icon === false ? "minmax(0, 1fr)" : "auto minmax(0, 1fr)",
23
+ alignItems: "start",
24
+ gap: metrics.gap * 2,
25
+ padding: metrics.spacing,
26
+ fontSize: controlFontSizes.medium,
27
+ lineHeight: 1.45,
28
+ ...style
29
+ }, children: [icon !== false && _jsx("span", { style: { display: "grid", height: "1.45em", alignItems: "center" }, children: icon ?? _jsx(MeaningIcon, { ...iconProps(16) }) }), _jsxs("div", { style: { display: "grid", gap: 2, minWidth: 0 }, children: [title != null && _jsx("strong", { style: { fontWeight: controlFontWeight }, children: title }), children != null && _jsx("div", { children: children })] })] });
30
+ });
@@ -0,0 +1,13 @@
1
+ import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
2
+ export interface AppLayoutProps extends HTMLAttributes<HTMLDivElement> {
3
+ readonly children: ReactNode;
4
+ /** The sidebar's width; eighteen times the Appearance spacing by default. */
5
+ readonly sidebarWidth?: CSSProperties["width"];
6
+ }
7
+ export type AppLayoutRegionProps = HTMLAttributes<HTMLElement>;
8
+ export declare const AppLayout: import("react").ForwardRefExoticComponent<AppLayoutProps & import("react").RefAttributes<HTMLDivElement>> & {
9
+ Sidebar: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
10
+ Header: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
11
+ Content: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
12
+ Footer: import("react").ForwardRefExoticComponent<AppLayoutRegionProps & import("react").RefAttributes<HTMLElement>>;
13
+ };