@astryxdesign/core 0.1.9 → 0.2.0-canary.0f56f82

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 (565) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +51 -5
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  245. package/dist/TopNav/TopNavMegaMenu.js +9 -3
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +1 -0
  248. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListBranches.js +8 -6
  250. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  251. package/dist/TreeList/TreeListItem.js +32 -14
  252. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  253. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  254. package/dist/Typeahead/BaseTypeahead.js +22 -11
  255. package/dist/Typeahead/Typeahead.d.ts +9 -2
  256. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  257. package/dist/Typeahead/Typeahead.js +11 -2
  258. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  259. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  260. package/dist/Typeahead/TypeaheadItem.js +20 -10
  261. package/dist/astryx.css +74 -33
  262. package/dist/astryx.umd.js +51 -51
  263. package/dist/astryx.umd.js.map +4 -4
  264. package/dist/hooks/index.d.ts +2 -0
  265. package/dist/hooks/index.d.ts.map +1 -1
  266. package/dist/hooks/index.js +1 -0
  267. package/dist/hooks/isRtlElement.d.ts +18 -0
  268. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  269. package/dist/hooks/isRtlElement.js +27 -0
  270. package/dist/hooks/useAnnounce.d.ts +3 -0
  271. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  272. package/dist/hooks/useAnnounce.js +39 -1
  273. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  274. package/dist/hooks/useFocusTrap.js +52 -11
  275. package/dist/hooks/useGridFocus.d.ts +6 -1
  276. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  277. package/dist/hooks/useGridFocus.js +12 -9
  278. package/dist/hooks/useHotkeys.d.ts +9 -0
  279. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  280. package/dist/hooks/useHotkeys.js +9 -0
  281. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  282. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  283. package/dist/hooks/useInputStatusIcon.js +169 -0
  284. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  285. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  286. package/dist/hooks/useKeyboardHint.js +4 -2
  287. package/dist/hooks/useListFocus.d.ts +39 -1
  288. package/dist/hooks/useListFocus.d.ts.map +1 -1
  289. package/dist/hooks/useListFocus.js +54 -9
  290. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  291. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  292. package/dist/i18n/InternationalizationContext.js +3 -0
  293. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  294. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  295. package/dist/i18n/InternationalizationProvider.js +5 -1
  296. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  297. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  298. package/dist/i18n/getLocaleDirection.js +29 -0
  299. package/dist/i18n/index.d.ts +6 -0
  300. package/dist/i18n/index.d.ts.map +1 -1
  301. package/dist/i18n/index.js +7 -1
  302. package/dist/i18n/useDirection.d.ts +2 -0
  303. package/dist/i18n/useDirection.d.ts.map +1 -0
  304. package/dist/i18n/useDirection.js +27 -0
  305. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  306. package/dist/theme/derivedVarRegistry.js +13 -0
  307. package/dist/utils/index.d.ts +1 -0
  308. package/dist/utils/index.d.ts.map +1 -1
  309. package/dist/utils/index.js +2 -1
  310. package/dist/utils/plainDate.d.ts +17 -0
  311. package/dist/utils/plainDate.d.ts.map +1 -1
  312. package/dist/utils/plainDate.js +23 -4
  313. package/dist/utils/rtlStyles.d.ts +24 -0
  314. package/dist/utils/rtlStyles.d.ts.map +1 -0
  315. package/dist/utils/rtlStyles.js +27 -0
  316. package/locales/en.json +112 -0
  317. package/locales/pseudo.json +84 -0
  318. package/package.json +6 -1
  319. package/src/Avatar/Avatar.doc.mjs +50 -3
  320. package/src/Avatar/Avatar.test.tsx +218 -0
  321. package/src/Avatar/Avatar.tsx +285 -79
  322. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  323. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  324. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  325. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  326. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  327. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  328. package/src/Banner/Banner.tsx +6 -6
  329. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  330. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  331. package/src/Breadcrumbs/index.ts +4 -0
  332. package/src/Button/Button.doc.mjs +0 -4
  333. package/src/Button/Button.test.tsx +16 -0
  334. package/src/Button/Button.tsx +6 -4
  335. package/src/Calendar/Calendar.test.tsx +170 -20
  336. package/src/Calendar/Calendar.tsx +73 -16
  337. package/src/Calendar/dayCellUtils.test.ts +18 -18
  338. package/src/Calendar/dayCellUtils.ts +2 -2
  339. package/src/Calendar/styles.ts +33 -39
  340. package/src/Card/Card.tsx +1 -1
  341. package/src/Carousel/Carousel.tsx +25 -6
  342. package/src/Chat/ChatComposer.doc.mjs +2 -2
  343. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  344. package/src/Chat/ChatComposerInput.tsx +2 -2
  345. package/src/Chat/ChatDictationButton.tsx +7 -1
  346. package/src/Chat/ChatLayout.tsx +4 -4
  347. package/src/Chat/ChatMessageBubble.tsx +15 -10
  348. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  349. package/src/Chat/ChatToolCalls.tsx +9 -2
  350. package/src/Chat/useTriggerMenu.tsx +1 -1
  351. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  352. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  353. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  354. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  355. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  356. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  357. package/src/Citation/Citation.doc.mjs +4 -3
  358. package/src/Citation/Citation.test.tsx +97 -0
  359. package/src/Citation/Citation.tsx +43 -11
  360. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  361. package/src/CodeBlock/CodeBlock.tsx +38 -11
  362. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  363. package/src/Collapsible/Collapsible.test.tsx +7 -0
  364. package/src/Collapsible/Collapsible.tsx +16 -6
  365. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  366. package/src/CommandPalette/CommandPalette.tsx +41 -2
  367. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  368. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  369. package/src/ContextMenu/ContextMenu.tsx +22 -11
  370. package/src/ContextMenu/index.ts +5 -0
  371. package/src/DateInput/DateInput.doc.mjs +25 -0
  372. package/src/DateInput/DateInput.test.tsx +230 -0
  373. package/src/DateInput/DateInput.tsx +51 -29
  374. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  375. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  376. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  377. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  378. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  379. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  380. package/src/Divider/Divider.test.tsx +32 -8
  381. package/src/Divider/Divider.tsx +14 -1
  382. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  383. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  384. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  385. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  386. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  387. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  388. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  389. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  390. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  391. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  392. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  393. package/src/DropdownMenu/index.ts +16 -0
  394. package/src/DropdownMenu/menuItemHover.ts +39 -0
  395. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  396. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  397. package/src/Field/Field.doc.mjs +4 -0
  398. package/src/Field/Field.tsx +14 -9
  399. package/src/Field/FieldLabel.test.tsx +25 -0
  400. package/src/Field/FieldLabel.tsx +12 -3
  401. package/src/Field/inputStyles.stylex.ts +2 -0
  402. package/src/FieldStatus/FieldStatus.doc.mjs +1 -0
  403. package/src/FieldStatus/FieldStatus.test.tsx +133 -0
  404. package/src/FieldStatus/FieldStatus.tsx +61 -4
  405. package/src/FileInput/FileInput.doc.mjs +23 -2
  406. package/src/FileInput/FileInput.test.tsx +104 -13
  407. package/src/FileInput/FileInput.tsx +106 -79
  408. package/src/HoverCard/useHoverCard.tsx +15 -3
  409. package/src/Icon/Icon.doc.mjs +13 -0
  410. package/src/Icon/Icon.test.tsx +89 -0
  411. package/src/Icon/Icon.tsx +51 -3
  412. package/src/Item/Item.doc.mjs +5 -0
  413. package/src/Item/Item.test.tsx +88 -0
  414. package/src/Item/Item.tsx +57 -11
  415. package/src/Layer/layerAnimations.stylex.ts +23 -0
  416. package/src/Lightbox/Lightbox.tsx +15 -7
  417. package/src/List/List.test.tsx +17 -29
  418. package/src/List/List.tsx +6 -1
  419. package/src/List/ListItem.doc.mjs +12 -0
  420. package/src/List/ListItem.tsx +13 -0
  421. package/src/Markdown/Markdown.test.tsx +21 -0
  422. package/src/Markdown/Markdown.tsx +4 -0
  423. package/src/MetadataList/MetadataList.test.tsx +24 -0
  424. package/src/MetadataList/MetadataList.tsx +6 -2
  425. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  426. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  427. package/src/MultiSelector/MultiSelector.tsx +120 -59
  428. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  429. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  430. package/src/NumberInput/NumberInput.test.tsx +71 -0
  431. package/src/NumberInput/NumberInput.tsx +35 -26
  432. package/src/Outline/Outline.test.tsx +12 -12
  433. package/src/Outline/Outline.tsx +2 -2
  434. package/src/Pagination/Pagination.tsx +11 -3
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  437. package/src/PowerSearch/PowerSearch.tsx +10 -1
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  439. package/src/ProgressBar/ProgressBar.tsx +16 -1
  440. package/src/RadioList/RadioList.doc.mjs +6 -0
  441. package/src/RadioList/RadioList.test.tsx +26 -4
  442. package/src/RadioList/RadioList.tsx +2 -2
  443. package/src/RadioList/RadioListItem.tsx +12 -5
  444. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  445. package/src/Resizable/ResizeHandle.tsx +70 -20
  446. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  447. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  448. package/src/Selector/Selector.doc.mjs +15 -0
  449. package/src/Selector/Selector.test.tsx +370 -0
  450. package/src/Selector/Selector.tsx +88 -20
  451. package/src/SideNav/SideNav.tsx +1 -8
  452. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  453. package/src/SideNav/SideNavItem.tsx +5 -3
  454. package/src/Skeleton/Skeleton.test.tsx +16 -0
  455. package/src/Skeleton/Skeleton.tsx +13 -1
  456. package/src/Slider/Slider.doc.mjs +6 -0
  457. package/src/Slider/Slider.test.tsx +240 -12
  458. package/src/Slider/Slider.tsx +96 -23
  459. package/src/Spinner/Spinner.test.tsx +10 -8
  460. package/src/Spinner/Spinner.tsx +15 -5
  461. package/src/Switch/Switch.doc.mjs +142 -42
  462. package/src/Switch/Switch.test.tsx +111 -0
  463. package/src/Switch/Switch.tsx +149 -32
  464. package/src/TabList/Tab.tsx +2 -2
  465. package/src/TabList/TabList.doc.mjs +0 -6
  466. package/src/TabList/TabList.test.tsx +3 -15
  467. package/src/TabList/TabList.tsx +6 -17
  468. package/src/TabList/TabListContext.ts +0 -6
  469. package/src/TabList/TabMenu.tsx +2 -2
  470. package/src/Table/BaseTable.tsx +34 -2
  471. package/src/Table/Table.doc.mjs +11 -0
  472. package/src/Table/Table.test.tsx +85 -0
  473. package/src/Table/TableCell.tsx +3 -3
  474. package/src/Table/TableHeaderCell.tsx +3 -3
  475. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  476. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  477. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  478. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  479. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  480. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  481. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  482. package/src/Table/types.ts +33 -0
  483. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  484. package/src/TextArea/TextArea.doc.mjs +21 -0
  485. package/src/TextArea/TextArea.test.tsx +23 -0
  486. package/src/TextArea/TextArea.tsx +27 -26
  487. package/src/TextInput/TextInput.doc.mjs +21 -0
  488. package/src/TextInput/TextInput.test.tsx +150 -0
  489. package/src/TextInput/TextInput.tsx +28 -25
  490. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  491. package/src/Thumbnail/Thumbnail.tsx +1 -1
  492. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  493. package/src/TimeInput/TimeInput.test.tsx +23 -0
  494. package/src/TimeInput/TimeInput.tsx +24 -25
  495. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  496. package/src/Timestamp/Timestamp.test.tsx +371 -0
  497. package/src/Timestamp/Timestamp.tsx +96 -93
  498. package/src/Timestamp/formatInstant.ts +218 -0
  499. package/src/Timestamp/index.ts +4 -0
  500. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  501. package/src/Timestamp/tooltipEntries.ts +210 -0
  502. package/src/Toast/ToastViewport.test.tsx +218 -8
  503. package/src/Toast/ToastViewport.tsx +71 -12
  504. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  505. package/src/ToggleButton/ToggleButton.tsx +19 -1
  506. package/src/Token/Token.test.tsx +166 -15
  507. package/src/Token/Token.tsx +104 -47
  508. package/src/Token/TokenLink.tsx +107 -0
  509. package/src/Token/index.ts +1 -1
  510. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  511. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  512. package/src/Tokenizer/Tokenizer.tsx +14 -2
  513. package/src/Tooltip/Tooltip.test.tsx +46 -0
  514. package/src/Tooltip/useTooltip.tsx +20 -0
  515. package/src/TopNav/TopNav.test.tsx +93 -2
  516. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  517. package/src/TopNav/TopNavItem.tsx +35 -6
  518. package/src/TopNav/TopNavMegaMenu.tsx +26 -2
  519. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  520. package/src/TreeList/TreeList.doc.mjs +6 -0
  521. package/src/TreeList/TreeList.test.tsx +50 -10
  522. package/src/TreeList/TreeList.tsx +5 -0
  523. package/src/TreeList/TreeListBranches.tsx +8 -6
  524. package/src/TreeList/TreeListItem.tsx +33 -11
  525. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  526. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  527. package/src/Typeahead/Typeahead.test.tsx +112 -0
  528. package/src/Typeahead/Typeahead.tsx +17 -1
  529. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  530. package/src/__tests__/forcedColors.ts +60 -0
  531. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  532. package/src/doc-mjs.d.ts +19 -0
  533. package/src/hooks/index.ts +6 -0
  534. package/src/hooks/isRtlElement.ts +27 -0
  535. package/src/hooks/useAnnounce.test.tsx +75 -2
  536. package/src/hooks/useAnnounce.ts +47 -1
  537. package/src/hooks/useFocusTrap.test.tsx +104 -0
  538. package/src/hooks/useFocusTrap.ts +61 -12
  539. package/src/hooks/useGridFocus.doc.mjs +3 -3
  540. package/src/hooks/useGridFocus.test.tsx +41 -0
  541. package/src/hooks/useGridFocus.ts +21 -10
  542. package/src/hooks/useHotkeys.ts +9 -0
  543. package/src/hooks/useInputStatusIcon.test.tsx +221 -0
  544. package/src/hooks/useInputStatusIcon.tsx +235 -0
  545. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  546. package/src/hooks/useKeyboardHint.tsx +6 -2
  547. package/src/hooks/useListFocus.doc.mjs +25 -3
  548. package/src/hooks/useListFocus.test.tsx +153 -1
  549. package/src/hooks/useListFocus.ts +102 -9
  550. package/src/i18n/InternationalizationContext.ts +4 -0
  551. package/src/i18n/InternationalizationProvider.tsx +12 -2
  552. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  553. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  554. package/src/i18n/getLocaleDirection.ts +29 -0
  555. package/src/i18n/index.ts +6 -0
  556. package/src/i18n/useDirection.ts +29 -0
  557. package/src/reset.css +0 -26
  558. package/src/reset.test.ts +60 -0
  559. package/src/theme/derivedVarRegistry.test.ts +1 -0
  560. package/src/theme/derivedVarRegistry.ts +6 -0
  561. package/src/theme/generateThemeRules.test.ts +42 -0
  562. package/src/utils/index.ts +2 -0
  563. package/src/utils/plainDate.test.ts +24 -6
  564. package/src/utils/plainDate.ts +18 -4
  565. package/src/utils/rtlStyles.ts +27 -0
@@ -41,6 +41,7 @@ import {useTooltip} from '../Tooltip';
41
41
  import {VisuallyHidden} from '../VisuallyHidden';
42
42
  import type {InputStatus} from '../Field/types';
43
43
  import {mergeProps, mergeRefs} from '../utils';
44
+ import {isRtlElement} from '../hooks/isRtlElement';
44
45
  import type {BaseProps} from '../BaseProps';
45
46
  import type {SizeValue} from '../utils/types';
46
47
  import {themeProps} from '../utils/themeProps';
@@ -192,8 +193,8 @@ const styles = stylex.create({
192
193
  borderRadius: radiusVars['--radius-full'],
193
194
  },
194
195
  trackHorizontal: {
195
- left: 0,
196
- right: 0,
196
+ insetInlineStart: 0,
197
+ insetInlineEnd: 0,
197
198
  height: TRACK_SIZE,
198
199
  top: '50%',
199
200
  transform: 'translateY(-50%)',
@@ -202,7 +203,7 @@ const styles = stylex.create({
202
203
  top: 0,
203
204
  bottom: 0,
204
205
  width: TRACK_SIZE,
205
- left: '50%',
206
+ insetInlineStart: '50%',
206
207
  transform: 'translateX(-50%)',
207
208
  },
208
209
  filledTrack: {
@@ -217,7 +218,7 @@ const styles = stylex.create({
217
218
  },
218
219
  filledTrackVertical: {
219
220
  width: TRACK_SIZE,
220
- left: '50%',
221
+ insetInlineStart: '50%',
221
222
  transform: 'translateX(-50%)',
222
223
  },
223
224
  thumb: {
@@ -239,9 +240,17 @@ const styles = stylex.create({
239
240
  },
240
241
  thumbHorizontal: {
241
242
  top: '50%',
243
+ // The thumb is positioned via logical `insetInlineStart`, which resolves
244
+ // from the right edge under RTL. The centering translate is a physical
245
+ // (screen-space) transform, so it must flip its X direction under RTL to
246
+ // keep the thumb centered on the value point.
247
+ transform: {
248
+ default: 'translate(-50%, -50%)',
249
+ ':is([dir="rtl"] *)': 'translate(50%, -50%)',
250
+ },
242
251
  },
243
252
  thumbVertical: {
244
- left: '50%',
253
+ insetInlineStart: '50%',
245
254
  transform: 'translate(-50%, 50%)',
246
255
  },
247
256
  thumbHover: {
@@ -277,14 +286,14 @@ const styles = stylex.create({
277
286
  position: 'absolute',
278
287
  },
279
288
  marksContainerHorizontal: {
280
- left: 0,
281
- right: 0,
289
+ insetInlineStart: 0,
290
+ insetInlineEnd: 0,
282
291
  top: '50%',
283
292
  },
284
293
  marksContainerVertical: {
285
294
  top: 0,
286
295
  bottom: 0,
287
- left: '50%',
296
+ insetInlineStart: '50%',
288
297
  },
289
298
  mark: {
290
299
  position: 'absolute',
@@ -294,7 +303,10 @@ const styles = stylex.create({
294
303
  markHorizontal: {
295
304
  width: 2,
296
305
  height: 8,
297
- transform: 'translate(-50%, -50%)',
306
+ transform: {
307
+ default: 'translate(-50%, -50%)',
308
+ ':is([dir="rtl"] *)': 'translate(50%, -50%)',
309
+ },
298
310
  },
299
311
  markVertical: {
300
312
  height: 2,
@@ -309,12 +321,15 @@ const styles = stylex.create({
309
321
  whiteSpace: 'nowrap',
310
322
  },
311
323
  markLabelHorizontal: {
312
- transform: 'translateX(-50%)',
324
+ transform: {
325
+ default: 'translateX(-50%)',
326
+ ':is([dir="rtl"] *)': 'translateX(50%)',
327
+ },
313
328
  top: THUMB_SIZE / 2 + 4,
314
329
  },
315
330
  markLabelVertical: {
316
331
  transform: 'translateY(50%)',
317
- left: THUMB_SIZE / 2 + 4,
332
+ insetInlineStart: THUMB_SIZE / 2 + 4,
318
333
  },
319
334
  });
320
335
 
@@ -326,12 +341,39 @@ function clamp(val: number, min: number, max: number): number {
326
341
  return Math.min(Math.max(val, min), max);
327
342
  }
328
343
 
344
+ /**
345
+ * Number of decimal places a value carries, including values in exponent
346
+ * notation (e.g. 1e-7 → 7). Used to round away binary floating-point error
347
+ * after step arithmetic.
348
+ */
349
+ function getDecimalPrecision(num: number): number {
350
+ if (Math.abs(num) < 1) {
351
+ const parts = num.toExponential().split('e-');
352
+ if (parts.length === 2) {
353
+ const mantissaDecimals = parts[0].split('.')[1]?.length ?? 0;
354
+ return mantissaDecimals + parseInt(parts[1], 10);
355
+ }
356
+ }
357
+ const decimalPart = String(num).split('.')[1];
358
+ return decimalPart ? decimalPart.length : 0;
359
+ }
360
+
329
361
  function snapToStep(val: number, min: number, step: number): number {
330
362
  if (step <= 0) {
331
363
  return val;
332
364
  }
333
365
  const steps = Math.round((val - min) / step);
334
- return min + steps * step;
366
+ const snapped = min + steps * step;
367
+ // `min + steps * step` accumulates binary floating-point error with
368
+ // fractional steps (0 + 3 * 0.1 → 0.30000000000000004), which leaks into
369
+ // onChange/onChangeEnd payloads, aria-valuenow, and the value tooltip.
370
+ // Snapped values can never carry more decimals than min/step combined, so
371
+ // rounding to that precision removes only the error.
372
+ const precision = Math.min(
373
+ Math.max(getDecimalPrecision(min), getDecimalPrecision(step)),
374
+ 20, // toFixed() throws past 20 digits
375
+ );
376
+ return Number(snapped.toFixed(precision));
335
377
  }
336
378
 
337
379
  function getPercent(val: number, min: number, max: number): number {
@@ -392,6 +434,7 @@ export function Slider({ref, ...props}: SliderProps) {
392
434
  const isHorizontal = orientation === 'horizontal';
393
435
 
394
436
  const id = useId();
437
+ const labelID = useId();
395
438
  const descriptionID = useId();
396
439
  const statusMessageID = useId();
397
440
  const requiredID = useId();
@@ -459,7 +502,12 @@ export function Slider({ref, ...props}: SliderProps) {
459
502
 
460
503
  let percent: number;
461
504
  if (isHorizontal) {
462
- percent = (clientX - rect.left) / rect.width;
505
+ // In RTL the inline-start (value = min) is the right edge, so measure
506
+ // the pointer fraction from the right instead of the left. Detected
507
+ // from the track's computed direction (lazy, only on pointer move).
508
+ percent = isRtlElement(track)
509
+ ? (rect.right - clientX) / rect.width
510
+ : (clientX - rect.left) / rect.width;
463
511
  } else {
464
512
  // Vertical: bottom = min, top = max
465
513
  percent = 1 - (clientY - rect.top) / rect.height;
@@ -692,14 +740,28 @@ export function Slider({ref, ...props}: SliderProps) {
692
740
  const percent = getPercent(val, min, max);
693
741
 
694
742
  const positionStyle = isHorizontal
695
- ? {left: `${percent}%`}
743
+ ? {insetInlineStart: `${percent}%`}
696
744
  : {bottom: `${percent}%`, left: '50%'};
697
745
 
746
+ // In range mode each thumb keeps a short individual name that composes
747
+ // with the group label (announced via the group's aria-labelledby), per
748
+ // the APG multi-thumb slider pattern. In single mode the thumb takes its
749
+ // name from the visible label element via aria-labelledby.
698
750
  const thumbLabel = isRange
699
751
  ? thumbIndex === 0
700
- ? `${label}, minimum value`
701
- : `${label}, maximum value`
702
- : label;
752
+ ? 'Minimum value'
753
+ : 'Maximum value'
754
+ : undefined;
755
+
756
+ // ARIA bounds must agree with the movement clamping in updateValue: in
757
+ // range mode a thumb can't cross its sibling (minus the
758
+ // minStepsBetweenThumbs gap), and the result is always clamped to
759
+ // [min, max].
760
+ const minGap = minStepsBetweenThumbs * step;
761
+ const ariaValueMin =
762
+ isRange && thumbIndex === 1 ? clamp(values[0] + minGap, min, max) : min;
763
+ const ariaValueMax =
764
+ isRange && thumbIndex === 0 ? clamp(values[1] - minGap, min, max) : max;
703
765
 
704
766
  // Suppress the per-thumb value bubble while the disabled-message tooltip is
705
767
  // showing, so a disabled slider surfaces the *reason* on hover/focus rather
@@ -716,14 +778,15 @@ export function Slider({ref, ...props}: SliderProps) {
716
778
  // focus-discoverable; value changes stay blocked by the isDisabled
717
779
  // guards in the pointer/keyboard handlers.
718
780
  tabIndex={isDisabled && !showsDisabledMessage ? -1 : 0}
719
- aria-valuemin={min}
720
- aria-valuemax={max}
781
+ aria-valuemin={ariaValueMin}
782
+ aria-valuemax={ariaValueMax}
721
783
  aria-valuenow={val}
722
784
  aria-valuetext={formatValue ? formatValue(val) : undefined}
723
785
  aria-orientation={orientation}
724
786
  aria-disabled={isDisabled || undefined}
725
787
  aria-invalid={status?.type === 'error' ? true : undefined}
726
788
  aria-label={thumbLabel}
789
+ aria-labelledby={!isRange ? labelID : undefined}
727
790
  aria-describedby={ariaDescribedBy}
728
791
  onKeyDown={e => handleKeyDown(thumbIndex, e)}
729
792
  {...mergeProps(
@@ -768,13 +831,13 @@ export function Slider({ref, ...props}: SliderProps) {
768
831
  const p0 = getPercent(v0, min, max);
769
832
  const p1 = getPercent(v1, min, max);
770
833
  if (isHorizontal) {
771
- return {left: `${p0}%`, width: `${p1 - p0}%`};
834
+ return {insetInlineStart: `${p0}%`, width: `${p1 - p0}%`};
772
835
  }
773
836
  return {bottom: `${p0}%`, height: `${p1 - p0}%`};
774
837
  }
775
838
  const p = getPercent(values[0], min, max);
776
839
  if (isHorizontal) {
777
- return {left: '0%', width: `${p}%`};
840
+ return {insetInlineStart: '0%', width: `${p}%`};
778
841
  }
779
842
  return {bottom: '0%', height: `${p}%`};
780
843
  })();
@@ -796,6 +859,14 @@ export function Slider({ref, ...props}: SliderProps) {
796
859
  isLabelHidden={isLabelHidden}
797
860
  description={description}
798
861
  inputID={id}
862
+ labelID={labelID}
863
+ // The thumb is a div[role="slider"], which a <label htmlFor> can't
864
+ // name (only form-associated elements are labelable). Render the label
865
+ // as a group label and associate it via aria-labelledby instead: the
866
+ // single thumb references it directly, and in range mode the
867
+ // role="group" container references it while each thumb keeps its own
868
+ // "Minimum value"/"Maximum value" name.
869
+ isGroupLabel
799
870
  descriptionID={description ? descriptionID : undefined}
800
871
  isOptional={isOptional}
801
872
  isRequired={isRequired}
@@ -838,7 +909,9 @@ export function Slider({ref, ...props}: SliderProps) {
838
909
  ))}
839
910
  <div
840
911
  ref={mergeRefs(ref, trackRef, disabledMessageTooltip.ref)}
841
- {...(isRange ? {role: 'group', 'aria-label': label} : undefined)}
912
+ {...(isRange
913
+ ? {role: 'group', 'aria-labelledby': labelID}
914
+ : undefined)}
842
915
  onPointerDown={handlePointerDown}
843
916
  onPointerMove={handlePointerMove}
844
917
  onPointerUp={handlePointerUp}
@@ -889,7 +962,7 @@ export function Slider({ref, ...props}: SliderProps) {
889
962
  {marks.map(mark => {
890
963
  const percent = getPercent(mark.value, min, max);
891
964
  const markPos = isHorizontal
892
- ? {left: `${percent}%`}
965
+ ? {insetInlineStart: `${percent}%`}
893
966
  : {bottom: `${percent}%`};
894
967
  return (
895
968
  <div key={mark.value}>
@@ -64,12 +64,16 @@ describe('Spinner', () => {
64
64
  );
65
65
  });
66
66
 
67
- it('uses string label as aria-label automatically', () => {
67
+ it('names the status element from the visible string label', () => {
68
68
  render(<Spinner label="Fetching data" data-testid="spinner" />);
69
- expect(screen.getByRole('status')).toHaveAttribute(
70
- 'aria-label',
71
- 'Fetching data',
72
- );
69
+ expect(screen.getByRole('status')).toHaveAccessibleName('Fetching data');
70
+ });
71
+
72
+ it('does not duplicate a visible string label as aria-label', () => {
73
+ render(<Spinner label="Fetching data" data-testid="spinner" />);
74
+ const status = screen.getByRole('status');
75
+ expect(status).not.toHaveAttribute('aria-label');
76
+ expect(status).toHaveAttribute('aria-labelledby');
73
77
  });
74
78
 
75
79
  it('uses explicit aria-label over string label', () => {
@@ -103,9 +107,7 @@ describe('Spinner', () => {
103
107
  });
104
108
 
105
109
  it('defaults aria-label to "Loading" for ReactNode label without explicit aria-label', () => {
106
- render(
107
- <Spinner label={<span>Rich content</span>} data-testid="spinner" />,
108
- );
110
+ render(<Spinner label={<span>Rich content</span>} data-testid="spinner" />);
109
111
  expect(screen.getByRole('status')).toHaveAttribute('aria-label', 'Loading');
110
112
  });
111
113
 
@@ -16,7 +16,7 @@
16
16
  * - /packages/cli/templates/blocks/components/Spinner/ (showcase blocks)
17
17
  */
18
18
 
19
- import {useEffect, useRef, type ReactNode} from 'react';
19
+ import {useEffect, useId, useRef, type ReactNode} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import {durationVars, spacingVars} from '../theme/tokens.stylex';
22
22
  import {useTheme} from '../theme/useTheme';
@@ -119,8 +119,9 @@ export interface SpinnerProps extends BaseProps<HTMLSpanElement> {
119
119
  * Visible content displayed below the spinner.
120
120
  * Accepts a string or ReactNode for rich content.
121
121
  *
122
- * When `label` is a string, it is also used as the accessible name
123
- * (aria-label) unless `aria-label` is explicitly set.
122
+ * When `label` is a string, the visible text also provides the accessible
123
+ * name of the status element (via aria-labelledby, avoiding a duplicate
124
+ * announcement) unless `aria-label` is explicitly set.
124
125
  *
125
126
  * @example
126
127
  * ```
@@ -255,6 +256,14 @@ export function Spinner({
255
256
  const {border, diameter} = SIZES[size];
256
257
  const frameSize = diameter + border * 2;
257
258
  const hasLabel = label != null;
259
+ const labelId = useId();
260
+
261
+ // When a visible string label renders (and no explicit aria-label is set),
262
+ // name the status element from the visible Text via aria-labelledby instead
263
+ // of duplicating the same string as aria-label — the duplicate would be
264
+ // announced twice by screen readers (WCAG 4.1.2).
265
+ const namedByVisibleLabel =
266
+ hasLabel && typeof label === 'string' && ariaLabel == null;
258
267
 
259
268
  // Resolve accessible name: explicit aria-label > string label > "Loading"
260
269
  const resolvedAriaLabel =
@@ -264,7 +273,8 @@ export function Spinner({
264
273
  <span
265
274
  ref={hasLabel ? undefined : ref}
266
275
  role="status"
267
- aria-label={resolvedAriaLabel}
276
+ aria-label={namedByVisibleLabel ? undefined : resolvedAriaLabel}
277
+ aria-labelledby={namedByVisibleLabel ? labelId : undefined}
268
278
  data-testid={hasLabel ? undefined : testId}
269
279
  {...(hasLabel ? {} : restProps)}
270
280
  {...mergeProps(
@@ -294,7 +304,7 @@ export function Spinner({
294
304
  )}>
295
305
  {spinner}
296
306
  {typeof label === 'string' ? (
297
- <Text type="body" weight="bold">
307
+ <Text id={labelId} type="body" weight="bold">
298
308
  {label}
299
309
  </Text>
300
310
  ) : (
@@ -6,7 +6,14 @@ export const docs = {
6
6
  name: 'Switch',
7
7
  displayName: 'Switch',
8
8
  category: 'Data Input',
9
- keywords: ["switch","toggle","onoff","flipswitch","boolean","toggleswitch"],
9
+ keywords: [
10
+ 'switch',
11
+ 'toggle',
12
+ 'onoff',
13
+ 'flipswitch',
14
+ 'boolean',
15
+ 'toggleswitch',
16
+ ],
10
17
  props: [
11
18
  {
12
19
  name: 'ref',
@@ -62,6 +69,13 @@ export const docs = {
62
69
  description: 'Whether the switch is disabled.',
63
70
  default: 'false',
64
71
  },
72
+ {
73
+ name: 'size',
74
+ type: "'sm' | 'md'",
75
+ description:
76
+ 'Size variant controlling track and thumb dimensions. sm (32x20px) matches sm checkbox/radio vertical rhythm; md (40x24px, default) matches md checkbox/radio vertical rhythm.',
77
+ default: "'md'",
78
+ },
65
79
  {
66
80
  name: 'htmlName',
67
81
  type: 'string',
@@ -107,7 +121,8 @@ export const docs = {
107
121
  {
108
122
  name: 'labelIcon',
109
123
  type: 'IconType',
110
- description: 'Icon displayed before the label text. See `astryx docs icons` for valid semantic names.',
124
+ description:
125
+ 'Icon displayed before the label text. See `astryx docs icons` for valid semantic names.',
111
126
  },
112
127
  {
113
128
  name: 'labelTooltip',
@@ -129,23 +144,60 @@ export const docs = {
129
144
  'Spacing behavior between label and switch. "hug" places them next to each other; "spread" pushes them to opposite ends of the container (full width). "default" is a deprecated alias for "hug".',
130
145
  default: "'hug'",
131
146
  },
147
+ {
148
+ name: 'width',
149
+ type: 'SizeValue',
150
+ description:
151
+ 'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
152
+ },
132
153
  ],
133
154
  theming: {
134
155
  targets: [
135
- {className: 'astryx-switch', states: ['checked', 'disabled']},
136
- {className: 'astryx-switch-thumb', states: ['checked']},
137
- {className: 'astryx-switch-field', visualProps: ['labelPosition', 'labelSpacing']},
156
+ {
157
+ className: 'astryx-switch',
158
+ visualProps: ['size'],
159
+ states: ['checked', 'disabled'],
160
+ },
161
+ {
162
+ className: 'astryx-switch-thumb',
163
+ visualProps: ['size'],
164
+ states: ['checked'],
165
+ },
166
+ {
167
+ className: 'astryx-switch-field',
168
+ visualProps: ['labelPosition', 'labelSpacing'],
169
+ },
138
170
  ],
139
171
  },
140
172
  usage: {
141
173
  description:
142
174
  'A toggle control for on/off states that take effect immediately. Supports labels, descriptions, loading states, and validation. Use it for settings or preferences that apply instantly. For changes requiring a form submission, use a checkbox instead.',
143
175
  bestPractices: [
144
- { guidance: true, description: 'Use for settings that apply immediately; the toggle should take effect without a separate save action.' },
145
- { guidance: true, description: 'Pair with a clear, concise label that describes the setting being controlled.' },
146
- { guidance: false, description: 'Use for options that require a form submission to take effect; use a checkbox instead.' },
147
- { guidance: false, description: 'Use a switch for multi-state values; it\'s strictly on/off.' },
148
- { guidance: false, description: 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
176
+ {
177
+ guidance: true,
178
+ description:
179
+ 'Use for settings that apply immediately; the toggle should take effect without a separate save action.',
180
+ },
181
+ {
182
+ guidance: true,
183
+ description:
184
+ 'Pair with a clear, concise label that describes the setting being controlled.',
185
+ },
186
+ {
187
+ guidance: false,
188
+ description:
189
+ 'Use for options that require a form submission to take effect; use a checkbox instead.',
190
+ },
191
+ {
192
+ guidance: false,
193
+ description:
194
+ "Use a switch for multi-state values; it's strictly on/off.",
195
+ },
196
+ {
197
+ guidance: false,
198
+ description:
199
+ 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
200
+ },
149
201
  ],
150
202
  },
151
203
  };
@@ -163,8 +215,7 @@ export const docsZh = {
163
215
  {
164
216
  name: 'label',
165
217
  type: 'string',
166
- description:
167
- '开关的标签文本(始终渲染以确保无障碍性)。',
218
+ description: '开关的标签文本(始终渲染以确保无障碍性)。',
168
219
  required: true,
169
220
  },
170
221
  {
@@ -187,15 +238,13 @@ export const docsZh = {
187
238
  {
188
239
  name: 'isLoading',
189
240
  type: 'boolean',
190
- description:
191
- '开关是否处于加载状态,在滑块内显示旋转器。',
241
+ description: '开关是否处于加载状态,在滑块内显示旋转器。',
192
242
  default: 'false',
193
243
  },
194
244
  {
195
245
  name: 'isLabelHidden',
196
246
  type: 'boolean',
197
- description:
198
- '视觉上隐藏标签,同时保持屏幕阅读器的无障碍性。',
247
+ description: '视觉上隐藏标签,同时保持屏幕阅读器的无障碍性。',
199
248
  default: 'false',
200
249
  },
201
250
  {
@@ -212,7 +261,8 @@ export const docsZh = {
212
261
  {
213
262
  name: 'htmlName',
214
263
  type: 'string',
215
- description: '底层复选框输入的 HTML name 属性,用于表单提交(开启时提交 "on")。',
264
+ description:
265
+ '底层复选框输入的 HTML name 属性,用于表单提交(开启时提交 "on")。',
216
266
  },
217
267
  {
218
268
  name: 'disabledMessage',
@@ -223,15 +273,13 @@ export const docsZh = {
223
273
  {
224
274
  name: 'isOptional',
225
275
  type: 'boolean',
226
- description:
227
- '字段是否为可选。与 isRequired 互斥。',
276
+ description: '字段是否为可选。与 isRequired 互斥。',
228
277
  default: 'false',
229
278
  },
230
279
  {
231
280
  name: 'isRequired',
232
281
  type: 'boolean',
233
- description:
234
- '开关是否为必填。与 isOptional 互斥。',
282
+ description: '开关是否为必填。与 isOptional 互斥。',
235
283
  default: 'false',
236
284
  },
237
285
  {
@@ -258,14 +306,12 @@ export const docsZh = {
258
306
  {
259
307
  name: 'labelTooltip',
260
308
  type: 'string',
261
- description:
262
- '在标签末尾的信息图标中显示的工具提示文本。',
309
+ description: '在标签末尾的信息图标中显示的工具提示文本。',
263
310
  },
264
311
  {
265
312
  name: 'labelPosition',
266
313
  type: "'start' | 'end'",
267
- description:
268
- '标签出现在开关的哪一侧。"start" 将标签放在开关前面。',
314
+ description: '标签出现在开关的哪一侧。"start" 将标签放在开关前面。',
269
315
  default: "'end'",
270
316
  },
271
317
  {
@@ -278,20 +324,51 @@ export const docsZh = {
278
324
  ],
279
325
  theming: {
280
326
  targets: [
281
- {className: 'astryx-switch', states: ['checked', 'disabled']},
282
- {className: 'astryx-switch-thumb', states: ['checked']},
283
- {className: 'astryx-switch-field', visualProps: ['labelPosition', 'labelSpacing']},
327
+ {
328
+ className: 'astryx-switch',
329
+ visualProps: ['size'],
330
+ states: ['checked', 'disabled'],
331
+ },
332
+ {
333
+ className: 'astryx-switch-thumb',
334
+ visualProps: ['size'],
335
+ states: ['checked'],
336
+ },
337
+ {
338
+ className: 'astryx-switch-field',
339
+ visualProps: ['labelPosition', 'labelSpacing'],
340
+ },
284
341
  ],
285
342
  },
286
343
  usage: {
287
344
  description:
288
345
  'A toggle control for on/off states that take effect immediately. Supports labels, descriptions, loading states, and validation. Use it for settings or preferences that apply instantly. For changes requiring a form submission, use a checkbox instead.',
289
346
  bestPractices: [
290
- { guidance: true, description: 'Use for settings that apply immediately; the toggle should take effect without a separate save action.' },
291
- { guidance: true, description: 'Pair with a clear, concise label that describes the setting being controlled.' },
292
- { guidance: false, description: 'Use for options that require a form submission to take effect; use a checkbox instead.' },
293
- { guidance: false, description: 'Use a switch for multi-state values; it\'s strictly on/off.' },
294
- { guidance: false, description: 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
347
+ {
348
+ guidance: true,
349
+ description:
350
+ 'Use for settings that apply immediately; the toggle should take effect without a separate save action.',
351
+ },
352
+ {
353
+ guidance: true,
354
+ description:
355
+ 'Pair with a clear, concise label that describes the setting being controlled.',
356
+ },
357
+ {
358
+ guidance: false,
359
+ description:
360
+ 'Use for options that require a form submission to take effect; use a checkbox instead.',
361
+ },
362
+ {
363
+ guidance: false,
364
+ description:
365
+ "Use a switch for multi-state values; it's strictly on/off.",
366
+ },
367
+ {
368
+ guidance: false,
369
+ description:
370
+ 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
371
+ },
295
372
  ],
296
373
  },
297
374
  };
@@ -303,11 +380,31 @@ export const docsDense = {
303
380
  description:
304
381
  'A toggle control for on/off states that take effect immediately. Supports labels, descriptions, loading states, and validation. Use it for settings or preferences that apply instantly. For changes requiring a form submission, use a checkbox instead.',
305
382
  bestPractices: [
306
- { guidance: true, description: 'Use for settings that apply immediately; the toggle should take effect without a separate save action.' },
307
- { guidance: true, description: 'Pair with a clear, concise label that describes the setting being controlled.' },
308
- { guidance: false, description: 'Use for options that require a form submission to take effect; use a checkbox instead.' },
309
- { guidance: false, description: 'Use a switch for multi-state values; it\'s strictly on/off.' },
310
- { guidance: false, description: 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.' },
383
+ {
384
+ guidance: true,
385
+ description:
386
+ 'Use for settings that apply immediately; the toggle should take effect without a separate save action.',
387
+ },
388
+ {
389
+ guidance: true,
390
+ description:
391
+ 'Pair with a clear, concise label that describes the setting being controlled.',
392
+ },
393
+ {
394
+ guidance: false,
395
+ description:
396
+ 'Use for options that require a form submission to take effect; use a checkbox instead.',
397
+ },
398
+ {
399
+ guidance: false,
400
+ description:
401
+ "Use a switch for multi-state values; it's strictly on/off.",
402
+ },
403
+ {
404
+ guidance: false,
405
+ description:
406
+ 'Wrap a disabled switch in Tooltip to explain why it is disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
407
+ },
311
408
  ],
312
409
  },
313
410
  propDescriptions: {
@@ -315,7 +412,8 @@ export const docsDense = {
315
412
  label: 'Label text (always rendered for a11y).',
316
413
  value: 'Whether switch is on or off.',
317
414
  onChange: 'Fired when switch state changes.',
318
- changeAction: 'Async action after onChange; triggers optimistic UI + loading spinner until resolved.',
415
+ changeAction:
416
+ 'Async action after onChange; triggers optimistic UI + loading spinner until resolved.',
319
417
  isLoading: 'Loading state; shows spinner in thumb.',
320
418
  isLabelHidden: 'Visually hides label; still accessible to screen readers.',
321
419
  description: 'Description text below label.',
@@ -323,12 +421,14 @@ export const docsDense = {
323
421
  htmlName: 'HTML name attr for the checkbox; submits "on" when on.',
324
422
  isOptional: 'Whether field is optional; mutually exclusive w/ isRequired.',
325
423
  isRequired: 'Whether switch is required; mutually exclusive w/ isOptional.',
326
- status: 'Status indicator w/ type + message; colored message box, sets aria-invalid on error.',
424
+ status:
425
+ 'Status indicator w/ type + message; colored message box, sets aria-invalid on error.',
327
426
  onFocus: 'Fired when switch receives focus.',
328
427
  onBlur: 'Fired when switch loses focus.',
329
428
  labelIcon: 'Icon before label text.',
330
429
  labelTooltip: 'Tooltip text in info icon at label end.',
331
430
  labelPosition: 'Which side label appears; "start" places before switch.',
332
- labelSpacing: 'Spacing behavior; "hug" places next to each other, "spread" pushes to opposite ends (full width). "default" is deprecated alias for "hug".',
431
+ labelSpacing:
432
+ 'Spacing behavior; "hug" places next to each other, "spread" pushes to opposite ends (full width). "default" is deprecated alias for "hug".',
333
433
  },
334
- };
434
+ };