@apostel/bedrock 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/accessible-icon/parts.d.ts +16 -0
  4. package/dist/accessible-icon/parts.d.ts.map +1 -0
  5. package/dist/accessible-icon/parts.js +24 -0
  6. package/dist/accessible-icon/parts.js.map +1 -0
  7. package/dist/accordion/controlled-root.d.ts +19 -0
  8. package/dist/accordion/controlled-root.d.ts.map +1 -0
  9. package/dist/accordion/controlled-root.js +53 -0
  10. package/dist/accordion/controlled-root.js.map +1 -0
  11. package/dist/accordion/parts.d.ts +35 -0
  12. package/dist/accordion/parts.d.ts.map +1 -0
  13. package/dist/accordion/parts.js +52 -0
  14. package/dist/accordion/parts.js.map +1 -0
  15. package/dist/accordion/root.d.ts +24 -0
  16. package/dist/accordion/root.d.ts.map +1 -0
  17. package/dist/accordion/root.js +45 -0
  18. package/dist/accordion/root.js.map +1 -0
  19. package/dist/accordion/shared.d.ts +23 -0
  20. package/dist/accordion/shared.d.ts.map +1 -0
  21. package/dist/accordion/shared.js +16 -0
  22. package/dist/accordion/shared.js.map +1 -0
  23. package/dist/alert-dialog/parts.d.ts +16 -0
  24. package/dist/alert-dialog/parts.d.ts.map +1 -0
  25. package/dist/alert-dialog/parts.js +20 -0
  26. package/dist/alert-dialog/parts.js.map +1 -0
  27. package/dist/anchor.d.ts +27 -0
  28. package/dist/anchor.d.ts.map +1 -0
  29. package/dist/anchor.js +70 -0
  30. package/dist/anchor.js.map +1 -0
  31. package/dist/aspect-ratio/parts.d.ts +16 -0
  32. package/dist/aspect-ratio/parts.d.ts.map +1 -0
  33. package/dist/aspect-ratio/parts.js +15 -0
  34. package/dist/aspect-ratio/parts.js.map +1 -0
  35. package/dist/avatar/parts.d.ts +23 -0
  36. package/dist/avatar/parts.d.ts.map +1 -0
  37. package/dist/avatar/parts.js +51 -0
  38. package/dist/avatar/parts.js.map +1 -0
  39. package/dist/bedrock.css +64 -0
  40. package/dist/capabilities.d.ts +25 -0
  41. package/dist/capabilities.d.ts.map +1 -0
  42. package/dist/capabilities.js +40 -0
  43. package/dist/capabilities.js.map +1 -0
  44. package/dist/checkbox/controlled-root.d.ts +18 -0
  45. package/dist/checkbox/controlled-root.d.ts.map +1 -0
  46. package/dist/checkbox/controlled-root.js +28 -0
  47. package/dist/checkbox/controlled-root.js.map +1 -0
  48. package/dist/checkbox/parts.d.ts +29 -0
  49. package/dist/checkbox/parts.d.ts.map +1 -0
  50. package/dist/checkbox/parts.js +42 -0
  51. package/dist/checkbox/parts.js.map +1 -0
  52. package/dist/client-render.d.ts +10 -0
  53. package/dist/client-render.d.ts.map +1 -0
  54. package/dist/client-render.js +14 -0
  55. package/dist/client-render.js.map +1 -0
  56. package/dist/collapsible/controlled-root.d.ts +17 -0
  57. package/dist/collapsible/controlled-root.d.ts.map +1 -0
  58. package/dist/collapsible/controlled-root.js +21 -0
  59. package/dist/collapsible/controlled-root.js.map +1 -0
  60. package/dist/collapsible/parts.d.ts +14 -0
  61. package/dist/collapsible/parts.d.ts.map +1 -0
  62. package/dist/collapsible/parts.js +24 -0
  63. package/dist/collapsible/parts.js.map +1 -0
  64. package/dist/collapsible/root.d.ts +16 -0
  65. package/dist/collapsible/root.d.ts.map +1 -0
  66. package/dist/collapsible/root.js +28 -0
  67. package/dist/collapsible/root.js.map +1 -0
  68. package/dist/collapsible/shared.d.ts +11 -0
  69. package/dist/collapsible/shared.d.ts.map +1 -0
  70. package/dist/collapsible/shared.js +26 -0
  71. package/dist/collapsible/shared.js.map +1 -0
  72. package/dist/compose-refs.d.ts +29 -0
  73. package/dist/compose-refs.d.ts.map +1 -0
  74. package/dist/compose-refs.js +67 -0
  75. package/dist/compose-refs.js.map +1 -0
  76. package/dist/context-menu/root.d.ts +17 -0
  77. package/dist/context-menu/root.d.ts.map +1 -0
  78. package/dist/context-menu/root.js +50 -0
  79. package/dist/context-menu/root.js.map +1 -0
  80. package/dist/controlled.d.ts +120 -0
  81. package/dist/controlled.d.ts.map +1 -0
  82. package/dist/controlled.js +91 -0
  83. package/dist/controlled.js.map +1 -0
  84. package/dist/create-controlled-root.d.ts +29 -0
  85. package/dist/create-controlled-root.d.ts.map +1 -0
  86. package/dist/create-controlled-root.js +92 -0
  87. package/dist/create-controlled-root.js.map +1 -0
  88. package/dist/dialog/controlled-root.d.ts +15 -0
  89. package/dist/dialog/controlled-root.d.ts.map +1 -0
  90. package/dist/dialog/controlled-root.js +21 -0
  91. package/dist/dialog/controlled-root.js.map +1 -0
  92. package/dist/dialog/parts.d.ts +18 -0
  93. package/dist/dialog/parts.d.ts.map +1 -0
  94. package/dist/dialog/parts.js +80 -0
  95. package/dist/dialog/parts.js.map +1 -0
  96. package/dist/dialog/root.d.ts +22 -0
  97. package/dist/dialog/root.d.ts.map +1 -0
  98. package/dist/dialog/root.js +39 -0
  99. package/dist/dialog/root.js.map +1 -0
  100. package/dist/dialog/shared.d.ts +32 -0
  101. package/dist/dialog/shared.d.ts.map +1 -0
  102. package/dist/dialog/shared.js +53 -0
  103. package/dist/dialog/shared.js.map +1 -0
  104. package/dist/dropdown-menu/controlled-root.d.ts +7 -0
  105. package/dist/dropdown-menu/controlled-root.d.ts.map +1 -0
  106. package/dist/dropdown-menu/controlled-root.js +13 -0
  107. package/dist/dropdown-menu/controlled-root.js.map +1 -0
  108. package/dist/dropdown-menu/root.d.ts +13 -0
  109. package/dist/dropdown-menu/root.d.ts.map +1 -0
  110. package/dist/dropdown-menu/root.js +22 -0
  111. package/dist/dropdown-menu/root.js.map +1 -0
  112. package/dist/hover-card/controlled-root.d.ts +9 -0
  113. package/dist/hover-card/controlled-root.d.ts.map +1 -0
  114. package/dist/hover-card/controlled-root.js +15 -0
  115. package/dist/hover-card/controlled-root.js.map +1 -0
  116. package/dist/hover-card/root.d.ts +13 -0
  117. package/dist/hover-card/root.d.ts.map +1 -0
  118. package/dist/hover-card/root.js +19 -0
  119. package/dist/hover-card/root.js.map +1 -0
  120. package/dist/index.d.ts +274 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +211 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/interest.d.ts +30 -0
  125. package/dist/interest.d.ts.map +1 -0
  126. package/dist/interest.js +94 -0
  127. package/dist/interest.js.map +1 -0
  128. package/dist/label/parts.d.ts +14 -0
  129. package/dist/label/parts.d.ts.map +1 -0
  130. package/dist/label/parts.js +21 -0
  131. package/dist/label/parts.js.map +1 -0
  132. package/dist/menu/parts.d.ts +64 -0
  133. package/dist/menu/parts.d.ts.map +1 -0
  134. package/dist/menu/parts.js +131 -0
  135. package/dist/menu/parts.js.map +1 -0
  136. package/dist/menu/shared.d.ts +7 -0
  137. package/dist/menu/shared.d.ts.map +1 -0
  138. package/dist/menu/shared.js +9 -0
  139. package/dist/menu/shared.js.map +1 -0
  140. package/dist/menubar/parts.d.ts +20 -0
  141. package/dist/menubar/parts.d.ts.map +1 -0
  142. package/dist/menubar/parts.js +34 -0
  143. package/dist/menubar/parts.js.map +1 -0
  144. package/dist/navigation-menu/parts.d.ts +28 -0
  145. package/dist/navigation-menu/parts.d.ts.map +1 -0
  146. package/dist/navigation-menu/parts.js +46 -0
  147. package/dist/navigation-menu/parts.js.map +1 -0
  148. package/dist/open-state.d.ts +23 -0
  149. package/dist/open-state.d.ts.map +1 -0
  150. package/dist/open-state.js +106 -0
  151. package/dist/open-state.js.map +1 -0
  152. package/dist/popover/controlled-root.d.ts +14 -0
  153. package/dist/popover/controlled-root.d.ts.map +1 -0
  154. package/dist/popover/controlled-root.js +17 -0
  155. package/dist/popover/controlled-root.js.map +1 -0
  156. package/dist/popover/parts.d.ts +27 -0
  157. package/dist/popover/parts.d.ts.map +1 -0
  158. package/dist/popover/parts.js +39 -0
  159. package/dist/popover/parts.js.map +1 -0
  160. package/dist/popover/root.d.ts +24 -0
  161. package/dist/popover/root.d.ts.map +1 -0
  162. package/dist/popover/root.js +24 -0
  163. package/dist/popover/root.js.map +1 -0
  164. package/dist/popover/shared.d.ts +22 -0
  165. package/dist/popover/shared.d.ts.map +1 -0
  166. package/dist/popover/shared.js +25 -0
  167. package/dist/popover/shared.js.map +1 -0
  168. package/dist/progress/parts.d.ts +25 -0
  169. package/dist/progress/parts.d.ts.map +1 -0
  170. package/dist/progress/parts.js +32 -0
  171. package/dist/progress/parts.js.map +1 -0
  172. package/dist/radio-group/controlled-root.d.ts +19 -0
  173. package/dist/radio-group/controlled-root.d.ts.map +1 -0
  174. package/dist/radio-group/controlled-root.js +29 -0
  175. package/dist/radio-group/controlled-root.js.map +1 -0
  176. package/dist/radio-group/parts.d.ts +36 -0
  177. package/dist/radio-group/parts.d.ts.map +1 -0
  178. package/dist/radio-group/parts.js +62 -0
  179. package/dist/radio-group/parts.js.map +1 -0
  180. package/dist/roving.d.ts +26 -0
  181. package/dist/roving.d.ts.map +1 -0
  182. package/dist/roving.js +124 -0
  183. package/dist/roving.js.map +1 -0
  184. package/dist/scroll-area/parts.d.ts +29 -0
  185. package/dist/scroll-area/parts.d.ts.map +1 -0
  186. package/dist/scroll-area/parts.js +44 -0
  187. package/dist/scroll-area/parts.js.map +1 -0
  188. package/dist/select/parts.d.ts +57 -0
  189. package/dist/select/parts.d.ts.map +1 -0
  190. package/dist/select/parts.js +76 -0
  191. package/dist/select/parts.js.map +1 -0
  192. package/dist/separator/parts.d.ts +14 -0
  193. package/dist/separator/parts.d.ts.map +1 -0
  194. package/dist/separator/parts.js +15 -0
  195. package/dist/separator/parts.js.map +1 -0
  196. package/dist/slider/parts.d.ts +32 -0
  197. package/dist/slider/parts.d.ts.map +1 -0
  198. package/dist/slider/parts.js +52 -0
  199. package/dist/slider/parts.js.map +1 -0
  200. package/dist/slot.d.ts +3 -0
  201. package/dist/slot.d.ts.map +1 -0
  202. package/dist/slot.js +49 -0
  203. package/dist/slot.js.map +1 -0
  204. package/dist/switch/parts.d.ts +21 -0
  205. package/dist/switch/parts.d.ts.map +1 -0
  206. package/dist/switch/parts.js +31 -0
  207. package/dist/switch/parts.js.map +1 -0
  208. package/dist/tabs/controlled-root.d.ts +11 -0
  209. package/dist/tabs/controlled-root.d.ts.map +1 -0
  210. package/dist/tabs/controlled-root.js +15 -0
  211. package/dist/tabs/controlled-root.js.map +1 -0
  212. package/dist/tabs/parts.d.ts +20 -0
  213. package/dist/tabs/parts.d.ts.map +1 -0
  214. package/dist/tabs/parts.js +42 -0
  215. package/dist/tabs/parts.js.map +1 -0
  216. package/dist/tabs/root.d.ts +15 -0
  217. package/dist/tabs/root.d.ts.map +1 -0
  218. package/dist/tabs/root.js +26 -0
  219. package/dist/tabs/root.js.map +1 -0
  220. package/dist/tabs/shared.d.ts +12 -0
  221. package/dist/tabs/shared.d.ts.map +1 -0
  222. package/dist/tabs/shared.js +11 -0
  223. package/dist/tabs/shared.js.map +1 -0
  224. package/dist/toast/parts.d.ts +40 -0
  225. package/dist/toast/parts.d.ts.map +1 -0
  226. package/dist/toast/parts.js +102 -0
  227. package/dist/toast/parts.js.map +1 -0
  228. package/dist/toggle/controlled-root.d.ts +9 -0
  229. package/dist/toggle/controlled-root.d.ts.map +1 -0
  230. package/dist/toggle/controlled-root.js +14 -0
  231. package/dist/toggle/controlled-root.js.map +1 -0
  232. package/dist/toggle/parts.d.ts +20 -0
  233. package/dist/toggle/parts.d.ts.map +1 -0
  234. package/dist/toggle/parts.js +29 -0
  235. package/dist/toggle/parts.js.map +1 -0
  236. package/dist/toggle-group/parts.d.ts +28 -0
  237. package/dist/toggle-group/parts.d.ts.map +1 -0
  238. package/dist/toggle-group/parts.js +57 -0
  239. package/dist/toggle-group/parts.js.map +1 -0
  240. package/dist/toolbar/parts.d.ts +22 -0
  241. package/dist/toolbar/parts.d.ts.map +1 -0
  242. package/dist/toolbar/parts.js +27 -0
  243. package/dist/toolbar/parts.js.map +1 -0
  244. package/dist/tooltip/controlled-root.d.ts +9 -0
  245. package/dist/tooltip/controlled-root.d.ts.map +1 -0
  246. package/dist/tooltip/controlled-root.js +15 -0
  247. package/dist/tooltip/controlled-root.js.map +1 -0
  248. package/dist/tooltip/parts.d.ts +23 -0
  249. package/dist/tooltip/parts.d.ts.map +1 -0
  250. package/dist/tooltip/parts.js +28 -0
  251. package/dist/tooltip/parts.js.map +1 -0
  252. package/dist/tooltip/root.d.ts +13 -0
  253. package/dist/tooltip/root.d.ts.map +1 -0
  254. package/dist/tooltip/root.js +17 -0
  255. package/dist/tooltip/root.js.map +1 -0
  256. package/dist/tooltip/shared.d.ts +13 -0
  257. package/dist/tooltip/shared.d.ts.map +1 -0
  258. package/dist/tooltip/shared.js +9 -0
  259. package/dist/tooltip/shared.js.map +1 -0
  260. package/dist/tooltip/use-controlled-interest-root.d.ts +19 -0
  261. package/dist/tooltip/use-controlled-interest-root.d.ts.map +1 -0
  262. package/dist/tooltip/use-controlled-interest-root.js +31 -0
  263. package/dist/tooltip/use-controlled-interest-root.js.map +1 -0
  264. package/dist/tooltip/use-interest-root.d.ts +16 -0
  265. package/dist/tooltip/use-interest-root.d.ts.map +1 -0
  266. package/dist/tooltip/use-interest-root.js +39 -0
  267. package/dist/tooltip/use-interest-root.js.map +1 -0
  268. package/dist/types.d.ts +55 -0
  269. package/dist/types.d.ts.map +1 -0
  270. package/dist/types.js +2 -0
  271. package/dist/types.js.map +1 -0
  272. package/dist/validate-element.d.ts +12 -0
  273. package/dist/validate-element.d.ts.map +1 -0
  274. package/dist/validate-element.js +21 -0
  275. package/dist/validate-element.js.map +1 -0
  276. package/dist/validate-trigger.d.ts +14 -0
  277. package/dist/validate-trigger.d.ts.map +1 -0
  278. package/dist/validate-trigger.js +59 -0
  279. package/dist/validate-trigger.js.map +1 -0
  280. package/dist/visually-hidden/parts.d.ts +12 -0
  281. package/dist/visually-hidden/parts.d.ts.map +1 -0
  282. package/dist/visually-hidden/parts.js +25 -0
  283. package/dist/visually-hidden/parts.js.map +1 -0
  284. package/package.json +81 -0
  285. package/skills/migrate-to-bedrock/SKILL.md +188 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam Apostel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # bedrock
2
+
3
+ Headless React primitives built on the browser instead of on top of it.
4
+
5
+ Radix-shaped anatomy — compound components, `asChild`, the same part names you
6
+ already type. Different foundation: the top layer, invoker commands, anchor
7
+ positioning, and `@starting-style` do the work that `Portal`, `Presence`,
8
+ `DismissableLayer`, and Floating UI used to do.
9
+
10
+ ```bash
11
+ npm i @apostel/bedrock
12
+ ```
13
+
14
+ Docs, and a compat table that tests the browser you open it in:
15
+ **<https://bedrock.sams.land>**
16
+
17
+ ## The shape
18
+
19
+ ```tsx
20
+ import { Dialog } from '@apostel/bedrock'
21
+
22
+ <Dialog.Root>
23
+ <Dialog.Trigger>Delete project</Dialog.Trigger>
24
+ <Dialog.Content>
25
+ <Dialog.Title>Delete project?</Dialog.Title>
26
+ <Dialog.Description>This cannot be undone.</Dialog.Description>
27
+ <Dialog.Close>Cancel</Dialog.Close>
28
+ <button onClick={destroy}>Delete</button>
29
+ </Dialog.Content>
30
+ </Dialog.Root>
31
+ ```
32
+
33
+ That renders roughly:
34
+
35
+ ```html
36
+ <button commandfor="d1" command="show-modal">Delete project</button>
37
+ <dialog id="d1">…</dialog>
38
+ ```
39
+
40
+ No effect opens it. No state hook holds it. No portal moves it. The button is
41
+ wired to the dialog by the parser, so it works before hydration and keeps
42
+ working if your JS bundle fails to load.
43
+
44
+ ## Two roots
45
+
46
+ The DOM owns open state. Most of the time that's all you need, so that's the
47
+ default — and it costs nothing.
48
+
49
+ **`Dialog.Root`** — the DOM opens and closes itself. Accepts `defaultOpen` and a
50
+ read-only `onOpenChange`, which is a `toggle` listener and nothing more. This
51
+ covers the common case that gets miscategorised as "controlled": *I need to know
52
+ when it closed so I can reset the form.*
53
+
54
+ **`Dialog.Root` from `@apostel/bedrock/controlled`** — React gets a veto. Your
55
+ `open` prop decides whether an open is allowed to proceed.
56
+
57
+ ```tsx
58
+ import { Dialog } from '@apostel/bedrock/controlled'
59
+
60
+ <Dialog.Root open={open} onOpenChange={setOpen}>
61
+ {/* identical children */}
62
+ </Dialog.Root>
63
+ ```
64
+
65
+ Same component names, same parts, same props on every part. Swapping is a
66
+ one-line import change, and a codemod if you're doing it across a repo.
67
+
68
+ The two entry points are separate module graphs, so if nothing in your app
69
+ imports `/controlled`, none of the reconciliation code reaches your bundle. This
70
+ is a real guarantee from the `exports` map, not a tree-shaking hope.
71
+
72
+ ### How the veto works
73
+
74
+ Controlled mode is **DOM leads, React vetoes**, not React-owns-state:
75
+
76
+ 1. User clicks. The browser fires `beforetoggle`, which is cancelable for
77
+ popovers. If your `open` prop disagrees, we `preventDefault()` and nothing
78
+ moved.
79
+ 2. `onOpenChange` fires either way, so you can decide.
80
+ 3. If your prop changes without user interaction, a reconciliation effect moves
81
+ the DOM to match.
82
+
83
+ Where the platform gives no cancelable hook, step 1 is skipped and step 3 puts
84
+ things back — one frame of visible movement in the refuse case only. Modal
85
+ dialogs get an extra veto on close via `request-close` and the `cancel` event.
86
+
87
+ ## `asChild` and the button rule
88
+
89
+ Invoker commands work on `<button>` only. Interest invokers also work on `<a>`.
90
+ So this is fine:
91
+
92
+ ```tsx
93
+ <Tooltip.Trigger asChild><a href="/pricing">Pricing</a></Tooltip.Trigger>
94
+ ```
95
+
96
+ and this is not:
97
+
98
+ ```tsx
99
+ <Dialog.Trigger asChild><div onClick={…}>Open</div></Dialog.Trigger>
100
+ ```
101
+
102
+ A trigger that isn't a button gets no `commandfor` wiring, which means no
103
+ keyboard activation and no implicit `aria-expanded`. That's an accessibility
104
+ regression, and it's silent, so we make noise about it: **development throws**
105
+ at mount, with the tag it found and the tags that part accepts. Production logs
106
+ an error at the same check point.
107
+
108
+ There is no fallback click handler. Attaching one would ship the exact
109
+ imperative trigger machinery this library exists to delete, to every consumer,
110
+ forever — and nobody removes a fallback that works.
111
+
112
+ If you're wrapping a third-party component that renders a div and can't change
113
+ it, take the props and own the consequences:
114
+
115
+ ```tsx
116
+ const props = useDialogTrigger(id)
117
+ <ThirdPartyThing {...props} />
118
+ ```
119
+
120
+ The same check catches `<button>` inside a `<form>` without `type="button"`.
121
+ Submit buttons can't invoke popovers — submitting and opening are conflicting
122
+ behaviours — and that one bites people more often than the div case.
123
+
124
+ ## What's native and what still ships JS
125
+
126
+ Be clear-eyed about this. The platform deleted positioning and layering. It did
127
+ not delete focus management.
128
+
129
+ **Almost entirely native** — AspectRatio, Separator, Label, VisuallyHidden,
130
+ Progress, Slider, ScrollArea, Collapsible, Accordion, Checkbox, Switch,
131
+ RadioGroup, Select, Dialog, AlertDialog, Popover.
132
+
133
+ Several of those are one element and no state at all: `Progress` is
134
+ `<progress>`, `Slider` is `<input type="range">`, `Select` is a `<select>` under
135
+ `appearance: base-select`, and `Accordion` gets single-open exclusivity from
136
+ `<details name>` rather than from an effect that closes its siblings.
137
+
138
+ **Native layering, small JS state layer** — Tooltip, HoverCard, Avatar, Toggle,
139
+ Toast.
140
+
141
+ **Still substantially JS** — DropdownMenu, ContextMenu, Menubar,
142
+ NavigationMenu, Tabs, Toolbar, ToggleGroup.
143
+
144
+ The reason is short: there is no native roving tabindex, no native typeahead, no
145
+ focus trap for non-modal layers, and no way to anchor to a pointer coordinate.
146
+ Every menu-shaped primitive needs all four, and they all share one `roving.ts` —
147
+ which ships from either entry point, so the two-root split saves them almost
148
+ nothing. The docs say that rather than implying otherwise.
149
+
150
+ Measured, not estimated — esbuild, minified, gzipped, React external:
151
+
152
+ | primitive | bedrock | Radix | |
153
+ | --- | --- | --- | --- |
154
+ | Checkbox | **0.70 kB** | 5.9 kB | 8.5× |
155
+ | Slider | **0.74 kB** | 9.8 kB | 13× |
156
+ | Select | **0.84 kB** | 31.5 kB | 37× |
157
+ | Accordion | **1.61 kB** | 8.8 kB | 5.5× |
158
+ | Dialog | **1.95 kB** | 13.7 kB | 7× |
159
+ | Popover | **2.11 kB** | 24.2 kB | 11× |
160
+ | Tooltip | **2.55 kB** | 19.3 kB | 7.6× |
161
+ | DropdownMenu | **3.55 kB** | 31.6 kB | 8.9× |
162
+
163
+ The ratio narrows as the roving module gets involved and widens where the
164
+ platform has a whole element to hand over. The bigger win is behavioural — no
165
+ z-index fights, no portal-and-clipping bugs, no positioning recalculation on
166
+ every scroll frame.
167
+
168
+ ## Browser support
169
+
170
+ Latest Chrome, deliberately, for now.
171
+
172
+ `appearance: base-select`, `interpolate-size`, `::scroll-marker`, and
173
+ `popover=hint` are shipped or shipping across engines, so that bet is about
174
+ timing. `interestfor` is the one that isn't standardised yet, and it's what
175
+ drives Tooltip and HoverCard triggers.
176
+
177
+ Because of that, **no platform feature appears in a public prop name.**
178
+ `Tooltip.Root` takes `delayDuration`; today it becomes `interest-show-delay`,
179
+ tomorrow it might become a timer. Feature detection lives in one module, every
180
+ non-Baseline CSS block sits behind `@supports`, and the implementation can be
181
+ swapped without a major version.
182
+
183
+ ## Styling
184
+
185
+ Ship `bedrock.css` or don't — every part takes `className` and renders one
186
+ element. Open and closed states are selectable with `:open` and
187
+ `:popover-open`; enter and exit animation is `@starting-style` plus
188
+ `transition-behavior: allow-discrete`, no `forceMount`, no presence wrapper.
189
+
190
+ ## Docs
191
+
192
+ [`docs/`](./docs/README.md) — getting started, the Dialog API, styling, the
193
+ [migration guide](./docs/migration-from-radix.md), and a
194
+ [shadcn registry](./docs/shadcn-registry.md) that swaps Radix for bedrock in
195
+ shadcn's own components.
196
+
197
+ [`docs/compat.html`](./docs/compat.html) — live at
198
+ <https://bedrock.sams.land/compat.html> — tests every platform feature bedrock
199
+ uses in whatever browser you open it in, and says what each missing one does to
200
+ your UI. Chrome 141 scores 20 of 21.
201
+
202
+ Migrating with an agent: [`skills/migrate-to-bedrock`](./skills/migrate-to-bedrock/SKILL.md),
203
+ a Claude Code skill that does the mechanical work and brings you the four
204
+ decisions it cannot make for you.
205
+
206
+ Contributing is [`CONTRIBUTING.md`](./CONTRIBUTING.md); releasing is
207
+ [`RELEASING.md`](./RELEASING.md).
208
+
209
+ [`docs/gaps.md`](./docs/gaps.md) is the case against adopting this. Read it
210
+ first if you are evaluating.
211
+
212
+ ## Status
213
+
214
+ Pre-alpha, but no longer a list: all 29 primitives exist, with 97 Playwright
215
+ tests against real Chrome. What is missing is soak time, other engines, and the
216
+ things named in [gaps](./docs/gaps.md) — not components.
@@ -0,0 +1,16 @@
1
+ import { type ReactElement } from 'react';
2
+ export interface AccessibleIconProps {
3
+ /** What a screen reader should announce in place of the glyph. */
4
+ label: string;
5
+ children: ReactElement;
6
+ }
7
+ /**
8
+ * Radix hides the icon and appends a visually hidden `<span>` carrying the
9
+ * label. Two elements, one of them off-screen text that has to be styled.
10
+ *
11
+ * `role="img"` plus `aria-label` says the same thing to assistive technology in
12
+ * one element and no CSS — an image with an accessible name, which is what an
13
+ * icon is.
14
+ */
15
+ export declare function AccessibleIconRoot({ label, children }: AccessibleIconProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
16
+ //# sourceMappingURL=parts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../../src/accessible-icon/parts.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAGvE,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,YAAY,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,mBAAmB,8EAe1E"}
@@ -0,0 +1,24 @@
1
+ import { cloneElement, isValidElement } from 'react';
2
+ /**
3
+ * Radix hides the icon and appends a visually hidden `<span>` carrying the
4
+ * label. Two elements, one of them off-screen text that has to be styled.
5
+ *
6
+ * `role="img"` plus `aria-label` says the same thing to assistive technology in
7
+ * one element and no CSS — an image with an accessible name, which is what an
8
+ * icon is.
9
+ */
10
+ export function AccessibleIconRoot({ label, children }) {
11
+ if (!isValidElement(children)) {
12
+ throw new Error('[bedrock] AccessibleIcon expects exactly one React element child.');
13
+ }
14
+ const props = children.props;
15
+ return cloneElement(children, {
16
+ ...props,
17
+ role: 'img',
18
+ 'aria-label': label,
19
+ 'aria-hidden': undefined,
20
+ focusable: false,
21
+ 'data-bedrock-accessible-icon': '',
22
+ });
23
+ }
24
+ //# sourceMappingURL=parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.js","sourceRoot":"","sources":["../../src/accessible-icon/parts.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAqB,MAAM,OAAO,CAAA;AASvE;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAuB;IACzE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAA;IACtF,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAiB,CAAA;IAExC,OAAO,YAAY,CAAC,QAAQ,EAAE;QAC5B,GAAG,KAAK;QACR,IAAI,EAAE,KAAK;QACX,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,SAAS;QACxB,SAAS,EAAE,KAAK;QAChB,8BAA8B,EAAE,EAAE;KACvB,CAAC,CAAA;AAChB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import type { AsChildProps } from '../types.js';
3
+ export interface ControlledAccordionRootProps extends Omit<ComponentPropsWithRef<'div'>, 'defaultValue'>, AsChildProps {
4
+ type?: 'single' | 'multiple';
5
+ value: string | string[];
6
+ orientation?: 'horizontal' | 'vertical';
7
+ onValueChange?: ((value: string[]) => void) | undefined;
8
+ }
9
+ /**
10
+ * Reconciles by walking its own subtree rather than by telling each item what
11
+ * to do, which is what keeps `Accordion.Item` byte-identical under both roots.
12
+ * Items publish their value as a data attribute; nothing about that costs the
13
+ * plain root anything.
14
+ *
15
+ * `<details>` has no cancelable toggle, so this cannot refuse before the fact:
16
+ * the disclosure moves, you are told, and it moves back if you decline.
17
+ */
18
+ export declare function AccordionRoot({ asChild, children, type, value, orientation, onValueChange, ref, ...props }: ControlledAccordionRootProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=controlled-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlled-root.d.ts","sourceRoot":"","sources":["../../src/accordion/controlled-root.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,qBAAqB,EAE3B,MAAM,OAAO,CAAA;AAGd,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC,EAAE,YAAY;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC5B,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACxB,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CACxD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,IAAe,EACf,KAAK,EACL,WAAwB,EACxB,aAAa,EACb,GAAG,EACH,GAAG,KAAK,EACT,EAAE,4BAA4B,+BAuD9B"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useId, useMemo, useRef, } from 'react';
3
+ import { useComposedRefs } from '../compose-refs.js';
4
+ import { Slot } from '../slot.js';
5
+ import { AccordionContext } from './shared.js';
6
+ function toArray(value) {
7
+ return Array.isArray(value) ? value : [value];
8
+ }
9
+ /**
10
+ * Reconciles by walking its own subtree rather than by telling each item what
11
+ * to do, which is what keeps `Accordion.Item` byte-identical under both roots.
12
+ * Items publish their value as a data attribute; nothing about that costs the
13
+ * plain root anything.
14
+ *
15
+ * `<details>` has no cancelable toggle, so this cannot refuse before the fact:
16
+ * the disclosure moves, you are told, and it moves back if you decline.
17
+ */
18
+ export function AccordionRoot({ asChild, children, type = 'single', value, orientation = 'vertical', onValueChange, ref, ...props }) {
19
+ const name = useId();
20
+ const Part = asChild ? Slot : 'div';
21
+ const nodeRef = useRef(null);
22
+ const changeRef = useRef(onValueChange);
23
+ changeRef.current = onValueChange;
24
+ const values = useMemo(() => toArray(value), [value]);
25
+ const report = useCallback((changed, open) => {
26
+ const next = open
27
+ ? [...new Set([...values, changed])]
28
+ : values.filter((entry) => entry !== changed);
29
+ changeRef.current?.(next);
30
+ }, [values]);
31
+ useEffect(() => {
32
+ const root = nodeRef.current;
33
+ if (!root)
34
+ return;
35
+ for (const item of root.querySelectorAll('[data-bedrock-accordion-item]')) {
36
+ const itemValue = item.dataset.value;
37
+ const shouldBeOpen = itemValue !== undefined && values.includes(itemValue);
38
+ if (item.open !== shouldBeOpen)
39
+ item.open = shouldBeOpen;
40
+ }
41
+ }, [values]);
42
+ const context = useMemo(() => ({
43
+ name: type === 'single' ? name : undefined,
44
+ orientation,
45
+ report,
46
+ value: values,
47
+ defaultValue: [],
48
+ }), [name, type, orientation, report, values]);
49
+ return (_jsx(AccordionContext.Provider, { value: context, children: _jsx(Part, { ...props, ref: useComposedRefs(ref, (node) => {
50
+ nodeRef.current = node;
51
+ }), "data-orientation": orientation, "data-bedrock-accordion": "", children: children }) }));
52
+ }
53
+ //# sourceMappingURL=controlled-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlled-root.js","sourceRoot":"","sources":["../../src/accordion/controlled-root.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import type { AsChildProps } from '../types.js';
3
+ export interface AccordionItemProps extends Omit<ComponentPropsWithRef<'details'>, 'value'>, AsChildProps {
4
+ value: string;
5
+ disabled?: boolean;
6
+ }
7
+ /**
8
+ * A `<details>`, sharing its `name` with its siblings when the root is
9
+ * `type="single"`. Exclusivity is the browser's, so it survives with no
10
+ * JavaScript and cannot get out of step with React state.
11
+ */
12
+ export declare function AccordionItem({ asChild, children, value, disabled, ref, ...props }: AccordionItemProps): import("react").JSX.Element;
13
+ export interface AccordionHeaderProps extends ComponentPropsWithRef<'summary'>, AsChildProps {
14
+ }
15
+ /**
16
+ * The `<summary>`. Radix renders `<h3><button aria-expanded>`; the platform's
17
+ * disclosure pattern is a summary, which carries the expanded state itself.
18
+ *
19
+ * If you need the heading semantics the APG asks for, put a heading *inside*
20
+ * this — `<Accordion.Header><h3>…</h3></Accordion.Header>` is valid and keeps
21
+ * the summary's behaviour.
22
+ */
23
+ export declare function AccordionHeader({ asChild, ...props }: AccordionHeaderProps): import("react").JSX.Element;
24
+ export interface AccordionTriggerProps extends ComponentPropsWithRef<'span'>, AsChildProps {
25
+ }
26
+ /**
27
+ * Sits inside the header and is not itself interactive — the summary already
28
+ * is, and nesting a button inside it would produce two tab stops for one
29
+ * control. It exists so Radix-shaped markup keeps its styling hook.
30
+ */
31
+ export declare function AccordionTrigger({ asChild, ...props }: AccordionTriggerProps): import("react").JSX.Element;
32
+ export interface AccordionContentProps extends ComponentPropsWithRef<'div'>, AsChildProps {
33
+ }
34
+ export declare function AccordionContent({ asChild, children, ...props }: AccordionContentProps): import("react").JSX.Element;
35
+ //# sourceMappingURL=parts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../../src/accordion/parts.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,qBAAqB,EAAoB,MAAM,OAAO,CAAA;AAKlG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,GAAG,EACH,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAiCpB;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB,CAAC,SAAS,CAAC,EAAE,YAAY;CAAG;AAE/F;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,oBAAoB,+BAI1E;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB,CAAC,MAAM,CAAC,EAAE,YAAY;CAAG;AAE7F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,+BAI5E;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB,CAAC,KAAK,CAAC,EAAE,YAAY;CAAG;AAE5F,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,+BAUtF"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useRef } from 'react';
3
+ import { useClientRender } from '../client-render.js';
4
+ import { useComposedRefs } from '../compose-refs.js';
5
+ import { useOpenState } from '../open-state.js';
6
+ import { Slot } from '../slot.js';
7
+ import { AccordionItemContext, useAccordionContext, useAccordionItemContext } from './shared.js';
8
+ /**
9
+ * A `<details>`, sharing its `name` with its siblings when the root is
10
+ * `type="single"`. Exclusivity is the browser's, so it survives with no
11
+ * JavaScript and cannot get out of step with React state.
12
+ */
13
+ export function AccordionItem({ asChild, children, value, disabled, ref, ...props }) {
14
+ const { name, report, value: controlled, defaultValue } = useAccordionContext('Accordion.Item');
15
+ const Part = asChild ? Slot : 'details';
16
+ const reportRef = useRef(report);
17
+ reportRef.current = report;
18
+ const onToggle = useCallback((next) => reportRef.current(value, next), [value]);
19
+ const startsOpen = (controlled ?? defaultValue).includes(value);
20
+ const { open, observe } = useOpenState(onToggle, startsOpen);
21
+ const startsOpenRef = useRef(startsOpen);
22
+ const item = useMemo(() => ({ value, open }), [value, open]);
23
+ return (_jsx(AccordionItemContext.Provider, { value: item, children: _jsx(Part, { ...props, name: name, open: startsOpenRef.current, "data-value": value, "data-disabled": disabled ? '' : undefined, ref: useComposedRefs(ref, observe), "data-bedrock-accordion-item": "", children: children }) }));
24
+ }
25
+ /**
26
+ * The `<summary>`. Radix renders `<h3><button aria-expanded>`; the platform's
27
+ * disclosure pattern is a summary, which carries the expanded state itself.
28
+ *
29
+ * If you need the heading semantics the APG asks for, put a heading *inside*
30
+ * this — `<Accordion.Header><h3>…</h3></Accordion.Header>` is valid and keeps
31
+ * the summary's behaviour.
32
+ */
33
+ export function AccordionHeader({ asChild, ...props }) {
34
+ const Part = asChild ? Slot : 'summary';
35
+ return _jsx(Part, { ...props, "data-bedrock-accordion-header": "" });
36
+ }
37
+ /**
38
+ * Sits inside the header and is not itself interactive — the summary already
39
+ * is, and nesting a button inside it would produce two tab stops for one
40
+ * control. It exists so Radix-shaped markup keeps its styling hook.
41
+ */
42
+ export function AccordionTrigger({ asChild, ...props }) {
43
+ const Part = asChild ? Slot : 'span';
44
+ return _jsx(Part, { ...props, "data-bedrock-accordion-trigger": "" });
45
+ }
46
+ export function AccordionContent({ asChild, children, ...props }) {
47
+ const { open } = useAccordionItemContext('Accordion.Content');
48
+ const onClient = useClientRender();
49
+ const Part = asChild ? Slot : 'div';
50
+ return (_jsx(Part, { ...props, "data-bedrock-accordion-content": "", children: open || !onClient ? children : null }));
51
+ }
52
+ //# sourceMappingURL=parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.js","sourceRoot":"","sources":["../../src/accordion/parts.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import type { AsChildProps } from '../types.js';
3
+ export interface AccordionRootProps extends ComponentPropsWithRef<'div'>, AsChildProps {
4
+ /**
5
+ * `single` gives the items a shared name, which is what makes the browser
6
+ * close the others. `multiple` leaves them independent.
7
+ */
8
+ type?: 'single' | 'multiple';
9
+ defaultValue?: string | string[];
10
+ orientation?: 'horizontal' | 'vertical';
11
+ /** Reports which items are open after a change. Cannot refuse. */
12
+ onValueChange?(value: string[]): void;
13
+ }
14
+ /**
15
+ * Renders a plain container. All the behaviour is in the items, and all of it
16
+ * is `<details name>` — no roving state, no open registry, no effect that
17
+ * closes siblings.
18
+ *
19
+ * `type="single"` therefore always allows closing the open item, because a
20
+ * `<summary>` toggles. Radix's `collapsible={false}` has no native equivalent
21
+ * and is not offered.
22
+ */
23
+ export declare function AccordionRoot({ asChild, children, type, defaultValue, orientation, onValueChange, ...props }: AccordionRootProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/accordion/root.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,qBAAqB,EAE3B,MAAM,OAAO,CAAA;AAEd,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB,CAAC,KAAK,CAAC,EAAE,YAAY;IACpF;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAChC,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,kEAAkE;IAClE,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CACtC;AAOD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,IAAe,EACf,YAAY,EACZ,WAAwB,EACxB,aAAa,EACb,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAwCpB"}
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useId, useMemo, useRef, } from 'react';
3
+ import { Slot } from '../slot.js';
4
+ import { AccordionContext } from './shared.js';
5
+ function toArray(value) {
6
+ if (value === undefined)
7
+ return [];
8
+ return Array.isArray(value) ? value : [value];
9
+ }
10
+ /**
11
+ * Renders a plain container. All the behaviour is in the items, and all of it
12
+ * is `<details name>` — no roving state, no open registry, no effect that
13
+ * closes siblings.
14
+ *
15
+ * `type="single"` therefore always allows closing the open item, because a
16
+ * `<summary>` toggles. Radix's `collapsible={false}` has no native equivalent
17
+ * and is not offered.
18
+ */
19
+ export function AccordionRoot({ asChild, children, type = 'single', defaultValue, orientation = 'vertical', onValueChange, ...props }) {
20
+ const name = useId();
21
+ const Part = asChild ? Slot : 'div';
22
+ const changeRef = useRef(onValueChange);
23
+ changeRef.current = onValueChange;
24
+ // Tracked in a ref rather than state: the DOM is the source of truth, and the
25
+ // root has nothing to re-render when an item opens. Read once, as the name of
26
+ // the prop says.
27
+ const initialValues = useRef(toArray(defaultValue));
28
+ const openValues = useRef(initialValues.current);
29
+ const report = useCallback((value, open) => {
30
+ const next = open
31
+ ? [...new Set([...openValues.current, value])]
32
+ : openValues.current.filter((entry) => entry !== value);
33
+ openValues.current = next;
34
+ changeRef.current?.(next);
35
+ }, []);
36
+ const context = useMemo(() => ({
37
+ name: type === 'single' ? name : undefined,
38
+ orientation,
39
+ report,
40
+ value: undefined,
41
+ defaultValue: initialValues.current,
42
+ }), [name, type, orientation, report]);
43
+ return (_jsx(AccordionContext.Provider, { value: context, children: _jsx(Part, { ...props, "data-orientation": orientation, "data-bedrock-accordion": "", children: children }) }));
44
+ }
45
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/accordion/root.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,WAAW,EACX,KAAK,EACL,OAAO,EACP,MAAM,GAGP,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAc3C,SAAS,OAAO,CAAC,KAAoC;IACnD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAClC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,IAAI,GAAG,QAAQ,EACf,YAAY,EACZ,WAAW,GAAG,UAAU,EACxB,aAAa,EACb,GAAG,KAAK,EACW;IACnB,MAAM,IAAI,GAAG,KAAK,EAAE,CAAA;IACpB,MAAM,IAAI,GAAgB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;IAEhD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,CAAA;IACvC,SAAS,CAAC,OAAO,GAAG,aAAa,CAAA;IAEjC,8EAA8E;IAC9E,8EAA8E;IAC9E,iBAAiB;IACjB,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA;IACnD,MAAM,UAAU,GAAG,MAAM,CAAW,aAAa,CAAC,OAAO,CAAC,CAAA;IAE1D,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,KAAa,EAAE,IAAa,EAAE,EAAE;QAC1D,MAAM,IAAI,GAAG,IAAI;YACf,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;QAEzD,UAAU,CAAC,OAAO,GAAG,IAAI,CAAA;QACzB,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,CAAC;QACL,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC1C,WAAW;QACX,MAAM;QACN,KAAK,EAAE,SAAS;QAChB,YAAY,EAAE,aAAa,CAAC,OAAO;KACpC,CAAC,EACF,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAClC,CAAA;IAED,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,OAAO,YACvC,KAAC,IAAI,OAAK,KAAK,sBAAoB,WAAW,4BAAyB,EAAE,YACtE,QAAQ,GACJ,GACmB,CAC7B,CAAA;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface AccordionContextValue {
2
+ /**
3
+ * Shared `name` for the items. A group of `<details>` with the same name is
4
+ * mutually exclusive in the browser — that is the whole of `type="single"`,
5
+ * with no state and no JavaScript.
6
+ */
7
+ name: string | undefined;
8
+ orientation: 'horizontal' | 'vertical';
9
+ /** Reported up from an item's `toggle`, for `onValueChange`. */
10
+ report(value: string, open: boolean): void;
11
+ /** Which values the root wants open, when it is the controlled root. */
12
+ value: readonly string[] | undefined;
13
+ defaultValue: readonly string[];
14
+ }
15
+ export declare const AccordionContext: import("react").Context<AccordionContextValue | null>;
16
+ export declare function useAccordionContext(part: string): AccordionContextValue;
17
+ export interface AccordionItemContextValue {
18
+ value: string;
19
+ open: boolean;
20
+ }
21
+ export declare const AccordionItemContext: import("react").Context<AccordionItemContextValue | null>;
22
+ export declare function useAccordionItemContext(part: string): AccordionItemContextValue;
23
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/accordion/shared.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,WAAW,EAAE,YAAY,GAAG,UAAU,CAAA;IACtC,gEAAgE;IAChE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1C,wEAAwE;IACxE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACpC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAED,eAAO,MAAM,gBAAgB,uDAAoD,CAAA;AAEjF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAIvE;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;CACd;AAED,eAAO,MAAM,oBAAoB,2DAAwD,CAAA;AAEzF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,CAI/E"}
@@ -0,0 +1,16 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const AccordionContext = createContext(null);
3
+ export function useAccordionContext(part) {
4
+ const context = useContext(AccordionContext);
5
+ if (!context)
6
+ throw new Error(`[bedrock] ${part} must be used inside Accordion.Root.`);
7
+ return context;
8
+ }
9
+ export const AccordionItemContext = createContext(null);
10
+ export function useAccordionItemContext(part) {
11
+ const context = useContext(AccordionItemContext);
12
+ if (!context)
13
+ throw new Error(`[bedrock] ${part} must be used inside Accordion.Item.`);
14
+ return context;
15
+ }
16
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/accordion/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAiBjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAA+B,IAAI,CAAC,CAAA;AAEjF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAA;IAC5C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,sCAAsC,CAAC,CAAA;IACtF,OAAO,OAAO,CAAA;AAChB,CAAC;AAOD,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAmC,IAAI,CAAC,CAAA;AAEzF,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,MAAM,OAAO,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAA;IAChD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,sCAAsC,CAAC,CAAA;IACtF,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ import { type DialogContentProps } from '../dialog/parts.js';
3
+ export type AlertDialogContentProps = DialogContentProps;
4
+ /**
5
+ * A Dialog announced as an alert dialog. The difference is `role`, plus the
6
+ * convention that the only ways out are the buttons you provide — which is the
7
+ * native default, since a modal `<dialog>` has no light dismiss to disable.
8
+ *
9
+ * Escape still closes, exactly as it does in Radix's AlertDialog.
10
+ */
11
+ export declare function AlertDialogContent(props: AlertDialogContentProps): import("react").JSX.Element;
12
+ export type AlertDialogActionProps = ComponentPropsWithRef<'button'>;
13
+ /** A close button that runs your handler. Both Action and Cancel dismiss. */
14
+ export declare function AlertDialogAction(props: AlertDialogActionProps): import("react").JSX.Element;
15
+ export declare function AlertDialogCancel(props: AlertDialogActionProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=parts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../../src/alert-dialog/parts.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAA8B,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAErF,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,CAAA;AAExD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,+BAEhE;AAED,MAAM,MAAM,sBAAsB,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAA;AAEpE,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAE9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAE9D"}