@dxos/react-ui 0.10.0 → 0.11.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 (354) hide show
  1. package/dist/lib/chunk-parse-stack.mjs +1566 -0
  2. package/dist/lib/chunk-parse-stack.mjs.map +1 -0
  3. package/dist/lib/index.mjs +5220 -0
  4. package/dist/lib/index.mjs.map +1 -0
  5. package/dist/lib/testing.mjs +144 -0
  6. package/dist/lib/testing.mjs.map +1 -0
  7. package/dist/lib/translations.mjs +38 -0
  8. package/dist/lib/translations.mjs.map +1 -0
  9. package/dist/types/src/components/Avatars/Avatar.d.ts +1 -6
  10. package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
  11. package/dist/types/src/components/Avatars/AvatarContext.d.ts +9 -0
  12. package/dist/types/src/components/Avatars/AvatarContext.d.ts.map +1 -0
  13. package/dist/types/src/components/Avatars/index.d.ts +1 -0
  14. package/dist/types/src/components/Avatars/index.d.ts.map +1 -1
  15. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts +1 -1
  16. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
  17. package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
  18. package/dist/types/src/components/Breadcrumb/Breadcrumb.theme.d.ts.map +1 -1
  19. package/dist/types/src/components/Button/Button.d.ts +1 -6
  20. package/dist/types/src/components/Button/Button.d.ts.map +1 -1
  21. package/dist/types/src/components/Button/ButtonGroupContext.d.ts +9 -0
  22. package/dist/types/src/components/Button/ButtonGroupContext.d.ts.map +1 -0
  23. package/dist/types/src/components/Button/IconButton.d.ts.map +1 -1
  24. package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Button/IconButton.theme.d.ts +1 -0
  26. package/dist/types/src/components/Button/IconButton.theme.d.ts.map +1 -1
  27. package/dist/types/src/components/Button/SystemIconButton.d.ts +1 -1
  28. package/dist/types/src/components/Button/SystemIconButton.d.ts.map +1 -1
  29. package/dist/types/src/components/Button/index.d.ts +1 -0
  30. package/dist/types/src/components/Button/index.d.ts.map +1 -1
  31. package/dist/types/src/components/Calendar/Calendar.d.ts +0 -2
  32. package/dist/types/src/components/Calendar/Calendar.d.ts.map +1 -1
  33. package/dist/types/src/components/Calendar/index.d.ts +1 -0
  34. package/dist/types/src/components/Calendar/index.d.ts.map +1 -1
  35. package/dist/types/src/components/Calendar/parse-calendar-date.d.ts +3 -0
  36. package/dist/types/src/components/Calendar/parse-calendar-date.d.ts.map +1 -0
  37. package/dist/types/src/components/Card/Card.d.ts +42 -15
  38. package/dist/types/src/components/Card/Card.d.ts.map +1 -1
  39. package/dist/types/src/components/Card/Card.theme.d.ts +0 -1
  40. package/dist/types/src/components/Card/Card.theme.d.ts.map +1 -1
  41. package/dist/types/src/components/Carousel/Carousel.d.ts +2 -4
  42. package/dist/types/src/components/Carousel/Carousel.d.ts.map +1 -1
  43. package/dist/types/src/components/Carousel/CarouselContext.d.ts +8 -0
  44. package/dist/types/src/components/Carousel/CarouselContext.d.ts.map +1 -0
  45. package/dist/types/src/components/Carousel/index.d.ts +1 -0
  46. package/dist/types/src/components/Carousel/index.d.ts.map +1 -1
  47. package/dist/types/src/components/Clipboard/ClipboardContext.d.ts +7 -0
  48. package/dist/types/src/components/Clipboard/ClipboardContext.d.ts.map +1 -0
  49. package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts +0 -6
  50. package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts.map +1 -1
  51. package/dist/types/src/components/Clipboard/index.d.ts +1 -1
  52. package/dist/types/src/components/Clipboard/index.d.ts.map +1 -1
  53. package/dist/types/src/components/Column/Column.d.ts +7 -12
  54. package/dist/types/src/components/Column/Column.d.ts.map +1 -1
  55. package/dist/types/src/components/Column/Column.stories.d.ts +2 -2
  56. package/dist/types/src/components/Column/Column.theme.d.ts +0 -1
  57. package/dist/types/src/components/Column/Column.theme.d.ts.map +1 -1
  58. package/dist/types/src/components/Column/ColumnContext.d.ts +9 -0
  59. package/dist/types/src/components/Column/ColumnContext.d.ts.map +1 -0
  60. package/dist/types/src/components/Column/index.d.ts +1 -0
  61. package/dist/types/src/components/Column/index.d.ts.map +1 -1
  62. package/dist/types/src/components/Column/withColumn.d.ts +40 -4
  63. package/dist/types/src/components/Column/withColumn.d.ts.map +1 -1
  64. package/dist/types/src/components/DatePicker/DatePicker.d.ts +0 -10
  65. package/dist/types/src/components/DatePicker/DatePicker.d.ts.map +1 -1
  66. package/dist/types/src/components/DatePicker/DatePickerContext.d.ts +13 -0
  67. package/dist/types/src/components/DatePicker/DatePickerContext.d.ts.map +1 -0
  68. package/dist/types/src/components/DatePicker/index.d.ts +1 -0
  69. package/dist/types/src/components/DatePicker/index.d.ts.map +1 -1
  70. package/dist/types/src/components/Dialog/AlertDialog.d.ts +3 -3
  71. package/dist/types/src/components/Dialog/Dialog.d.ts +3 -3
  72. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts +5 -10
  73. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts.map +1 -1
  74. package/dist/types/src/components/ErrorFallback/index.d.ts +1 -0
  75. package/dist/types/src/components/ErrorFallback/index.d.ts.map +1 -1
  76. package/dist/types/src/components/ErrorFallback/parse-stack.d.ts +8 -0
  77. package/dist/types/src/components/ErrorFallback/parse-stack.d.ts.map +1 -0
  78. package/dist/types/src/components/Focus/Focus.d.ts +2 -10
  79. package/dist/types/src/components/Focus/Focus.d.ts.map +1 -1
  80. package/dist/types/src/components/Focus/FocusContext.d.ts +10 -0
  81. package/dist/types/src/components/Focus/FocusContext.d.ts.map +1 -0
  82. package/dist/types/src/components/Focus/index.d.ts +1 -0
  83. package/dist/types/src/components/Focus/index.d.ts.map +1 -1
  84. package/dist/types/src/components/Input/Input.d.ts +9 -10
  85. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  86. package/dist/types/src/components/Input/Input.stories.d.ts +7 -2
  87. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  88. package/dist/types/src/components/Input/Input.theme.d.ts +13 -0
  89. package/dist/types/src/components/Input/Input.theme.d.ts.map +1 -1
  90. package/dist/types/src/components/Input/InputTriggerContext.d.ts +15 -0
  91. package/dist/types/src/components/Input/InputTriggerContext.d.ts.map +1 -0
  92. package/dist/types/src/components/Input/SegmentedInput.d.ts.map +1 -1
  93. package/dist/types/src/components/Input/index.d.ts +1 -0
  94. package/dist/types/src/components/Input/index.d.ts.map +1 -1
  95. package/dist/types/src/components/Main/Main.d.ts +2 -35
  96. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  97. package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/Main/Main.theme.d.ts.map +1 -1
  99. package/dist/types/src/components/Main/MainContext.d.ts +38 -0
  100. package/dist/types/src/components/Main/MainContext.d.ts.map +1 -0
  101. package/dist/types/src/components/Main/index.d.ts +1 -0
  102. package/dist/types/src/components/Main/index.d.ts.map +1 -1
  103. package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts +1 -17
  104. package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts.map +1 -1
  105. package/dist/types/src/components/MediaPlayer/index.d.ts +1 -0
  106. package/dist/types/src/components/MediaPlayer/index.d.ts.map +1 -1
  107. package/dist/types/src/components/MediaPlayer/media-kind.d.ts +18 -0
  108. package/dist/types/src/components/MediaPlayer/media-kind.d.ts.map +1 -0
  109. package/dist/types/src/components/Menu/DropdownMenu.d.ts +6 -22
  110. package/dist/types/src/components/Menu/DropdownMenu.d.ts.map +1 -1
  111. package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts +1 -11
  112. package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts.map +1 -1
  113. package/dist/types/src/components/Menu/DropdownMenuContext.d.ts +27 -0
  114. package/dist/types/src/components/Menu/DropdownMenuContext.d.ts.map +1 -0
  115. package/dist/types/src/components/Menu/index.d.ts +1 -0
  116. package/dist/types/src/components/Menu/index.d.ts.map +1 -1
  117. package/dist/types/src/components/Message/Message.d.ts +11 -6
  118. package/dist/types/src/components/Message/Message.d.ts.map +1 -1
  119. package/dist/types/src/components/Message/Message.stories.d.ts +4 -1
  120. package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
  121. package/dist/types/src/components/Message/Message.theme.d.ts.map +1 -1
  122. package/dist/types/src/components/Message/index.d.ts +1 -0
  123. package/dist/types/src/components/Message/index.d.ts.map +1 -1
  124. package/dist/types/src/components/Message/message-icons.d.ts +3 -0
  125. package/dist/types/src/components/Message/message-icons.d.ts.map +1 -0
  126. package/dist/types/src/components/Panel/Panel.d.ts +4 -4
  127. package/dist/types/src/components/Panel/Panel.stories.d.ts.map +1 -1
  128. package/dist/types/src/components/Panel/Panel.theme.d.ts +1 -1
  129. package/dist/types/src/components/Panel/Panel.theme.d.ts.map +1 -1
  130. package/dist/types/src/components/Popover/Popover.d.ts +6 -10
  131. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  132. package/dist/types/src/components/Popover/PopoverContext.d.ts +29 -0
  133. package/dist/types/src/components/Popover/PopoverContext.d.ts.map +1 -0
  134. package/dist/types/src/components/Popover/index.d.ts +1 -0
  135. package/dist/types/src/components/Popover/index.d.ts.map +1 -1
  136. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +2 -2
  137. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +2 -2
  138. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +3 -16
  139. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
  140. package/dist/types/src/components/ScrollContainer/ScrollContainerContext.d.ts +16 -0
  141. package/dist/types/src/components/ScrollContainer/ScrollContainerContext.d.ts.map +1 -0
  142. package/dist/types/src/components/ScrollContainer/index.d.ts +1 -0
  143. package/dist/types/src/components/ScrollContainer/index.d.ts.map +1 -1
  144. package/dist/types/src/components/Slider/Slider.d.ts +25 -0
  145. package/dist/types/src/components/Slider/Slider.d.ts.map +1 -0
  146. package/dist/types/src/components/Slider/Slider.stories.d.ts +29 -0
  147. package/dist/types/src/components/Slider/Slider.stories.d.ts.map +1 -0
  148. package/dist/types/src/components/Slider/Slider.test.d.ts +2 -0
  149. package/dist/types/src/components/Slider/Slider.test.d.ts.map +1 -0
  150. package/dist/types/src/components/Slider/Slider.theme.d.ts +7 -0
  151. package/dist/types/src/components/Slider/Slider.theme.d.ts.map +1 -0
  152. package/dist/types/src/components/Slider/index.d.ts +2 -0
  153. package/dist/types/src/components/Slider/index.d.ts.map +1 -0
  154. package/dist/types/src/components/Splitter/Splitter.d.ts +3 -3
  155. package/dist/types/src/components/Status/Status.d.ts.map +1 -1
  156. package/dist/types/src/components/Toolbar/Toolbar.d.ts +4 -2
  157. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  158. package/dist/types/src/components/Toolbar/Toolbar.theme.d.ts.map +1 -1
  159. package/dist/types/src/components/Tooltip/TextTooltip.d.ts.map +1 -1
  160. package/dist/types/src/components/Tooltip/Tooltip.d.ts +2 -23
  161. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  162. package/dist/types/src/components/Tooltip/TooltipContext.d.ts +35 -0
  163. package/dist/types/src/components/Tooltip/TooltipContext.d.ts.map +1 -0
  164. package/dist/types/src/components/Tooltip/index.d.ts +1 -0
  165. package/dist/types/src/components/Tooltip/index.d.ts.map +1 -1
  166. package/dist/types/src/components/index.d.ts +1 -0
  167. package/dist/types/src/components/index.d.ts.map +1 -1
  168. package/dist/types/src/exemplars/generics.stories.d.ts +1 -1
  169. package/dist/types/src/hooks/useIconHref.d.ts +8 -0
  170. package/dist/types/src/hooks/useIconHref.d.ts.map +1 -1
  171. package/dist/types/src/hooks/useThemeContext.d.ts +2 -1
  172. package/dist/types/src/hooks/useThemeContext.d.ts.map +1 -1
  173. package/dist/types/src/hooks/useThemeContext.test.d.ts +2 -0
  174. package/dist/types/src/hooks/useThemeContext.test.d.ts.map +1 -0
  175. package/dist/types/src/playground/Density.stories.d.ts +11 -0
  176. package/dist/types/src/playground/Density.stories.d.ts.map +1 -0
  177. package/dist/types/src/playground/Elevation.stories.d.ts +14 -0
  178. package/dist/types/src/playground/Elevation.stories.d.ts.map +1 -0
  179. package/dist/types/src/primitives/Container/Container.d.ts +1 -1
  180. package/dist/types/src/primitives/Container/Container.d.ts.map +1 -1
  181. package/dist/types/src/primitives/DensityProvider/DensityContext.d.ts +6 -0
  182. package/dist/types/src/primitives/DensityProvider/DensityContext.d.ts.map +1 -0
  183. package/dist/types/src/primitives/DensityProvider/DensityProvider.d.ts +10 -4
  184. package/dist/types/src/primitives/DensityProvider/DensityProvider.d.ts.map +1 -1
  185. package/dist/types/src/primitives/DensityProvider/index.d.ts +1 -0
  186. package/dist/types/src/primitives/DensityProvider/index.d.ts.map +1 -1
  187. package/dist/types/src/primitives/Flex/Flex.d.ts +1 -1
  188. package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -1
  189. package/dist/types/src/primitives/Grid/Grid.d.ts +1 -1
  190. package/dist/types/src/primitives/Grid/Grid.d.ts.map +1 -1
  191. package/dist/types/src/primitives/ThemeProvider/IconRegistry.d.ts +15 -0
  192. package/dist/types/src/primitives/ThemeProvider/IconRegistry.d.ts.map +1 -0
  193. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.d.ts +1 -2
  194. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.d.ts.map +1 -1
  195. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.stories.d.ts +1 -1
  196. package/dist/types/src/primitives/ThemeProvider/TranslationsContext.d.ts +112 -0
  197. package/dist/types/src/primitives/ThemeProvider/TranslationsContext.d.ts.map +1 -0
  198. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts +0 -109
  199. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts.map +1 -1
  200. package/dist/types/src/primitives/ThemeProvider/icon-registry.d.ts +34 -0
  201. package/dist/types/src/primitives/ThemeProvider/icon-registry.d.ts.map +1 -0
  202. package/dist/types/src/primitives/ThemeProvider/index.d.ts +3 -1
  203. package/dist/types/src/primitives/ThemeProvider/index.d.ts.map +1 -1
  204. package/dist/types/src/testing/decorators/withTheme.d.ts +1 -1
  205. package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
  206. package/dist/types/src/theme/defaultTheme.d.ts.map +1 -1
  207. package/dist/types/src/theme/index.d.ts +1 -0
  208. package/dist/types/src/theme/index.d.ts.map +1 -1
  209. package/dist/types/src/util/slots.d.ts +6 -3
  210. package/dist/types/src/util/slots.d.ts.map +1 -1
  211. package/dist/types/src/util/slots.stories.d.ts +14 -0
  212. package/dist/types/src/util/slots.stories.d.ts.map +1 -0
  213. package/dist/types/tsconfig.tsbuildinfo +1 -1
  214. package/package.json +25 -27
  215. package/src/components/Avatars/Avatar.tsx +1 -11
  216. package/src/components/Avatars/AvatarContext.ts +16 -0
  217. package/src/components/Avatars/index.ts +2 -0
  218. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +27 -25
  219. package/src/components/Breadcrumb/Breadcrumb.theme.ts +7 -5
  220. package/src/components/Breadcrumb/Breadcrumb.tsx +2 -2
  221. package/src/components/Button/Button.tsx +2 -11
  222. package/src/components/Button/ButtonGroupContext.ts +17 -0
  223. package/src/components/Button/IconButton.stories.tsx +2 -3
  224. package/src/components/Button/IconButton.theme.ts +15 -2
  225. package/src/components/Button/IconButton.tsx +6 -1
  226. package/src/components/Button/SystemIconButton.tsx +23 -16
  227. package/src/components/Button/index.ts +2 -0
  228. package/src/components/Calendar/Calendar.theme.ts +5 -5
  229. package/src/components/Calendar/Calendar.tsx +1 -3
  230. package/src/components/Calendar/index.ts +2 -0
  231. package/src/components/Calendar/parse-calendar-date.ts +9 -0
  232. package/src/components/Card/Card.theme.ts +48 -38
  233. package/src/components/Card/Card.tsx +77 -16
  234. package/src/components/Carousel/Carousel.tsx +4 -11
  235. package/src/components/Carousel/CarouselContext.ts +17 -0
  236. package/src/components/Carousel/index.ts +2 -0
  237. package/src/components/Clipboard/ClipboardContext.ts +20 -0
  238. package/src/components/Clipboard/ClipboardProvider.tsx +2 -12
  239. package/src/components/Clipboard/CopyButton.tsx +1 -1
  240. package/src/components/Clipboard/index.ts +1 -1
  241. package/src/components/Column/Column.stories.tsx +2 -2
  242. package/src/components/Column/Column.theme.ts +2 -11
  243. package/src/components/Column/Column.tsx +35 -57
  244. package/src/components/Column/ColumnContext.ts +22 -0
  245. package/src/components/Column/index.ts +2 -0
  246. package/src/components/Column/withColumn.ts +41 -4
  247. package/src/components/DatePicker/DatePicker.theme.ts +2 -2
  248. package/src/components/DatePicker/DatePicker.tsx +1 -14
  249. package/src/components/DatePicker/DatePickerContext.ts +21 -0
  250. package/src/components/DatePicker/index.ts +2 -0
  251. package/src/components/ErrorFallback/ErrorStack.tsx +8 -28
  252. package/src/components/ErrorFallback/index.ts +1 -0
  253. package/src/components/ErrorFallback/parse-stack.ts +33 -0
  254. package/src/components/Focus/Focus.stories.tsx +6 -6
  255. package/src/components/Focus/Focus.tsx +2 -30
  256. package/src/components/Focus/FocusContext.ts +22 -0
  257. package/src/components/Focus/index.ts +2 -0
  258. package/src/components/Image/Image.tsx +1 -1
  259. package/src/components/Input/Input.stories.tsx +63 -48
  260. package/src/components/Input/Input.theme.ts +33 -27
  261. package/src/components/Input/Input.tsx +46 -43
  262. package/src/components/Input/InputTriggerContext.ts +45 -0
  263. package/src/components/Input/SegmentedInput.tsx +2 -1
  264. package/src/components/Input/index.ts +2 -0
  265. package/src/components/Main/Main.stories.tsx +2 -1
  266. package/src/components/Main/Main.theme.ts +10 -1
  267. package/src/components/Main/Main.tsx +1 -115
  268. package/src/components/Main/MainContext.ts +126 -0
  269. package/src/components/Main/index.ts +2 -0
  270. package/src/components/MediaPlayer/MediaPlayer.tsx +16 -47
  271. package/src/components/MediaPlayer/index.ts +2 -0
  272. package/src/components/MediaPlayer/media-kind.ts +41 -0
  273. package/src/components/Menu/DropdownMenu.tsx +27 -44
  274. package/src/components/Menu/DropdownMenuContext.ts +35 -0
  275. package/src/components/Menu/Menu.theme.ts +3 -3
  276. package/src/components/Menu/index.ts +2 -0
  277. package/src/components/Message/Message.stories.tsx +32 -15
  278. package/src/components/Message/Message.theme.ts +8 -2
  279. package/src/components/Message/Message.tsx +47 -23
  280. package/src/components/Message/index.ts +2 -0
  281. package/src/components/Message/message-icons.ts +16 -0
  282. package/src/components/Panel/Panel.stories.tsx +15 -2
  283. package/src/components/Panel/Panel.theme.ts +15 -6
  284. package/src/components/Popover/Popover.theme.ts +1 -1
  285. package/src/components/Popover/Popover.tsx +22 -37
  286. package/src/components/Popover/PopoverContext.ts +32 -0
  287. package/src/components/Popover/index.ts +2 -0
  288. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +1 -1
  289. package/src/components/ScrollContainer/ScrollContainer.tsx +1 -18
  290. package/src/components/ScrollContainer/ScrollContainerContext.ts +25 -0
  291. package/src/components/ScrollContainer/index.ts +2 -0
  292. package/src/components/Select/Select.theme.ts +2 -2
  293. package/src/components/Slider/Slider.stories.tsx +107 -0
  294. package/src/components/Slider/Slider.test.tsx +105 -0
  295. package/src/components/Slider/Slider.theme.ts +38 -0
  296. package/src/components/Slider/Slider.tsx +64 -0
  297. package/src/components/Slider/index.ts +5 -0
  298. package/src/components/Status/Status.theme.ts +1 -1
  299. package/src/components/Status/Status.tsx +0 -1
  300. package/src/components/Tag/Tag.stories.tsx +1 -1
  301. package/src/components/Toolbar/Toolbar.theme.ts +14 -5
  302. package/src/components/Toolbar/Toolbar.tsx +5 -1
  303. package/src/components/Tooltip/TextTooltip.tsx +2 -1
  304. package/src/components/Tooltip/Tooltip.tsx +12 -35
  305. package/src/components/Tooltip/TooltipContext.ts +41 -0
  306. package/src/components/Tooltip/index.ts +2 -0
  307. package/src/components/index.ts +1 -0
  308. package/src/hooks/useIconHref.ts +26 -5
  309. package/src/hooks/useThemeContext.test.tsx +33 -0
  310. package/src/hooks/useThemeContext.ts +28 -3
  311. package/src/playground/Density.stories.tsx +41 -0
  312. package/src/playground/Elevation.stories.tsx +348 -0
  313. package/src/primitives/DensityProvider/DensityContext.ts +15 -0
  314. package/src/primitives/DensityProvider/DensityProvider.tsx +12 -6
  315. package/src/primitives/DensityProvider/index.ts +1 -0
  316. package/src/primitives/Flex/Flex.stories.tsx +1 -1
  317. package/src/primitives/Grid/Grid.stories.tsx +1 -1
  318. package/src/primitives/ThemeProvider/IconRegistry.tsx +42 -0
  319. package/src/primitives/ThemeProvider/ThemeProvider.tsx +16 -15
  320. package/src/primitives/ThemeProvider/TranslationsContext.ts +24 -0
  321. package/src/primitives/ThemeProvider/TranslationsProvider.tsx +4 -16
  322. package/src/primitives/ThemeProvider/icon-registry.ts +272 -0
  323. package/src/primitives/ThemeProvider/index.ts +9 -1
  324. package/src/testing/decorators/withLayout.tsx +4 -4
  325. package/src/testing/decorators/withLayoutVariants.tsx +3 -3
  326. package/src/testing/decorators/withTheme.tsx +1 -2
  327. package/src/theme/defaultTheme.ts +2 -0
  328. package/src/theme/index.ts +1 -0
  329. package/src/util/slots.stories.tsx +80 -0
  330. package/src/util/slots.ts +14 -5
  331. package/dist/lib/browser/chunk-CMHHVHX3.mjs +0 -46
  332. package/dist/lib/browser/chunk-CMHHVHX3.mjs.map +0 -7
  333. package/dist/lib/browser/chunk-GTHV4WYK.mjs +0 -1499
  334. package/dist/lib/browser/chunk-GTHV4WYK.mjs.map +0 -7
  335. package/dist/lib/browser/index.mjs +0 -5632
  336. package/dist/lib/browser/index.mjs.map +0 -7
  337. package/dist/lib/browser/meta.json +0 -1
  338. package/dist/lib/browser/testing/index.mjs +0 -133
  339. package/dist/lib/browser/testing/index.mjs.map +0 -7
  340. package/dist/lib/browser/translations.mjs +0 -9
  341. package/dist/lib/browser/translations.mjs.map +0 -7
  342. package/dist/lib/node-esm/chunk-3VSKWNCQ.mjs +0 -1501
  343. package/dist/lib/node-esm/chunk-3VSKWNCQ.mjs.map +0 -7
  344. package/dist/lib/node-esm/chunk-P4KFQGJI.mjs +0 -48
  345. package/dist/lib/node-esm/chunk-P4KFQGJI.mjs.map +0 -7
  346. package/dist/lib/node-esm/index.mjs +0 -5633
  347. package/dist/lib/node-esm/index.mjs.map +0 -7
  348. package/dist/lib/node-esm/meta.json +0 -1
  349. package/dist/lib/node-esm/testing/index.mjs +0 -134
  350. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  351. package/dist/lib/node-esm/translations.mjs +0 -10
  352. package/dist/lib/node-esm/translations.mjs.map +0 -7
  353. package/src/components/Column/AUDIT.md +0 -148
  354. package/src/components/Focus/AUDIT.md +0 -43
@@ -10,6 +10,7 @@ import { type SlottableProps } from '@dxos/ui-types';
10
10
 
11
11
  import { useThemeContext } from '../../hooks';
12
12
  import { composableProps, slottable } from '../../util';
13
+ import { ColumnContext } from './ColumnContext';
13
14
 
14
15
  //
15
16
  // Root
@@ -25,7 +26,7 @@ const gutterSizes: Record<GutterSize, string> = {
25
26
  lg: 'var(--dx-gutter-lg)',
26
27
  };
27
28
 
28
- type ColumnRootProps = { gutter?: GutterSize };
29
+ type ColumnRootProps = { gutter?: GutterSize; subgrid?: boolean };
29
30
 
30
31
  /**
31
32
  * Creates a 3-column CSS grid with left/right gutter columns and a center content column.
@@ -34,37 +35,45 @@ type ColumnRootProps = { gutter?: GutterSize };
34
35
  * `--dx-col` defaults to `2 / span 1` (center column),
35
36
  * enabling `withColumn` utilities to cascade the correct grid placement to slotted children.
36
37
  *
38
+ * With `subgrid`, the root adopts the parent grid's tracks instead of defining its own — it spans
39
+ * all parent columns (`grid-column: 1 / -1`) and its column template becomes `subgrid`, so a Column
40
+ * (or Card) nested inside another 3-track grid aligns its gutters/center to the outer columns. The
41
+ * parent must expose (at least) three tracks; `--dx-col`/gutter placement still targets track 2/1/3.
42
+ *
37
43
  * Direct children participate in the grid in one of several ways:
38
44
  * - **Column.Center** — places element in the center column (col 2). Preferred for plain content.
39
- * - **Column.Bleed** — spans all 3 columns gutter-to-gutter. Preferred for `ScrollArea` and
40
- * other content that should ignore the gutters.
41
45
  * - **Column.Row** — 3-col subgrid row (icons in gutters, content in center).
42
46
  *
43
- * Use `withColumn.center()` / `withColumn.bleed()` helpers to apply placement on slotted elements.
47
+ * Use the `withColumn.center()` helper to apply placement on slotted elements.
44
48
  */
45
49
  const ColumnRoot = slottable<HTMLDivElement, ColumnRootProps>(
46
- ({ children, asChild, role, gutter = 'lg', ...props }, forwardedRef) => {
50
+ ({ children, asChild, role, gutter = 'lg', subgrid, ...props }, forwardedRef) => {
47
51
  const { className, ...rest } = composableProps(props);
48
52
  const Comp = asChild ? Slot : Primitive.div;
49
53
  const { tx } = useThemeContext();
50
54
  const gutterSize = gutterSizes[gutter];
55
+ // The provider wraps `Comp` rather than the children: under `asChild`, `Comp` is a Slot that
56
+ // merges its props into its single child, and a Provider in that position would swallow them.
51
57
  return (
52
- <Comp
53
- {...rest}
54
- role={role ?? 'none'}
55
- style={
56
- {
57
- ...rest.style,
58
- '--gutter': gutterSize,
59
- '--dx-col': '2 / span 1',
60
- 'gridTemplateColumns': [gutterSize, 'minmax(0,1fr)', gutterSize].join(' '),
61
- } as CSSProperties
62
- }
63
- className={tx('column.root', { gutter }, className)}
64
- ref={forwardedRef}
65
- >
66
- {children}
67
- </Comp>
58
+ <ColumnContext.Provider value={true}>
59
+ <Comp
60
+ {...rest}
61
+ role={role ?? 'none'}
62
+ style={
63
+ {
64
+ ...rest.style,
65
+ '--gutter': gutterSize,
66
+ '--dx-col': '2 / span 1',
67
+ 'gridTemplateColumns': subgrid ? 'subgrid' : [gutterSize, 'minmax(0,1fr)', gutterSize].join(' '),
68
+ ...(subgrid && { gridColumn: '1 / -1' }),
69
+ } as CSSProperties
70
+ }
71
+ className={tx('column.root', { gutter }, className)}
72
+ ref={forwardedRef}
73
+ >
74
+ {children}
75
+ </Comp>
76
+ </ColumnContext.Provider>
68
77
  );
69
78
  },
70
79
  );
@@ -97,32 +106,6 @@ const ColumnRow = slottable<HTMLDivElement, ColumnRowProps>(({ children, asChild
97
106
 
98
107
  ColumnRow.displayName = COLUMN_ROW_NAME;
99
108
 
100
- //
101
- // Bleed
102
- //
103
-
104
- const COLUMN_BLEED_NAME = 'Column.Bleed';
105
-
106
- type ColumnBleedProps = SlottableProps;
107
-
108
- /**
109
- * Spans all 3 columns of the parent Column.Root (gutter-to-gutter).
110
- * Establishes a CSS subgrid so that grandchildren can participate in the parent column tracks.
111
- * Use for `ScrollArea`, full-width dividers, tables, or any content that should ignore the gutters.
112
- */
113
- const ColumnBleed = slottable<HTMLDivElement>(({ children, asChild, ...props }, forwardedRef) => {
114
- const { tx } = useThemeContext();
115
- const { className, ...rest } = composableProps(props);
116
- const Comp = asChild ? Slot : Primitive.div;
117
- return (
118
- <Comp {...rest} className={tx('column.bleed', {}, className)} ref={forwardedRef}>
119
- {children}
120
- </Comp>
121
- );
122
- });
123
-
124
- ColumnBleed.displayName = COLUMN_BLEED_NAME;
125
-
126
109
  //
127
110
  // Center
128
111
  //
@@ -160,8 +143,9 @@ type ColumnBlockProps = SlottableProps<{ end?: boolean; compact?: boolean; squar
160
143
  /**
161
144
  * A gutter slot inside a Column.Row. Sized to `--dx-rail-item` and centers its child so a passive
162
145
  * `<Icon>` and an interactive `IconButton` align to the pixel. `end` opts into the trailing gutter
163
- * (column 3); default is the leading gutter (column 1). Placement is via `data-slot`, so it is
164
- * robust to conditional rendering and source order.
146
+ * (column 3); default is the leading gutter (column 1). Placement is class-based (`col-start-*`
147
+ * plus the `dx-gutter` marker — see `Column.theme.ts`), so it is robust to conditional rendering
148
+ * and source order.
165
149
  */
166
150
  const ColumnBlock = slottable<HTMLDivElement, ColumnBlockProps>(
167
151
  ({ children, asChild, end, compact, square, ...props }, forwardedRef) => {
@@ -169,12 +153,7 @@ const ColumnBlock = slottable<HTMLDivElement, ColumnBlockProps>(
169
153
  const { className, ...rest } = composableProps(props);
170
154
  const Comp = asChild ? Slot : Primitive.div;
171
155
  return (
172
- <Comp
173
- {...rest}
174
- data-slot={end ? 'end' : 'start'}
175
- className={tx('column.block', { end, compact, square }, className)}
176
- ref={forwardedRef}
177
- >
156
+ <Comp {...rest} className={tx('column.block', { end, compact, square }, className)} ref={forwardedRef}>
178
157
  {children}
179
158
  </Comp>
180
159
  );
@@ -191,8 +170,7 @@ export const Column = {
191
170
  Root: ColumnRoot,
192
171
  Row: ColumnRow,
193
172
  Block: ColumnBlock,
194
- Bleed: ColumnBleed,
195
173
  Center: ColumnCenter,
196
174
  };
197
175
 
198
- export type { ColumnBleedProps, ColumnBlockProps, ColumnCenterProps, ColumnRootProps, ColumnRowProps };
176
+ export type { ColumnBlockProps, ColumnCenterProps, ColumnRootProps, ColumnRowProps };
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { createContext, useContext } from 'react';
6
+
7
+ // Kept out of `Column.tsx`: react-refresh only fast-refreshes a module whose exports are all
8
+ // components, so a context and its hook exported beside them force a full page reload on every edit.
9
+
10
+ //
11
+ // Context
12
+ //
13
+
14
+ export const ColumnContext = createContext(false);
15
+
16
+ /**
17
+ * Whether the caller is already inside a `Column.Root`. A component that would otherwise establish
18
+ * its own gutter grid (a form viewport, say) should use this to place itself in the host's content
19
+ * track instead — nesting a second grid is what makes a form's fields inset differently from the
20
+ * card title above them.
21
+ */
22
+ export const useInColumn = () => useContext(ColumnContext);
@@ -2,5 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export { useInColumn } from './ColumnContext';
6
+
5
7
  export * from './Column';
6
8
  export * from './withColumn';
@@ -12,16 +12,53 @@
12
12
  * - ScrollArea.Viewport resets `--dx-col: auto` after consuming `--gutter`.
13
13
  * - Components apply `grid-column: var(--dx-col, auto)` to auto-center in Column
14
14
  * or do nothing outside Column / inside ScrollArea.
15
+ *
16
+ * ## Which one to use
17
+ *
18
+ * Three placement mechanisms coexist because they solve different problems. An earlier plan was to
19
+ * collapse them onto `--dx-col` alone; that was tried and abandoned — the other two are each the
20
+ * only way to express their case (see AUDIT.md §3).
21
+ *
22
+ * | Mechanism | Places | Reach for it when |
23
+ * | ----------------- | ----------------------------- | -------------------------------------------------- |
24
+ * | `center()` | the element itself | one element belongs in the content track |
25
+ * | `placeContent()` | the element's children | a subgrid row mixes gutter slots with content |
26
+ * | `propagate()` | opens the tracks to children | a child must reach the gutters |
27
+ *
28
+ * - **`center()`** is inheritance-based, so it survives a `display: contents` wrapper — the case
29
+ * where a child selector or subgrid silently fails. Prefer it whenever it is sufficient.
30
+ * - **`placeContent()`** needs the element to already be a subgrid row (`col-span-3 grid
31
+ * grid-cols-subgrid`). It reaches grandchildren too, so a wrapper element (a link, a button)
32
+ * doesn't strand its content in column 1.
33
+ * - **`propagate()`** is the only one that lets a *descendant* address the gutters: it spans the
34
+ * element across all tracks and re-exposes them via subgrid, with `.dx-container` exempted so a
35
+ * ScrollArea can span full width and keep its scrollbar in the gutter. `Dialog.Body` depends on
36
+ * this — replacing it with `center()` confines the body's ScrollArea to the content track and
37
+ * pulls the scrollbar 32px inboard.
15
38
  */
16
39
  export const withColumn = {
17
- /** Centers element in the Column grid via --dx-col. No-op outside Column or inside ScrollArea. */
40
+ /**
41
+ * Centers element in the Column grid via --dx-col. No-op outside Column or inside ScrollArea.
42
+ */
18
43
  center: () => '[grid-column:var(--dx-col,auto)]',
19
44
 
20
- /** Propagates the Column grid to children via subgrid. No-op outside Column.
21
- * Direct children default to center column unless they are a dx-container (ScrollArea). */
45
+ /**
46
+ * Places a subgrid row's content children in the center track, leaving `.dx-gutter` slots to
47
+ * their own explicit `col-start`. Covers grandchildren too, so a wrapper element (a link, a
48
+ * button) doesn't strand its content in column 1 — and excludes gutters at that depth as well,
49
+ * since a `display: contents` wrapper promotes its children into this grid.
50
+ */
51
+ placeContent: () => '[&>*:not(.dx-gutter)]:col-start-2 [&>*:not(.dx-gutter)>*:not(.dx-gutter)]:col-start-2',
52
+
53
+ /**
54
+ * Propagates the Column grid to children via subgrid. No-op outside Column.
55
+ * Direct children default to center column unless they are a dx-container (ScrollArea).
56
+ */
22
57
  propagate: () =>
23
58
  '[.dx-column-root_&]:col-span-full [.dx-column-root_&]:grid [.dx-column-root_&]:grid-cols-subgrid [.dx-column-root_&]:[&>*:not(.dx-container)]:[grid-column:var(--dx-col,auto)]',
24
59
 
25
- /** Resets --dx-col after consuming --gutter. Applied by ScrollArea.Viewport. */
60
+ /**
61
+ * Resets --dx-col after consuming --gutter. Applied by ScrollArea.Viewport.
62
+ */
26
63
  consumed: () => '[--dx-col:auto]',
27
64
  };
@@ -12,13 +12,13 @@ export type DatePickerStyleProps = Partial<{
12
12
  const trigger: ComponentFunction<DatePickerStyleProps> = ({ hasValue }, ...etc) =>
13
13
  mx(
14
14
  'inline-flex w-fit items-center gap-2 px-2 rounded-sm border border-separator',
15
- 'bg-input-surface text-base-fg',
15
+ 'dx-input-surface text-base-fg',
16
16
  'hover:bg-hover-surface focus-visible:outline-2 focus-visible:outline-primary-500',
17
17
  !hasValue && 'text-description',
18
18
  ...etc,
19
19
  );
20
20
 
21
- const content: ComponentFunction<DatePickerStyleProps> = (_p, ...etc) => mx('flex flex-col gap-2 p-2', ...etc);
21
+ const content: ComponentFunction<DatePickerStyleProps> = (_p, ...etc) => mx('flex flex-col gap-2', ...etc);
22
22
 
23
23
  const timeField: ComponentFunction<DatePickerStyleProps> = (_p, ...etc) =>
24
24
  mx(
@@ -2,7 +2,6 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { createContext } from '@radix-ui/react-context';
6
5
  import { format as formatDate } from 'date-fns';
7
6
  import React, {
8
7
  type ComponentPropsWithoutRef,
@@ -20,6 +19,7 @@ import { type ThemedClassName } from '../../util';
20
19
  import { Calendar, type DateRange } from '../Calendar';
21
20
  import { Icon } from '../Icon';
22
21
  import { Popover } from '../Popover';
22
+ import { DatePickerProvider, useDatePickerContext } from './DatePickerContext';
23
23
 
24
24
  //
25
25
  // Public API.
@@ -37,17 +37,6 @@ type ValueByMode = {
37
37
  range: DateRange | undefined;
38
38
  };
39
39
 
40
- type DatePickerContextValue = {
41
- mode: DatePickerMode;
42
- value: ValueByMode[DatePickerMode];
43
- setValue: (next: ValueByMode[DatePickerMode]) => void;
44
- withTime: boolean;
45
- open: boolean;
46
- onOpenChange: (open: boolean) => void;
47
- };
48
-
49
- const [DatePickerProvider, useDatePickerContext] = createContext<DatePickerContextValue>('DatePicker');
50
-
51
40
  //
52
41
  // Root.
53
42
  //
@@ -274,6 +263,4 @@ export const DatePicker = {
274
263
  Calendar: DatePickerCalendar,
275
264
  };
276
265
 
277
- export { useDatePickerContext };
278
-
279
266
  export type { ValueByMode };
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { createContext } from '@radix-ui/react-context';
6
+
7
+ import { type DatePickerMode, type ValueByMode } from './DatePicker';
8
+
9
+ // Kept out of `DatePicker.tsx`: react-refresh only fast-refreshes a module whose exports are all
10
+ // components, so a context and its hook exported beside them force a full page reload on every edit.
11
+
12
+ export type DatePickerContextValue = {
13
+ mode: DatePickerMode;
14
+ value: ValueByMode[DatePickerMode];
15
+ setValue: (next: ValueByMode[DatePickerMode]) => void;
16
+ withTime: boolean;
17
+ open: boolean;
18
+ onOpenChange: (open: boolean) => void;
19
+ };
20
+
21
+ export const [DatePickerProvider, useDatePickerContext] = createContext<DatePickerContextValue>('DatePicker');
@@ -2,4 +2,6 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
+ export { useDatePickerContext } from './DatePickerContext';
6
+
5
7
  export * from './DatePicker';
@@ -7,50 +7,30 @@ import React from 'react';
7
7
 
8
8
  import { mx } from '@dxos/ui-theme';
9
9
 
10
- type LocalFrame = { href: string; fileName: string };
10
+ import { ThemedClassName } from '../../util';
11
+ import { type ParsedStackFrame } from './parse-stack';
11
12
 
12
- export type ParsedStackFrame = ReturnType<typeof ErrorStackParser.parse>[number];
13
+ /** A stack frame resolved to a local (workspace) source location. */
14
+ type LocalFrame = { href: string; fileName: string };
13
15
 
14
- export type ErrorStackProps = {
16
+ export type ErrorStackProps = ThemedClassName<{
15
17
  /** When set, these frames are shown instead of parsing `error`. */
16
18
  frames?: ParsedStackFrame[];
17
19
  /** Used when `frames` is omitted. */
18
20
  error?: Error;
19
- };
20
-
21
- /**
22
- * Parses `captureOwnerStack()` output (React dev) into frames for {@link ErrorStack}.
23
- * Prefixes a synthetic Error line when needed so `error-stack-parser` can read V8-style stacks.
24
- */
25
- export const parseCaptureOwnerStack = (stack: string | null): ParsedStackFrame[] | null => {
26
- if (stack == null || stack.length === 0) {
27
- return null;
28
- }
29
- const err = new Error();
30
- err.stack = stack;
31
- try {
32
- return ErrorStackParser.parse(err);
33
- } catch {
34
- err.stack = `Error\n${stack}`;
35
- try {
36
- return ErrorStackParser.parse(err);
37
- } catch {
38
- return null;
39
- }
40
- }
41
- };
21
+ }>;
42
22
 
43
23
  /**
44
24
  * Renders a parsed error stack trace with tree connector symbols and clickable vscode:// links for local frames.
45
25
  */
46
- export const ErrorStack = ({ error, frames: framesProp }: ErrorStackProps) => {
26
+ export const ErrorStack = ({ classNames, error, frames: framesProp }: ErrorStackProps) => {
47
27
  const frames = framesProp ?? (error ? ErrorStackParser.parse(error) : []);
48
28
  if (frames.length === 0) {
49
29
  return null;
50
30
  }
51
31
 
52
32
  return (
53
- <div className='font-mono text-sm'>
33
+ <div className={mx('font-mono text-sm', classNames)}>
54
34
  {frames.map((frame, i) => {
55
35
  const isLast = i === frames.length - 1;
56
36
  const local = frame.fileName
@@ -5,5 +5,6 @@
5
5
  export { ErrorBoundary, type ErrorBoundaryProps, type FallbackProps } from '@dxos/react-error-boundary';
6
6
 
7
7
  export * from './ErrorFallback';
8
+ export { type ParsedStackFrame, parseCaptureOwnerStack } from './parse-stack';
8
9
  export * from './ErrorStack';
9
10
  export * from './ThrowError';
@@ -0,0 +1,33 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import ErrorStackParser from 'error-stack-parser';
6
+
7
+ // Kept out of `ErrorStack.tsx`: react-refresh only fast-refreshes a module whose exports are all
8
+ // components, so a context and its hook exported beside them force a full page reload on every edit.
9
+
10
+ export type ParsedStackFrame = ReturnType<typeof ErrorStackParser.parse>[number];
11
+
12
+ /**
13
+ * Parses `captureOwnerStack()` output (React dev) into frames for {@link ErrorStack}.
14
+ * Prefixes a synthetic Error line when needed so `error-stack-parser` can read V8-style stacks.
15
+ */
16
+ export const parseCaptureOwnerStack = (stack: string | null): ParsedStackFrame[] | null => {
17
+ if (stack == null || stack.length === 0) {
18
+ return null;
19
+ }
20
+
21
+ const err = new Error();
22
+ err.stack = stack;
23
+ try {
24
+ return ErrorStackParser.parse(err);
25
+ } catch {
26
+ err.stack = `Error\n${stack}`;
27
+ try {
28
+ return ErrorStackParser.parse(err);
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ };
@@ -57,7 +57,7 @@ const DefaultStory = () => {
57
57
  key={item.id}
58
58
  current={current === item.id}
59
59
  onCurrentChange={() => setCurrent(item.id)}
60
- className={itemClassName}
60
+ classNames={itemClassName}
61
61
  >
62
62
  <span className='size-2 bg-primary-500 opacity-0 aria-[current]:opacity-100' />
63
63
  <span>{item.label}</span>
@@ -85,7 +85,7 @@ const HorizontalStory = () => {
85
85
  key={item.id}
86
86
  current={current === item.id}
87
87
  onCurrentChange={() => setCurrent(item.id)}
88
- className='flex flex-col items-center justify-center w-20 h-20 border border-separator aria-current:border-primary-500 aria-current:bg-primary-50 dark:aria-current:bg-primary-900/20 cursor-pointer'
88
+ classNames='flex flex-col items-center justify-center w-20 h-20 border border-separator aria-current:border-primary-500 aria-current:bg-primary-50 dark:aria-current:bg-primary-900/20 cursor-pointer'
89
89
  >
90
90
  <span className='text-xs mt-1'>{item.label}</span>
91
91
  </Focus.Item>
@@ -103,14 +103,14 @@ const HorizontalStory = () => {
103
103
  const GridCell = ({ border, items }: { border?: boolean; items: Item[] }) => {
104
104
  const [current, setCurrent] = useState<string | undefined>();
105
105
  return (
106
- <div className='overflow-hidden bg-base-surface'>
106
+ <div className='overflow-hidden dx-base-surface'>
107
107
  <Focus.Group classNames='h-full' border={border}>
108
108
  {items.map((item) => (
109
109
  <Focus.Item
110
110
  key={item.id}
111
111
  current={current === item.id}
112
112
  onCurrentChange={() => setCurrent(item.id)}
113
- className={itemClassName}
113
+ classNames={itemClassName}
114
114
  >
115
115
  <span>{item.label}</span>
116
116
  </Focus.Item>
@@ -166,7 +166,7 @@ const GridStory = () => {
166
166
  <div className='h-48 overflow-hidden'>
167
167
  <Focus.Group classNames='h-full'>
168
168
  {ITEMS.slice(0, 3).map((item) => (
169
- <Focus.Item key={item.id} border className={itemClassName}>
169
+ <Focus.Item key={item.id} border classNames={itemClassName}>
170
170
  <span>{item.label}</span>
171
171
  </Focus.Item>
172
172
  ))}
@@ -178,7 +178,7 @@ const GridStory = () => {
178
178
  <div className='h-48 overflow-hidden'>
179
179
  <Focus.Group classNames='h-full' data-focus-state='error'>
180
180
  {ITEMS.slice(0, 3).map((item) => (
181
- <Focus.Item key={item.id} className={itemClassName}>
181
+ <Focus.Item key={item.id} classNames={itemClassName}>
182
182
  <span>{item.label}</span>
183
183
  </Focus.Item>
184
184
  ))}
@@ -10,39 +10,13 @@ import {
10
10
  import { useComposedRefs } from '@radix-ui/react-compose-refs';
11
11
  import { Primitive } from '@radix-ui/react-primitive';
12
12
  import { Slot } from '@radix-ui/react-slot';
13
- import React, {
14
- type FocusEvent,
15
- type KeyboardEvent,
16
- type MouseEvent,
17
- createContext,
18
- useCallback,
19
- useContext,
20
- useRef,
21
- useState,
22
- } from 'react';
13
+ import React, { type FocusEvent, type KeyboardEvent, type MouseEvent, useCallback, useRef, useState } from 'react';
23
14
 
24
15
  import { type Axis } from '@dxos/ui-types';
25
16
 
26
17
  import { useThemeContext } from '../../hooks';
27
18
  import { composableProps, slottable } from '../../util';
28
-
29
- //
30
- // Context
31
- //
32
-
33
- type FocusState = 'active' | 'error';
34
-
35
- const FOCUS_STATE_ATTR = 'focus-state';
36
-
37
- type ContextValue = {
38
- setFocus?: (state: FocusState | undefined) => void;
39
- /** True when any item within the group has DOM focus. */
40
- groupHasFocus?: boolean;
41
- };
42
-
43
- const FocusContext = createContext<ContextValue>({});
44
-
45
- const useFocus = () => useContext(FocusContext);
19
+ import { FOCUS_STATE_ATTR, FocusContext, type FocusState } from './FocusContext';
46
20
 
47
21
  //
48
22
  // Group
@@ -197,5 +171,3 @@ export const Focus = {
197
171
  };
198
172
 
199
173
  export type { GroupProps as FocusGroupProps, ItemProps as FocusItemProps };
200
-
201
- export { useFocus };
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { createContext, useContext } from 'react';
6
+
7
+ // Kept out of `Focus.tsx`: react-refresh only fast-refreshes a module whose exports are all
8
+ // components, so a context and its hook exported beside them force a full page reload on every edit.
9
+
10
+ export type FocusState = 'active' | 'error';
11
+
12
+ export const FOCUS_STATE_ATTR = 'focus-state';
13
+
14
+ export type ContextValue = {
15
+ setFocus?: (state: FocusState | undefined) => void;
16
+ /** True when any item within the group has DOM focus. */
17
+ groupHasFocus?: boolean;
18
+ };
19
+
20
+ export const FocusContext = createContext<ContextValue>({});
21
+
22
+ export const useFocus = () => useContext(FocusContext);
@@ -2,4 +2,6 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
+ export { useFocus } from './FocusContext';
6
+
5
7
  export * from './Focus';
@@ -82,7 +82,7 @@ export const Image = ({
82
82
  // `dx-ring-pseudo` `::after`) painted on ancestors — most visibly,
83
83
  // the focus ring on a Card containing a Card.Poster.
84
84
  className={mx(
85
- `relative flex w-full justify-center overflow-hidden transition-all duration-700 isolate`,
85
+ `relative shrink-0 flex w-full justify-center overflow-hidden transition-all duration-700 isolate`,
86
86
  classNames,
87
87
  )}
88
88
  style={{