@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -10,7 +10,10 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
13
- import {render, screen, fireEvent, waitFor} from '@testing-library/react';
13
+ import {render, screen, fireEvent, waitFor, act} from '@testing-library/react';
14
+ import {renderToString} from 'react-dom/server';
15
+ import {hydrateRoot} from 'react-dom/client';
16
+ import {StrictMode} from 'react';
14
17
  import {HoverCard} from './HoverCard';
15
18
 
16
19
  // Store original matches to restore later
@@ -55,6 +58,17 @@ describe('HoverCard', () => {
55
58
  expect(screen.getByRole('button', {name: 'Trigger'})).toBeInTheDocument();
56
59
  });
57
60
 
61
+ it('gives the floating layer role="dialog"', () => {
62
+ render(
63
+ <HoverCard content={<span>Card content</span>}>
64
+ <button type="button">Trigger</button>
65
+ </HoverCard>,
66
+ );
67
+ expect(screen.getByRole('dialog', {hidden: true})).toHaveTextContent(
68
+ 'Card content',
69
+ );
70
+ });
71
+
58
72
  it('wraps element children in an inline-safe span', () => {
59
73
  const {container} = render(
60
74
  <p>
@@ -73,6 +87,31 @@ describe('HoverCard', () => {
73
87
  expect(paragraph?.querySelector('div')).toBeNull();
74
88
  });
75
89
 
90
+ it('renders the floating layer with inline-safe markup (no block elements in a paragraph)', () => {
91
+ // HoverCard renders its floating layer inline (no portal), so the layer
92
+ // must be phrasing content to stay valid — and stay put on hydration —
93
+ // inside a <p>. Assert the layer popover element is a <span> and that the
94
+ // paragraph contains no <div> descendants at all.
95
+ const {container} = render(
96
+ <p>
97
+ Before{' '}
98
+ <HoverCard content={<span>Card content</span>}>
99
+ <a href="#trigger">Trigger</a>
100
+ </HoverCard>{' '}
101
+ after
102
+ </p>,
103
+ );
104
+
105
+ const paragraph = container.querySelector('p');
106
+ const layer = screen.getByText('Card content').closest('[popover]');
107
+
108
+ expect(layer).not.toBeNull();
109
+ expect(layer?.tagName).toBe('SPAN');
110
+ // The whole layer subtree lives inside the paragraph with no block boxes.
111
+ expect(paragraph?.contains(layer as Node)).toBe(true);
112
+ expect(paragraph?.querySelector('div')).toBeNull();
113
+ });
114
+
76
115
  it('does not show content initially', () => {
77
116
  render(
78
117
  <HoverCard content={<span>Card content</span>}>
@@ -84,7 +123,7 @@ describe('HoverCard', () => {
84
123
  expect(content).toBeInTheDocument();
85
124
  });
86
125
 
87
- it('applies the theme body font to the portaled layer', () => {
126
+ it('applies the theme body font to the floating layer', () => {
88
127
  render(
89
128
  <HoverCard content={<span>Card content</span>}>
90
129
  <button type="button">Trigger</button>
@@ -370,4 +409,152 @@ describe('HoverCard', () => {
370
409
  expect(onOpenChange).not.toHaveBeenCalledWith(true);
371
410
  });
372
411
  });
412
+
413
+ describe('SSR / hydration', () => {
414
+ // Regression coverage for the hydration mismatch (#3107). The floating
415
+ // layer used to be portaled into document.body behind a
416
+ // `typeof document !== 'undefined'` gate: the server rendered nothing while
417
+ // the first client render emitted the portal, so the two trees disagreed.
418
+ //
419
+ // The layer is now rendered inline as inline-safe phrasing markup (a
420
+ // `<span popover>`), identically on the server and the client, so there is
421
+ // nothing for hydration to mismatch.
422
+
423
+ it('renders the floating layer in server markup (no document gate)', () => {
424
+ const html = renderToString(
425
+ <HoverCard content={<span>Card content</span>}>
426
+ <button type="button">Trigger</button>
427
+ </HoverCard>,
428
+ );
429
+
430
+ // The popover element is present in the server output...
431
+ expect(html).toContain('popover="manual"');
432
+ expect(html).toContain('Card content');
433
+ // ...and it is a <span> (inline-safe), not a <div>.
434
+ expect(html).toMatch(/<span[^>]*popover="manual"/);
435
+ });
436
+
437
+ it('keeps the floating layer inline-safe in server markup inside a paragraph', () => {
438
+ const html = renderToString(
439
+ <p>
440
+ Before{' '}
441
+ <HoverCard content={<span>Card content</span>}>
442
+ <a href="#trigger">Trigger</a>
443
+ </HoverCard>{' '}
444
+ after
445
+ </p>,
446
+ );
447
+
448
+ // No <div> is emitted inside the paragraph — the layer and its wrappers
449
+ // are all phrasing content, so the server string is valid <p> markup that
450
+ // the browser parser will not reparent (which would itself desync
451
+ // hydration).
452
+ expect(html).not.toContain('<div');
453
+ expect(html).toMatch(/<span[^>]*popover="manual"/);
454
+ });
455
+
456
+ it('server markup matches the first client render (no hydration mismatch)', async () => {
457
+ const tree = (
458
+ <StrictMode>
459
+ <p>
460
+ Glossary:{' '}
461
+ <HoverCard content={<span>Definition</span>}>
462
+ <a href="#term">term</a>
463
+ </HoverCard>
464
+ .
465
+ </p>
466
+ </StrictMode>
467
+ );
468
+
469
+ const serverHTML = renderToString(tree);
470
+
471
+ const container = document.createElement('div');
472
+ container.innerHTML = serverHTML;
473
+ document.body.appendChild(container);
474
+
475
+ // Capture any hydration diagnostics. React reports hydration mismatches
476
+ // both through console.error and through onRecoverableError.
477
+ const consoleErrorSpy = vi
478
+ .spyOn(console, 'error')
479
+ .mockImplementation(() => {});
480
+ const recoverableErrors: unknown[] = [];
481
+
482
+ let root: ReturnType<typeof hydrateRoot>;
483
+ await act(async () => {
484
+ root = hydrateRoot(container, tree, {
485
+ onRecoverableError: error => {
486
+ recoverableErrors.push(error);
487
+ },
488
+ });
489
+ });
490
+
491
+ const hydrationErrors = consoleErrorSpy.mock.calls.filter(call =>
492
+ String(call[0] ?? '')
493
+ .toLowerCase()
494
+ .includes('hydrat'),
495
+ );
496
+
497
+ expect(hydrationErrors).toEqual([]);
498
+ expect(recoverableErrors).toEqual([]);
499
+
500
+ await act(async () => {
501
+ root.unmount();
502
+ });
503
+ consoleErrorSpy.mockRestore();
504
+ container.remove();
505
+ });
506
+
507
+ it('hydrates a default-open hover card without a mismatch', async () => {
508
+ vi.mocked(HTMLElement.prototype.showPopover).mockClear();
509
+
510
+ const tree = (
511
+ <HoverCard content={<span>Default open</span>} isDefaultOpen>
512
+ <button type="button">Trigger</button>
513
+ </HoverCard>
514
+ );
515
+
516
+ const serverHTML = renderToString(tree);
517
+ // isDefaultOpen must not leak the open state into SSR markup — the open
518
+ // call happens in an effect after hydration, so the server output is the
519
+ // same closed markup the first client render produces.
520
+ expect(serverHTML).toContain('popover="manual"');
521
+
522
+ const container = document.createElement('div');
523
+ container.innerHTML = serverHTML;
524
+ document.body.appendChild(container);
525
+
526
+ const consoleErrorSpy = vi
527
+ .spyOn(console, 'error')
528
+ .mockImplementation(() => {});
529
+ const recoverableErrors: unknown[] = [];
530
+
531
+ let root: ReturnType<typeof hydrateRoot>;
532
+ await act(async () => {
533
+ root = hydrateRoot(container, tree, {
534
+ onRecoverableError: error => {
535
+ recoverableErrors.push(error);
536
+ },
537
+ });
538
+ });
539
+
540
+ const hydrationErrors = consoleErrorSpy.mock.calls.filter(call =>
541
+ String(call[0] ?? '')
542
+ .toLowerCase()
543
+ .includes('hydrat'),
544
+ );
545
+ expect(hydrationErrors).toEqual([]);
546
+ expect(recoverableErrors).toEqual([]);
547
+
548
+ // The card opens after hydration via the mount effect.
549
+ await waitFor(() => {
550
+ expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
551
+ });
552
+
553
+ await act(async () => {
554
+ root.unmount();
555
+ });
556
+ consoleErrorSpy.mockRestore();
557
+ container.remove();
558
+ });
559
+ });
373
560
  });
@@ -4,9 +4,9 @@
4
4
 
5
5
  /**
6
6
  * @file HoverCard.tsx
7
- * @input Uses React, ReactDOM createPortal, useHoverCard hook
7
+ * @input Uses React, useHoverCard hook
8
8
  * @output Exports HoverCard component for hover/focus triggered layers
9
- * @position Layer component; uses inline-safe trigger wrapper and portals floating layer
9
+ * @position Layer component; uses inline-safe trigger wrapper and renders the floating layer inline
10
10
  *
11
11
  * SYNC: When modified, update these files to stay in sync:
12
12
  * - /packages/core/src/HoverCard/HoverCard.test.tsx
@@ -15,13 +15,7 @@
15
15
  * - /packages/cli/templates/blocks/components/HoverCard/ (showcase blocks)
16
16
  */
17
17
 
18
- import React, {
19
- useCallback,
20
- useRef,
21
- type ReactElement,
22
- type ReactNode,
23
- } from 'react';
24
- import {createPortal} from 'react-dom';
18
+ import {useCallback, useRef, type ReactElement, type ReactNode} from 'react';
25
19
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
26
20
  import * as stylex from '@stylexjs/stylex';
27
21
  import {useHoverCard, type HoverCardFocusTrigger} from './useHoverCard';
@@ -245,13 +239,23 @@ export function HoverCard({
245
239
  };
246
240
  }, [textOnly, hoverCard.ref, hoverCard.describedBy]);
247
241
 
248
- const renderedHoverCard =
249
- typeof document !== 'undefined'
250
- ? createPortal(
251
- hoverCard.renderHoverCard(content, {xstyle, className, style}),
252
- document.body,
253
- )
254
- : null;
242
+ // Render the floating layer inline, in the same place on the server and the
243
+ // client. The layer is a `popover` element opened via the Popover API, so the
244
+ // browser promotes it to the top layer when shown — that already escapes
245
+ // ancestor clipping, stacking, and transform containing-block traps, and CSS
246
+ // anchor positioning resolves the trigger reference regardless of where the
247
+ // element sits in the DOM, so no portal is needed to "escape" layout.
248
+ //
249
+ // The layer renders as inline-safe phrasing markup (a `<span>`, see
250
+ // useHoverCard), which stays put inside a `<p>` instead of being reparented
251
+ // by the HTML parser. That keeps the server markup and the first client
252
+ // render identical, so there is no hydration mismatch — and it preserves the
253
+ // inline-safety guarantee (no block elements injected into a paragraph).
254
+ const renderedHoverCard = hoverCard.renderHoverCard(content, {
255
+ xstyle,
256
+ className,
257
+ style,
258
+ });
255
259
 
256
260
  // For text-only children: use inline span with ref on wrapper
257
261
  if (textOnly) {
@@ -58,8 +58,12 @@ const styles = stylex.create({
58
58
  marginInlineStart: spacingVars['--spacing-1'],
59
59
  marginInlineEnd: spacingVars['--spacing-1'],
60
60
  },
61
- // Content wrapper for padding and mouse events
61
+ // Content wrapper for padding and mouse events.
62
+ // `display: block` keeps the wrapper a block box even though it renders as a
63
+ // `span` (the layer uses inline-safe phrasing markup so it is valid inside a
64
+ // paragraph and produces identical server/client markup).
62
65
  content: {
66
+ display: 'block',
63
67
  paddingBlockStart: spacingVars['--spacing-3'],
64
68
  paddingBlockEnd: spacingVars['--spacing-3'],
65
69
  paddingInlineStart: spacingVars['--spacing-3'],
@@ -234,9 +238,7 @@ function isFocusable(element: HTMLElement): boolean {
234
238
  * {hoverCard.renderHoverCard(<ProfileCard user={user} />)}
235
239
  * ```
236
240
  */
237
- export function useHoverCard(
238
- options: HoverCardOptions = {},
239
- ): HoverCardReturn {
241
+ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
240
242
  const {
241
243
  placement = 'above',
242
244
  alignment = 'center',
@@ -453,15 +455,16 @@ export function useHoverCard(
453
455
  const renderProps = {
454
456
  placement: renderPlacement,
455
457
  alignment: props?.alignment ?? alignment,
458
+ role: 'dialog',
456
459
  xstyle: [popoverXstyle, layerAnimations[renderPlacement]],
460
+ // Render the layer as inline-safe phrasing markup so HoverCard stays
461
+ // valid (and hydration-stable) inside inline contexts like a `<p>`.
462
+ as: 'span' as const,
457
463
  };
458
464
 
459
465
  return layer.render(
460
- <div
461
- {...mergeProps(
462
- themeProps('hovercard'),
463
- stylex.props(styles.content),
464
- )}
466
+ <span
467
+ {...mergeProps(themeProps('hovercard'), stylex.props(styles.content))}
465
468
  onMouseEnter={() => {
466
469
  isHoveringContentRef.current = true;
467
470
  clearTimeouts();
@@ -502,7 +505,7 @@ export function useHoverCard(
502
505
  scheduleHide();
503
506
  }}>
504
507
  {children}
505
- </div>,
508
+ </span>,
506
509
  renderProps,
507
510
  );
508
511
  },
@@ -7,6 +7,14 @@ export const docs = {
7
7
  displayName: 'Icon',
8
8
  category: 'Content',
9
9
  keywords: ["icon","svg","glyph","symbol","pictogram","graphic","vector"],
10
+ playground: {
11
+ // `icon` is required and its type can't be auto-generated, so the
12
+ // properties-tab preview showed "Missing: icon". Seed a valid semantic
13
+ // icon name so the interactive preview renders.
14
+ defaults: {
15
+ icon: 'search',
16
+ },
17
+ },
10
18
  props: [
11
19
  {
12
20
  name: 'icon',
@@ -92,7 +92,7 @@ export const docs = {
92
92
  bestPractices: [
93
93
  {guidance: true, description: 'Use text addons to show units, prefixes, or suffixes that clarify the input format (e.g., "$", "kg", "https://").'},
94
94
  {guidance: true, description: 'Use InputGroupText for static prefixes/suffixes like "$", "kg", or "https://".'},
95
- {guidance: true, description: 'Set isLabelHidden on the inner input and let the group label be visible.'},
95
+ {guidance: true, description: "Keep each inner input's label specific; grouped inputs automatically combine the group label with their own label and inherit the group description/status context."},
96
96
  {guidance: false, description: 'Don\'t put multiple text inputs in one group; use separate fields instead.'},
97
97
  {guidance: false, description: 'Don\'t use InputGroup for unrelated inputs; it\'s for a single input with decorations.'},
98
98
  ],
@@ -114,7 +114,7 @@ export const docsDense = {
114
114
  bestPractices: [
115
115
  {guidance: true, description: 'Use text addons to show units, prefixes, or suffixes that clarify input format (e.g. "$", "kg", "https://").'},
116
116
  {guidance: true, description: 'Use InputGroupText for static prefixes/suffixes like "$", "kg", or "https://".'},
117
- {guidance: true, description: 'Set isLabelHidden on the inner input and let the group label be visible.'},
117
+ {guidance: true, description: "Keep each inner input's label specific; grouped inputs combine the group label with their own label and inherit group description/status."},
118
118
  {guidance: false, description: 'Don\'t put multiple text inputs in one group; use separate fields instead.'},
119
119
  {guidance: false, description: 'Don\'t use InputGroup for unrelated inputs; it\'s for a single input with decorations.'},
120
120
  ],
@@ -14,35 +14,113 @@ import {render, screen} from '@testing-library/react';
14
14
  import {InputGroup} from './InputGroup';
15
15
  import {InputGroupText} from './InputGroupText';
16
16
  import {TextInput} from '../TextInput';
17
+ import {NumberInput} from '../NumberInput';
17
18
 
18
19
  describe('InputGroup', () => {
19
- it('renders a group with aria-label', () => {
20
+ it('names the group via the label element (forms-14)', () => {
20
21
  render(
21
22
  <InputGroup label="Price">
22
23
  <InputGroupText>$</InputGroupText>
23
- <TextInput
24
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
25
+ </InputGroup>,
26
+ );
27
+
28
+ // The group is named by the field label via aria-labelledby (not a
29
+ // duplicated aria-label). The label is rendered as a <span> (not a literal
30
+ // <label>, which can't name a group) and carries no orphaned htmlFor.
31
+ const group = screen.getByRole('group', {name: 'Price'});
32
+ expect(group).toBeInTheDocument();
33
+ expect(group).not.toHaveAttribute('aria-label');
34
+ const label = screen.getByText('Price');
35
+ expect(label.tagName).toBe('SPAN');
36
+ expect(label.closest('label')).toBeNull();
37
+ expect(label).not.toHaveAttribute('for');
38
+ expect(group.getAttribute('aria-labelledby')).toBe(label.id);
39
+ });
40
+
41
+ it('associates group description and status through aria-describedby', () => {
42
+ render(
43
+ <InputGroup
44
+ label="Price"
45
+ description="Enter the amount in USD"
46
+ status={{type: 'error', message: 'Price is required'}}>
47
+ <InputGroupText>$</InputGroupText>
48
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
49
+ </InputGroup>,
50
+ );
51
+
52
+ const group = screen.getByRole('group', {name: 'Price'});
53
+ const describedBy = group.getAttribute('aria-describedby');
54
+ expect(describedBy).toBeTruthy();
55
+ expect(describedBy!.split(' ')).toHaveLength(2);
56
+ expect(describedBy).toContain(
57
+ screen.getByText('Enter the amount in USD').id,
58
+ );
59
+ expect(describedBy).toContain(screen.getByText('Price is required').id);
60
+ });
61
+
62
+ it('labels grouped TextInput from the group and inner input labels', () => {
63
+ render(
64
+ <InputGroup
65
+ label="Price"
66
+ description="Enter the amount in USD"
67
+ status={{type: 'error', message: 'Price is required'}}>
68
+ <InputGroupText>$</InputGroupText>
69
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
70
+ </InputGroup>,
71
+ );
72
+
73
+ const group = screen.getByRole('group', {name: 'Price'});
74
+ const groupLabelID = group.getAttribute('aria-labelledby');
75
+ const describedBy = group.getAttribute('aria-describedby');
76
+ const input = screen.getByRole('textbox', {name: 'Price Amount'});
77
+ const labelledByIDs =
78
+ input.getAttribute('aria-labelledby')?.split(' ') ?? [];
79
+
80
+ expect(labelledByIDs).toHaveLength(2);
81
+ expect(labelledByIDs[0]).toBe(groupLabelID);
82
+ expect(document.getElementById(labelledByIDs[1])).toHaveTextContent(
83
+ 'Amount',
84
+ );
85
+ expect(input).not.toHaveAttribute('aria-label');
86
+ expect(input).toHaveAttribute('aria-describedby', describedBy);
87
+ });
88
+
89
+ it('labels grouped NumberInput from the group and inner input labels', () => {
90
+ render(
91
+ <InputGroup label="Budget" description="Whole dollars only">
92
+ <InputGroupText>$</InputGroupText>
93
+ <NumberInput
24
94
  label="Amount"
25
95
  isLabelHidden
26
- value=""
96
+ value={null}
27
97
  onChange={() => {}}
28
98
  />
29
99
  </InputGroup>,
30
100
  );
31
101
 
32
- const group = screen.getByRole('group');
33
- expect(group).toBeInTheDocument();
34
- expect(group).toHaveAttribute('aria-label', 'Price');
102
+ const group = screen.getByRole('group', {name: 'Budget'});
103
+ const groupLabelID = group.getAttribute('aria-labelledby');
104
+ const input = screen.getByRole('spinbutton', {name: 'Budget Amount'});
105
+ const labelledByIDs =
106
+ input.getAttribute('aria-labelledby')?.split(' ') ?? [];
107
+
108
+ expect(labelledByIDs).toHaveLength(2);
109
+ expect(labelledByIDs[0]).toBe(groupLabelID);
110
+ expect(document.getElementById(labelledByIDs[1])).toHaveTextContent(
111
+ 'Amount',
112
+ );
113
+ expect(input).not.toHaveAttribute('aria-label');
114
+ expect(input).toHaveAttribute(
115
+ 'aria-describedby',
116
+ group.getAttribute('aria-describedby'),
117
+ );
35
118
  });
36
119
 
37
120
  it('renders the visible label', () => {
38
121
  render(
39
122
  <InputGroup label="Price">
40
- <TextInput
41
- label="Amount"
42
- isLabelHidden
43
- value=""
44
- onChange={() => {}}
45
- />
123
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
46
124
  </InputGroup>,
47
125
  );
48
126
 
@@ -53,12 +131,7 @@ describe('InputGroup', () => {
53
131
  render(
54
132
  <InputGroup label="Price">
55
133
  <InputGroupText>$</InputGroupText>
56
- <TextInput
57
- label="Amount"
58
- isLabelHidden
59
- value=""
60
- onChange={() => {}}
61
- />
134
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
62
135
  </InputGroup>,
63
136
  );
64
137
 
@@ -68,12 +141,7 @@ describe('InputGroup', () => {
68
141
  it('renders the input', () => {
69
142
  render(
70
143
  <InputGroup label="Price">
71
- <TextInput
72
- label="Amount"
73
- isLabelHidden
74
- value=""
75
- onChange={() => {}}
76
- />
144
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
77
145
  </InputGroup>,
78
146
  );
79
147
 
@@ -97,12 +165,7 @@ describe('InputGroup', () => {
97
165
  it('applies data-testid', () => {
98
166
  render(
99
167
  <InputGroup label="Price" data-testid="price-group">
100
- <TextInput
101
- label="Amount"
102
- isLabelHidden
103
- value=""
104
- onChange={() => {}}
105
- />
168
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
106
169
  </InputGroup>,
107
170
  );
108
171
 
@@ -112,12 +175,7 @@ describe('InputGroup', () => {
112
175
  it('renders description text', () => {
113
176
  render(
114
177
  <InputGroup label="Price" description="Enter the price in USD">
115
- <TextInput
116
- label="Amount"
117
- isLabelHidden
118
- value=""
119
- onChange={() => {}}
120
- />
178
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
121
179
  </InputGroup>,
122
180
  );
123
181
 
@@ -129,12 +187,7 @@ describe('InputGroup', () => {
129
187
  <InputGroup
130
188
  label="Price"
131
189
  status={{type: 'error', message: 'Price is required'}}>
132
- <TextInput
133
- label="Amount"
134
- isLabelHidden
135
- value=""
136
- onChange={() => {}}
137
- />
190
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
138
191
  </InputGroup>,
139
192
  );
140
193
 
@@ -144,12 +197,7 @@ describe('InputGroup', () => {
144
197
  it('renders with hidden label', () => {
145
198
  render(
146
199
  <InputGroup label="Price" isLabelHidden>
147
- <TextInput
148
- label="Amount"
149
- isLabelHidden
150
- value=""
151
- onChange={() => {}}
152
- />
200
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
153
201
  </InputGroup>,
154
202
  );
155
203
 
@@ -160,24 +208,14 @@ describe('InputGroup', () => {
160
208
  it('renders with different sizes', () => {
161
209
  const {rerender} = render(
162
210
  <InputGroup label="Price" size="sm">
163
- <TextInput
164
- label="Amount"
165
- isLabelHidden
166
- value=""
167
- onChange={() => {}}
168
- />
211
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
169
212
  </InputGroup>,
170
213
  );
171
214
  expect(screen.getByRole('textbox')).toBeInTheDocument();
172
215
 
173
216
  rerender(
174
217
  <InputGroup label="Price" size="lg">
175
- <TextInput
176
- label="Amount"
177
- isLabelHidden
178
- value=""
179
- onChange={() => {}}
180
- />
218
+ <TextInput label="Amount" isLabelHidden value="" onChange={() => {}} />
181
219
  </InputGroup>,
182
220
  );
183
221
  expect(screen.getByRole('textbox')).toBeInTheDocument();
@@ -4,8 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file InputGroup.tsx
7
- * @input Uses React, StyleX, theme tokens, InputGroupContext
8
- * @output Exports InputGroup component
7
+ * @input Uses React, StyleX, theme tokens, InputGroupContext, Field
8
+ * @output Exports InputGroup component with group label/description ARIA wiring
9
9
  * @position Groups input with prefix/suffix addons; consumed by index.ts
10
10
  *
11
11
  * Children (TextInput, NumberInput) consume the InputGroup context
@@ -156,9 +156,22 @@ export function InputGroup({
156
156
  }: InputGroupProps) {
157
157
  const size = useSize(sizeProp, 'md');
158
158
  const inputId = useId();
159
+ const labelID = useId();
160
+ const descriptionID = useId();
159
161
  const statusMessageId = useId();
160
162
 
161
- const contextValue = useMemo(() => ({isInGroup: true as const}), []);
163
+ const describedByIDs =
164
+ [
165
+ description ? descriptionID : null,
166
+ status?.message ? statusMessageId : null,
167
+ ]
168
+ .filter(Boolean)
169
+ .join(' ') || undefined;
170
+
171
+ const contextValue = useMemo(
172
+ () => ({isInGroup: true as const, labelID, describedByIDs}),
173
+ [labelID, describedByIDs],
174
+ );
162
175
 
163
176
  return (
164
177
  <InputGroupContext value={contextValue}>
@@ -168,6 +181,9 @@ export function InputGroup({
168
181
  isLabelHidden={isLabelHidden}
169
182
  description={description}
170
183
  inputID={inputId}
184
+ labelID={labelID}
185
+ descriptionID={description ? descriptionID : undefined}
186
+ isGroupLabel
171
187
  isOptional={isOptional}
172
188
  isRequired={isRequired}
173
189
  isDisabled={isDisabled}
@@ -185,7 +201,8 @@ export function InputGroup({
185
201
  <div
186
202
  ref={ref}
187
203
  role="group"
188
- aria-label={label}
204
+ aria-labelledby={labelID}
205
+ aria-describedby={describedByIDs}
189
206
  data-testid={testId}
190
207
  {...rest}
191
208
  {...mergeProps(