@astryxdesign/core 0.4.5 → 0.4.6-canary.f1d4034

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 (531) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/dist/Avatar/Avatar.d.ts +10 -5
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +139 -30
  5. package/dist/Avatar/AvatarStatusDot.d.ts +2 -1
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/Avatar/AvatarStatusDot.js +30 -4
  8. package/dist/Avatar/AvatarStatusLabelContext.d.ts +39 -0
  9. package/dist/Avatar/AvatarStatusLabelContext.d.ts.map +1 -0
  10. package/dist/Avatar/AvatarStatusLabelContext.js +41 -0
  11. package/dist/AvatarGroup/AvatarGroup.d.ts +3 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +15 -9
  15. package/dist/Blockquote/Blockquote.d.ts +4 -3
  16. package/dist/Blockquote/Blockquote.d.ts.map +1 -1
  17. package/dist/Blockquote/Blockquote.js +9 -10
  18. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  19. package/dist/BottomSheet/BottomSheetPanel.js +21 -5
  20. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/BreadcrumbItem.js +47 -34
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +7 -7
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +21 -12
  26. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  27. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  28. package/dist/Calendar/styles.d.ts +2 -2
  29. package/dist/Calendar/styles.d.ts.map +1 -1
  30. package/dist/Calendar/styles.js +5 -5
  31. package/dist/Center/Center.d.ts +23 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +12 -6
  34. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  35. package/dist/Chat/ChatComposer.js +3 -3
  36. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposerDrawer.js +2 -2
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +2 -2
  40. package/dist/Chat/useChatDictation.d.ts +1 -1
  41. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  42. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  43. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  44. package/dist/Chat/useSpeechRecognition.js +4 -3
  45. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  46. package/dist/Chat/useTriggerMenu.js +2 -2
  47. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  48. package/dist/CheckboxInput/CheckboxInput.js +2 -2
  49. package/dist/Citation/Citation.d.ts.map +1 -1
  50. package/dist/Citation/Citation.js +5 -5
  51. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  52. package/dist/ClickableCard/ClickableCard.js +4 -4
  53. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  54. package/dist/CodeBlock/CodeBlock.js +1 -1
  55. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  56. package/dist/Collapsible/Collapsible.js +2 -2
  57. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  58. package/dist/CommandPalette/CommandPaletteItem.js +3 -3
  59. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  60. package/dist/ComplexSelector/ComplexSelector.js +6 -6
  61. package/dist/DateInput/DateInput.d.ts +31 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +90 -22
  64. package/dist/DateInput/MonthScroller.d.ts +35 -0
  65. package/dist/DateInput/MonthScroller.d.ts.map +1 -0
  66. package/dist/DateInput/MonthScroller.js +658 -0
  67. package/dist/DateInput/MonthYearWheels.d.ts +24 -0
  68. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
  69. package/dist/DateInput/MonthYearWheels.js +114 -0
  70. package/dist/DateInput/TouchDateField.d.ts +10 -0
  71. package/dist/DateInput/TouchDateField.d.ts.map +1 -0
  72. package/dist/DateInput/TouchDateField.js +810 -0
  73. package/dist/DateInput/Wheel.d.ts +41 -0
  74. package/dist/DateInput/Wheel.d.ts.map +1 -0
  75. package/dist/DateInput/Wheel.js +315 -0
  76. package/dist/DateInput/monthGeometry.d.ts +65 -0
  77. package/dist/DateInput/monthGeometry.d.ts.map +1 -0
  78. package/dist/DateInput/monthGeometry.js +98 -0
  79. package/dist/DateInput/tokens.stylex.d.ts +63 -0
  80. package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
  81. package/dist/DateInput/tokens.stylex.js +74 -0
  82. package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
  83. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
  84. package/dist/DateInput/useOwnScrollGesture.js +202 -0
  85. package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
  86. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
  87. package/dist/DateInput/usePointerDragScroll.js +216 -0
  88. package/dist/DateInput/useScrollSettle.d.ts +26 -0
  89. package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
  90. package/dist/DateInput/useScrollSettle.js +149 -0
  91. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  92. package/dist/DateRangeInput/DateRangeInput.js +17 -14
  93. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  94. package/dist/DateTimeInput/DateTimeInput.js +17 -14
  95. package/dist/Dialog/Dialog.d.ts.map +1 -1
  96. package/dist/Dialog/Dialog.js +3 -3
  97. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  98. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +2 -2
  99. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  100. package/dist/DropdownMenu/DropdownMenuItem.js +2 -2
  101. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  102. package/dist/DropdownMenu/DropdownMenuRadioItem.js +2 -2
  103. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  104. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -2
  105. package/dist/Field/FieldLabel.d.ts.map +1 -1
  106. package/dist/Field/FieldLabel.js +4 -4
  107. package/dist/Field/InputClearButton.d.ts.map +1 -1
  108. package/dist/Field/InputClearButton.js +6 -0
  109. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  110. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  111. package/dist/Field/inputStyles.stylex.js +5 -5
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +6 -6
  114. package/dist/HoverCard/HoverCard.d.ts +13 -3
  115. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  116. package/dist/HoverCard/HoverCard.js +2 -0
  117. package/dist/HoverCard/index.d.ts +1 -1
  118. package/dist/HoverCard/index.d.ts.map +1 -1
  119. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  120. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/useHoverCard.js +78 -9
  122. package/dist/InputGroup/InputGroup.js +1 -1
  123. package/dist/Item/Item.d.ts +9 -1
  124. package/dist/Item/Item.d.ts.map +1 -1
  125. package/dist/Item/Item.js +52 -14
  126. package/dist/Layer/index.d.ts +4 -2
  127. package/dist/Layer/index.d.ts.map +1 -1
  128. package/dist/Layer/index.js +2 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +3 -2
  131. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  132. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  133. package/dist/Layer/useTouchTrigger.js +241 -0
  134. package/dist/Layout/container.stylex.d.ts +4 -2
  135. package/dist/Layout/container.stylex.d.ts.map +1 -1
  136. package/dist/Layout/container.stylex.js +19 -11
  137. package/dist/Layout/index.d.ts +1 -0
  138. package/dist/Layout/index.d.ts.map +1 -1
  139. package/dist/Layout/index.js +4 -0
  140. package/dist/Layout/padding.stylex.d.ts +315 -12
  141. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  142. package/dist/Layout/padding.stylex.js +389 -12
  143. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  144. package/dist/Lightbox/Lightbox.js +5 -4
  145. package/dist/Link/Link.d.ts.map +1 -1
  146. package/dist/Link/Link.js +6 -6
  147. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  148. package/dist/MetadataList/MetadataList.js +1 -1
  149. package/dist/MobileNav/MobileNav.d.ts +1 -0
  150. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  151. package/dist/MobileNav/MobileNav.js +24 -6
  152. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  153. package/dist/MultiSelector/MultiSelector.js +8 -8
  154. package/dist/NavItem/navItemStyles.stylex.d.ts +4 -4
  155. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  156. package/dist/NavItem/navItemStyles.stylex.js +4 -4
  157. package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
  158. package/dist/NavMenu/NavHeadingMenuItem.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +36 -8
  161. package/dist/Outline/Outline.d.ts.map +1 -1
  162. package/dist/Outline/Outline.js +3 -3
  163. package/dist/Pagination/Pagination.d.ts.map +1 -1
  164. package/dist/Pagination/Pagination.js +2 -2
  165. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  166. package/dist/PowerSearch/PowerSearch.js +11 -12
  167. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  169. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  170. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  171. package/dist/PowerSearch/formatFilterValue.js +23 -8
  172. package/dist/RadioList/RadioListItem.d.ts +2 -2
  173. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  174. package/dist/RadioList/RadioListItem.js +70 -40
  175. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  176. package/dist/Resizable/ResizeHandle.js +7 -7
  177. package/dist/Section/Section.d.ts +36 -1
  178. package/dist/Section/Section.d.ts.map +1 -1
  179. package/dist/Section/Section.js +12 -2
  180. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  181. package/dist/SegmentedControl/SegmentedControlItem.js +7 -2
  182. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  183. package/dist/SelectableCard/SelectableCard.js +3 -3
  184. package/dist/Selector/Selector.d.ts +26 -1
  185. package/dist/Selector/Selector.d.ts.map +1 -1
  186. package/dist/Selector/Selector.js +93 -20
  187. package/dist/Selector/SelectorOption.d.ts +14 -1
  188. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  189. package/dist/Selector/SelectorOption.js +6 -0
  190. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  191. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  192. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  193. package/dist/Selector/types.d.ts +1 -0
  194. package/dist/Selector/types.d.ts.map +1 -1
  195. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  196. package/dist/SideNav/SideNavHeading.js +4 -4
  197. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  198. package/dist/SideNav/SideNavItem.js +3 -3
  199. package/dist/Slider/Slider.d.ts.map +1 -1
  200. package/dist/Slider/Slider.js +7 -7
  201. package/dist/Stack/Stack.d.ts +23 -1
  202. package/dist/Stack/Stack.d.ts.map +1 -1
  203. package/dist/Stack/Stack.js +12 -6
  204. package/dist/Switch/Switch.d.ts.map +1 -1
  205. package/dist/Switch/Switch.js +2 -2
  206. package/dist/TabList/Tab.d.ts.map +1 -1
  207. package/dist/TabList/Tab.js +1 -1
  208. package/dist/TabList/TabMenu.d.ts.map +1 -1
  209. package/dist/TabList/TabMenu.js +3 -3
  210. package/dist/Table/TableRow.js +4 -4
  211. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  212. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  213. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  214. package/dist/Table/plugins/filtering/useTableFiltering.js +2 -2
  215. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  216. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +2 -2
  217. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  218. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -2
  219. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  220. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  221. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  222. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  223. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  224. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  225. package/dist/Table/plugins/tree/useTableTreeData.js +3 -3
  226. package/dist/TextArea/TextArea.js +1 -1
  227. package/dist/TextInput/TextInput.js +1 -1
  228. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  229. package/dist/Thumbnail/Thumbnail.js +3 -3
  230. package/dist/TimeInput/TimeInput.js +2 -2
  231. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  232. package/dist/Timestamp/Timestamp.js +6 -4
  233. package/dist/Timestamp/formatInstant.d.ts +3 -2
  234. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  235. package/dist/Timestamp/formatInstant.js +8 -8
  236. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  237. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  238. package/dist/Timestamp/tooltipEntries.js +5 -3
  239. package/dist/Toast/Toast.d.ts +4 -3
  240. package/dist/Toast/Toast.d.ts.map +1 -1
  241. package/dist/Toast/Toast.js +10 -7
  242. package/dist/Token/Token.d.ts.map +1 -1
  243. package/dist/Token/Token.js +6 -6
  244. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  245. package/dist/Tokenizer/Tokenizer.js +1 -1
  246. package/dist/Tooltip/Tooltip.d.ts +16 -3
  247. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  248. package/dist/Tooltip/Tooltip.js +2 -0
  249. package/dist/Tooltip/index.d.ts +1 -1
  250. package/dist/Tooltip/index.d.ts.map +1 -1
  251. package/dist/Tooltip/useTooltip.d.ts +21 -2
  252. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  253. package/dist/Tooltip/useTooltip.js +71 -13
  254. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  255. package/dist/TopNav/TopNavHeading.js +4 -4
  256. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavItem.js +3 -3
  258. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  259. package/dist/TopNav/TopNavMegaMenu.js +2 -2
  260. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  261. package/dist/TopNav/TopNavMegaMenuItem.js +2 -2
  262. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  263. package/dist/TopNav/TopNavMenu.js +4 -4
  264. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  265. package/dist/TreeList/TreeListItem.js +7 -7
  266. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  267. package/dist/Typeahead/BaseTypeahead.js +2 -2
  268. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  269. package/dist/Typeahead/Typeahead.js +1 -1
  270. package/dist/astryx.css +124 -62
  271. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  272. package/dist/hooks/containerReveal.stylex.js +18 -18
  273. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  274. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  275. package/dist/hooks/scrollbarGutter.js +103 -0
  276. package/dist/hooks/useAutoMediaMode.d.ts +31 -0
  277. package/dist/hooks/useAutoMediaMode.d.ts.map +1 -0
  278. package/dist/hooks/useAutoMediaMode.js +201 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  280. package/dist/hooks/useInputStatusIcon.js +1 -1
  281. package/dist/hooks/useScrollLock.d.ts +4 -0
  282. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  283. package/dist/hooks/useScrollLock.js +13 -1
  284. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  285. package/dist/i18n/InternationalizationContext.js +2 -0
  286. package/dist/i18n/index.d.ts +6 -0
  287. package/dist/i18n/index.d.ts.map +1 -1
  288. package/dist/i18n/index.js +6 -0
  289. package/dist/i18n/useCollator.d.ts +12 -0
  290. package/dist/i18n/useCollator.d.ts.map +1 -0
  291. package/dist/i18n/useCollator.js +40 -0
  292. package/dist/i18n/useLocale.d.ts +3 -0
  293. package/dist/i18n/useLocale.d.ts.map +1 -0
  294. package/dist/i18n/useLocale.js +31 -0
  295. package/dist/theme/MediaTheme.d.ts +25 -5
  296. package/dist/theme/MediaTheme.d.ts.map +1 -1
  297. package/dist/theme/MediaTheme.js +31 -6
  298. package/dist/theme/defineTheme.d.ts +5 -0
  299. package/dist/theme/defineTheme.d.ts.map +1 -1
  300. package/dist/theme/defineTheme.js +5 -0
  301. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  302. package/dist/theme/derivedVarRegistry.js +7 -0
  303. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  304. package/dist/theme/generateThemeRules.js +66 -5
  305. package/dist/theme/index.d.ts +1 -1
  306. package/dist/theme/index.d.ts.map +1 -1
  307. package/dist/theme/syntax/index.d.ts +6 -0
  308. package/dist/theme/syntax/index.d.ts.map +1 -1
  309. package/dist/theme/syntax/index.js +7 -2
  310. package/dist/utils/dateParser.d.ts +4 -3
  311. package/dist/utils/dateParser.d.ts.map +1 -1
  312. package/dist/utils/dateParser.js +8 -8
  313. package/dist/utils/index.d.ts +1 -1
  314. package/dist/utils/index.d.ts.map +1 -1
  315. package/dist/utils/index.js +1 -1
  316. package/dist/utils/plainDate.d.ts +5 -2
  317. package/dist/utils/plainDate.d.ts.map +1 -1
  318. package/dist/utils/plainDate.js +20 -4
  319. package/locales/en.json +28 -0
  320. package/locales/pseudo.json +21 -0
  321. package/package.json +3 -3
  322. package/src/Avatar/Avatar.doc.mjs +10 -6
  323. package/src/Avatar/Avatar.test.tsx +219 -1
  324. package/src/Avatar/Avatar.tsx +179 -37
  325. package/src/Avatar/AvatarStatusDot.tsx +30 -4
  326. package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
  327. package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
  328. package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
  329. package/src/AvatarGroup/AvatarGroup.tsx +3 -1
  330. package/src/AvatarGroup/AvatarGroupOverflow.tsx +31 -8
  331. package/src/Banner/Banner.doc.mjs +2 -0
  332. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  333. package/src/Blockquote/Blockquote.test.tsx +34 -3
  334. package/src/Blockquote/Blockquote.tsx +11 -9
  335. package/src/BottomSheet/BottomSheet.doc.mjs +3 -3
  336. package/src/BottomSheet/BottomSheet.test.tsx +24 -0
  337. package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
  338. package/src/BottomSheet/BottomSheetPanel.tsx +58 -3
  339. package/src/Breadcrumbs/BreadcrumbItem.tsx +36 -23
  340. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
  341. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
  342. package/src/Button/Button.tsx +12 -9
  343. package/src/Calendar/Calendar.tsx +15 -9
  344. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  345. package/src/Calendar/styles.ts +8 -5
  346. package/src/Center/Center.doc.mjs +48 -0
  347. package/src/Center/Center.test.tsx +158 -0
  348. package/src/Center/Center.tsx +50 -9
  349. package/src/Chat/ChatComposer.tsx +11 -5
  350. package/src/Chat/ChatComposerDrawer.tsx +4 -1
  351. package/src/Chat/ChatToolCalls.tsx +13 -4
  352. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  353. package/src/Chat/useChatDictation.ts +1 -1
  354. package/src/Chat/useSpeechRecognition.ts +5 -3
  355. package/src/Chat/useTriggerMenu.tsx +4 -1
  356. package/src/CheckboxInput/CheckboxInput.tsx +5 -2
  357. package/src/Citation/Citation.test.tsx +6 -1
  358. package/src/Citation/Citation.tsx +11 -5
  359. package/src/ClickableCard/ClickableCard.tsx +7 -4
  360. package/src/CodeBlock/CodeBlock.tsx +4 -1
  361. package/src/Collapsible/Collapsible.tsx +5 -2
  362. package/src/CommandPalette/CommandPaletteItem.tsx +6 -3
  363. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  364. package/src/ComplexSelector/ComplexSelector.tsx +14 -7
  365. package/src/DateInput/DateInput.test.tsx +17 -1
  366. package/src/DateInput/DateInput.tsx +93 -21
  367. package/src/DateInput/DateInputTouch.test.tsx +2582 -0
  368. package/src/DateInput/MonthScroller.tsx +878 -0
  369. package/src/DateInput/MonthYearWheels.tsx +147 -0
  370. package/src/DateInput/TouchDateField.tsx +1223 -0
  371. package/src/DateInput/Wheel.tsx +473 -0
  372. package/src/DateInput/monthGeometry.ts +118 -0
  373. package/src/DateInput/tokens.stylex.ts +99 -0
  374. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  375. package/src/DateInput/usePointerDragScroll.ts +220 -0
  376. package/src/DateInput/useScrollSettle.ts +164 -0
  377. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  378. package/src/DateRangeInput/DateRangeInput.tsx +31 -13
  379. package/src/DateTimeInput/DateTimeInput.tsx +25 -9
  380. package/src/Dialog/Dialog.test.tsx +24 -0
  381. package/src/Dialog/Dialog.tsx +3 -0
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
  384. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
  385. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
  386. package/src/Field/Field.doc.mjs +2 -0
  387. package/src/Field/FieldLabel.tsx +9 -3
  388. package/src/Field/InputClearButton.test.tsx +110 -3
  389. package/src/Field/InputClearButton.tsx +38 -0
  390. package/src/Field/inputStyles.stylex.ts +17 -13
  391. package/src/FileInput/FileInput.tsx +15 -8
  392. package/src/FormLayout/FormLayout.doc.mjs +1 -1
  393. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  394. package/src/HoverCard/HoverCard.test.tsx +189 -0
  395. package/src/HoverCard/HoverCard.tsx +22 -2
  396. package/src/HoverCard/index.ts +1 -0
  397. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  398. package/src/HoverCard/useHoverCard.tsx +103 -7
  399. package/src/InputGroup/InputGroup.tsx +1 -1
  400. package/src/Item/Item.doc.mjs +1 -0
  401. package/src/Item/Item.test.tsx +50 -0
  402. package/src/Item/Item.tsx +54 -6
  403. package/src/Layer/index.ts +10 -0
  404. package/src/Layer/useLayer.tsx +13 -2
  405. package/src/Layer/useTouchTrigger.test.ts +70 -0
  406. package/src/Layer/useTouchTrigger.ts +366 -0
  407. package/src/Layout/Layout.doc.mjs +18 -0
  408. package/src/Layout/container.stylex.ts +11 -3
  409. package/src/Layout/index.ts +4 -0
  410. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  411. package/src/Layout/padding.stylex.ts +192 -12
  412. package/src/Lightbox/Lightbox.tsx +11 -3
  413. package/src/Link/Link.tsx +9 -6
  414. package/src/MetadataList/MetadataList.tsx +4 -1
  415. package/src/MobileNav/MobileNav.tsx +61 -5
  416. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  417. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  418. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  419. package/src/MultiSelector/MultiSelector.tsx +26 -10
  420. package/src/NavItem/navItemStyles.stylex.ts +7 -4
  421. package/src/NavMenu/NavHeadingMenuItem.tsx +6 -3
  422. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  423. package/src/NumberInput/NumberInput.test.tsx +138 -0
  424. package/src/NumberInput/NumberInput.tsx +47 -8
  425. package/src/Outline/Outline.tsx +5 -2
  426. package/src/Pagination/Pagination.tsx +5 -2
  427. package/src/PowerSearch/PowerSearch.tsx +17 -8
  428. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  429. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  430. package/src/PowerSearch/formatFilterValue.ts +31 -6
  431. package/src/RadioList/RadioList.doc.mjs +1 -1
  432. package/src/RadioList/RadioList.test.tsx +170 -2
  433. package/src/RadioList/RadioListItem.tsx +88 -53
  434. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  435. package/src/Resizable/ResizeHandle.tsx +25 -7
  436. package/src/Section/Section.doc.mjs +55 -0
  437. package/src/Section/Section.test.tsx +201 -4
  438. package/src/Section/Section.tsx +69 -0
  439. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  440. package/src/SegmentedControl/SegmentedControlItem.tsx +15 -3
  441. package/src/SelectableCard/SelectableCard.tsx +6 -3
  442. package/src/Selector/Selector.doc.mjs +16 -2
  443. package/src/Selector/Selector.test.tsx +525 -1
  444. package/src/Selector/Selector.tsx +180 -25
  445. package/src/Selector/SelectorOption.doc.mjs +7 -0
  446. package/src/Selector/SelectorOption.tsx +21 -0
  447. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  448. package/src/Selector/types.ts +4 -4
  449. package/src/SideNav/SideNavHeading.tsx +14 -5
  450. package/src/SideNav/SideNavItem.tsx +9 -3
  451. package/src/Slider/Slider.tsx +25 -6
  452. package/src/Stack/Stack.doc.mjs +72 -0
  453. package/src/Stack/Stack.test.tsx +156 -0
  454. package/src/Stack/Stack.tsx +50 -8
  455. package/src/Switch/Switch.tsx +5 -2
  456. package/src/TabList/Tab.tsx +4 -1
  457. package/src/TabList/TabMenu.tsx +9 -3
  458. package/src/Table/TableRow.tsx +4 -4
  459. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +6 -2
  460. package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
  461. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +10 -3
  462. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +6 -3
  463. package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
  464. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  465. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  466. package/src/Table/plugins/tree/useTableTreeData.tsx +10 -4
  467. package/src/TextArea/TextArea.doc.mjs +1 -1
  468. package/src/TextArea/TextArea.tsx +1 -1
  469. package/src/TextInput/TextInput.tsx +1 -1
  470. package/src/Thumbnail/Thumbnail.tsx +9 -3
  471. package/src/TimeInput/TimeInput.tsx +1 -1
  472. package/src/Timestamp/Timestamp.test.tsx +27 -7
  473. package/src/Timestamp/Timestamp.tsx +8 -4
  474. package/src/Timestamp/formatInstant.ts +9 -7
  475. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  476. package/src/Timestamp/tooltipEntries.ts +6 -2
  477. package/src/Toast/Toast.tsx +9 -7
  478. package/src/Token/Token.tsx +14 -5
  479. package/src/Tokenizer/Tokenizer.tsx +4 -1
  480. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  481. package/src/Tooltip/Tooltip.test.tsx +254 -1
  482. package/src/Tooltip/Tooltip.tsx +22 -2
  483. package/src/Tooltip/index.ts +1 -0
  484. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  485. package/src/Tooltip/useTooltip.tsx +99 -21
  486. package/src/TopNav/TopNavHeading.tsx +13 -4
  487. package/src/TopNav/TopNavItem.tsx +6 -3
  488. package/src/TopNav/TopNavMegaMenu.tsx +5 -2
  489. package/src/TopNav/TopNavMegaMenuItem.tsx +5 -2
  490. package/src/TopNav/TopNavMenu.tsx +10 -4
  491. package/src/TreeList/TreeListItem.tsx +22 -7
  492. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  493. package/src/Typeahead/Typeahead.tsx +4 -1
  494. package/src/hooks/containerReveal.stylex.ts +54 -18
  495. package/src/hooks/scrollbarGutter.test.ts +167 -0
  496. package/src/hooks/scrollbarGutter.ts +121 -0
  497. package/src/hooks/useAutoMediaMode.test.ts +109 -0
  498. package/src/hooks/useAutoMediaMode.ts +233 -0
  499. package/src/hooks/useInputStatusIcon.tsx +4 -1
  500. package/src/hooks/useScrollLock.doc.mjs +4 -4
  501. package/src/hooks/useScrollLock.test.ts +73 -0
  502. package/src/hooks/useScrollLock.ts +14 -0
  503. package/src/i18n/InternationalizationContext.ts +2 -0
  504. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  505. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  506. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  507. package/src/i18n/index.ts +6 -0
  508. package/src/i18n/useCollator.doc.mjs +84 -0
  509. package/src/i18n/useCollator.ts +44 -0
  510. package/src/i18n/useLocale.doc.mjs +70 -0
  511. package/src/i18n/useLocale.ts +34 -0
  512. package/src/i18n/useTranslator.doc.mjs +1 -1
  513. package/src/reset.css +9 -2
  514. package/src/reset.test.ts +22 -0
  515. package/src/theme/MediaTheme.doc.mjs +23 -4
  516. package/src/theme/MediaTheme.dom.test.tsx +97 -0
  517. package/src/theme/MediaTheme.tsx +42 -7
  518. package/src/theme/defineTheme.test.ts +9 -3
  519. package/src/theme/defineTheme.ts +5 -0
  520. package/src/theme/derivedVarRegistry.test.ts +19 -5
  521. package/src/theme/derivedVarRegistry.ts +4 -0
  522. package/src/theme/generateThemeRules.test.ts +118 -0
  523. package/src/theme/generateThemeRules.ts +83 -5
  524. package/src/theme/index.ts +1 -1
  525. package/src/theme/syntax/index.ts +6 -2
  526. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  527. package/src/utils/dateParser.test.ts +20 -0
  528. package/src/utils/dateParser.ts +14 -7
  529. package/src/utils/index.ts +2 -0
  530. package/src/utils/plainDate.test.ts +6 -0
  531. package/src/utils/plainDate.ts +21 -4
@@ -0,0 +1,97 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file MediaTheme.dom.test.tsx
5
+ * @input MediaTheme rendered in jsdom
6
+ * @output Coverage for mode="auto" and mode="off" DOM behavior
7
+ * @position Companion to useAutoMediaMode.test.ts, which covers the pure pick
8
+ *
9
+ * jsdom applies no stylesheet and resolves no custom properties, so auto
10
+ * cannot measure here — which makes this the right place to pin the fallback
11
+ * behavior that an unmeasurable surface depends on.
12
+ *
13
+ * Named `.dom.test.tsx` rather than `.test.tsx`: a sibling `.test.ts` of the
14
+ * same basename collides in the TypeScript project service, and type-aware
15
+ * lint then cannot resolve either file.
16
+ */
17
+
18
+ import {describe, it, expect} from 'vitest';
19
+ import {render, screen} from '@testing-library/react';
20
+ import {MediaTheme} from './MediaTheme';
21
+ import type {MediaThemeMode} from './MediaTheme';
22
+
23
+ function renderMode(mode: MediaThemeMode, fallback?: 'dark' | 'light') {
24
+ render(
25
+ <div style={{background: 'rgb(10, 19, 23)'}}>
26
+ <MediaTheme mode={mode} fallback={fallback}>
27
+ <span data-testid="child">content</span>
28
+ </MediaTheme>
29
+ </div>,
30
+ );
31
+ return screen.getByTestId('child').parentElement;
32
+ }
33
+
34
+ describe('MediaTheme', () => {
35
+ it.each(['dark', 'light'] as const)('applies mode=%s verbatim', mode => {
36
+ expect(renderMode(mode)?.getAttribute('data-astryx-media')).toBe(mode);
37
+ });
38
+
39
+ it('renders no media attribute for mode="off"', () => {
40
+ const wrapper = renderMode('off');
41
+ expect(wrapper?.hasAttribute('data-astryx-media')).toBe(false);
42
+ });
43
+
44
+ it('still renders its element for mode="off" so children never remount', () => {
45
+ expect(renderMode('off')?.tagName).toBe('DIV');
46
+ });
47
+
48
+ it('falls back to dark for an unmeasurable auto surface', () => {
49
+ expect(renderMode('auto')?.getAttribute('data-astryx-media')).toBe('dark');
50
+ });
51
+
52
+ it('honors an explicit fallback', () => {
53
+ expect(renderMode('auto', 'light')?.getAttribute('data-astryx-media')).toBe(
54
+ 'light',
55
+ );
56
+ });
57
+
58
+ it('ignores fallback when the mode is explicit', () => {
59
+ expect(renderMode('dark', 'light')?.getAttribute('data-astryx-media')).toBe(
60
+ 'dark',
61
+ );
62
+ });
63
+
64
+ it('leaves no probe element behind', () => {
65
+ const {container} = render(
66
+ <div style={{background: 'rgb(10, 19, 23)'}}>
67
+ <MediaTheme mode="auto">
68
+ <span>content</span>
69
+ </MediaTheme>
70
+ </div>,
71
+ );
72
+ expect(container.querySelectorAll('span')).toHaveLength(1);
73
+ });
74
+
75
+ // Regression: an earlier revision keyed the measurement to a dependency
76
+ // array, so a surface whose color changed by prop kept a stale mode until
77
+ // something unrelated re-ran the effect.
78
+ it('re-measures when the surface changes without the theme changing', () => {
79
+ function Harness({background}: {background: string}) {
80
+ return (
81
+ <div style={{background}}>
82
+ <MediaTheme mode="auto" fallback="dark">
83
+ <span data-testid="child">content</span>
84
+ </MediaTheme>
85
+ </div>
86
+ );
87
+ }
88
+ // jsdom resolves no custom properties, so the measured pick is always the
89
+ // fallback; what this pins is that the effect RUNS again — a stale
90
+ // implementation skips it entirely.
91
+ const {rerender} = render(<Harness background="rgb(10, 19, 23)" />);
92
+ const first = screen.getByTestId('child').parentElement;
93
+ rerender(<Harness background="rgb(255, 255, 255)" />);
94
+ expect(screen.getByTestId('child').parentElement).toBe(first);
95
+ expect(first?.getAttribute('data-astryx-media')).toBe('dark');
96
+ });
97
+ });
@@ -4,13 +4,13 @@
4
4
 
5
5
  /**
6
6
  * @file MediaTheme.tsx
7
- * @input mode prop ("dark" | "light") + children
7
+ * @input mode prop ("dark" | "light" | "auto" | "off") + children
8
8
  * @output Wrapper div that creates an inverted theming context
9
9
  * @position Theme system component; sibling to Theme
10
10
  *
11
11
  * Provides semantic token overrides for content rendered on a surface
12
12
  * with a different luminance than the page background. Used by Toast,
13
- * Tooltip, and other components that render on inverted surfaces.
13
+ * Overlay, and other components that render on inverted surfaces.
14
14
  *
15
15
  * How it works:
16
16
  * 1. Sets `data-astryx-media="dark|light"` — the theme's generated CSS
@@ -22,11 +22,14 @@
22
22
  * `onDark.components` / `onLight.components` in defineTheme().
23
23
  * 4. Children (Button, Link, Text, etc.) pick up inverted tokens
24
24
  * naturally through CSS custom property inheritance
25
+ * 5. `mode="auto"` measures the painted surface and picks a side — or none;
26
+ * `mode="off"` renders the same element with no media attribute, so
27
+ * either can change without remounting children.
25
28
  *
26
29
  * @example
27
30
  * ```
28
31
  * <div style={{ backgroundColor: 'var(--color-background-inverted)' }}>
29
- * <MediaTheme mode="dark">
32
+ * <MediaTheme mode="auto">
30
33
  * <Button label="Undo" variant="ghost" />
31
34
  * </MediaTheme>
32
35
  * </div>
@@ -34,24 +37,46 @@
34
37
  */
35
38
 
36
39
  import * as React from 'react';
40
+ import {useRef} from 'react';
37
41
  import * as stylex from '@stylexjs/stylex';
38
42
  import {colorVars} from './tokens.stylex';
39
43
  import {dataAttr} from '../naming';
44
+ import {useAutoMediaMode} from '../hooks/useAutoMediaMode';
40
45
 
41
46
  const styles = stylex.create({
42
47
  root: {
43
48
  display: 'contents',
49
+ },
50
+ active: {
44
51
  color: colorVars['--color-text-primary'],
45
52
  },
46
53
  });
47
54
 
55
+ /** Surface luminance context, or `"off"` for no media context at all. */
56
+ export type MediaThemeMode = 'dark' | 'light' | 'auto' | 'off';
57
+
48
58
  export interface MediaThemeProps {
49
59
  /**
50
60
  * The surface luminance context for children.
51
61
  * - `"dark"` — children are on a dark background (get light text/icons)
52
62
  * - `"light"` — children are on a light background (get dark text/icons)
63
+ * - `"auto"` — measure the painted surface and use whichever side reads
64
+ * better on it, or no media context at all when the surface's ambient
65
+ * text already reads better than either
66
+ * - `"off"` — children keep the ambient theme; the surface needs no
67
+ * inversion
53
68
  */
54
- mode: 'dark' | 'light';
69
+ mode: MediaThemeMode;
70
+ /**
71
+ * Which side `mode="auto"` uses when the surface cannot be measured: on the
72
+ * server, on the first client frame, and whenever the backdrop is not
73
+ * knowable from CSS — most often a `background-image`, whose pixels need
74
+ * sampling (see `useImageMode`) rather than a computed style.
75
+ *
76
+ * Ignored unless `mode="auto"`.
77
+ * @default "dark"
78
+ */
79
+ fallback?: 'dark' | 'light';
55
80
  /** Content to render in the media context */
56
81
  children: React.ReactNode;
57
82
  }
@@ -65,13 +90,23 @@ export interface MediaThemeProps {
65
90
  */
66
91
  export function MediaTheme({
67
92
  mode,
93
+ fallback = 'dark',
68
94
  children,
69
95
  }: MediaThemeProps): React.ReactElement {
96
+ const ref = useRef<HTMLDivElement>(null);
97
+ // Measures this element's PARENT — see useAutoMediaMode. `display: contents`
98
+ // means the parent is the element that actually paints the surface.
99
+ const detected = useAutoMediaMode(ref, mode === 'auto');
100
+ const resolved = mode === 'auto' ? (detected ?? fallback) : mode;
101
+
102
+ // "off" keeps the element — and therefore the React tree and the DOM
103
+ // structure — identical, so a surface can switch its media context on and
104
+ // off without remounting its children.
70
105
  return (
71
106
  <div
72
- {...{[dataAttr('media')]: mode}}
73
- {...stylex.props(styles.root)}
74
- >
107
+ ref={ref}
108
+ {...(resolved === 'off' ? null : {[dataAttr('media')]: resolved})}
109
+ {...stylex.props(styles.root, resolved !== 'off' && styles.active)}>
75
110
  {children}
76
111
  </div>
77
112
  );
@@ -738,7 +738,9 @@ describe('pseudo-class overrides in components', () => {
738
738
  expect(css).toContain('.astryx-radio {');
739
739
  expect(css).toContain('border-color: #8F9296');
740
740
  // Pseudo rule — separate selector
741
- expect(css).toContain('.astryx-radio:hover {');
741
+ expect(css).toContain(
742
+ '.astryx-radio:hover:where(:not(:disabled,[aria-disabled="true"])) {',
743
+ );
742
744
  expect(css).toContain(
743
745
  'border-color: color-mix(in srgb, #8F9296, black 20%)',
744
746
  );
@@ -764,7 +766,9 @@ describe('pseudo-class overrides in components', () => {
764
766
  const css = generateThemeTestCSS(theme);
765
767
  expect(css).toContain('.astryx-button.primary-muted {');
766
768
  expect(css).toContain('background-color: #ECF5FF');
767
- expect(css).toContain('.astryx-button.primary-muted:hover {');
769
+ expect(css).toContain(
770
+ '.astryx-button.primary-muted:hover:where(:not(:disabled,[aria-disabled="true"])) {',
771
+ );
768
772
  expect(css).toContain('background-color: #D6EBFF');
769
773
  expect(css).toContain('.astryx-button.primary-muted:focus-visible {');
770
774
  expect(css).toContain('outline: 2px solid var(--color-accent)');
@@ -787,7 +791,9 @@ describe('pseudo-class overrides in components', () => {
787
791
  // Should NOT emit an empty base rule
788
792
  expect(css).not.toMatch(/\.astryx-switch\s*\{\s*\}/);
789
793
  // Should emit the pseudo rule
790
- expect(css).toContain('.astryx-switch:hover {');
794
+ expect(css).toContain(
795
+ '.astryx-switch:hover:where(:not(:disabled,[aria-disabled="true"])) {',
796
+ );
791
797
  });
792
798
 
793
799
  it('keeps non-pseudo string values as regular properties', () => {
@@ -113,6 +113,11 @@ export type TokenValue = string | [light: string, dark: string];
113
113
  * to the component selector. Supported pseudo-classes include `:hover`,
114
114
  * `:focus-visible`, `:active`, `:checked`, `:disabled`, etc.
115
115
  *
116
+ * A `:hover` override describes the ENABLED control: it is emitted with a
117
+ * guard that keeps it off disabled and `aria-disabled` elements, which
118
+ * `:hover` would otherwise still match. Style the disabled state through
119
+ * `:disabled` instead.
120
+ *
116
121
  * @example
117
122
  * ```ts
118
123
  * {
@@ -85,6 +85,9 @@ const STRUCTURAL_VARS = new Set([
85
85
  '--table-row-overlay',
86
86
  '--separator-display',
87
87
  '--astryx-section-padding',
88
+ // Private counterpart of the public token above: one ancestor Section's
89
+ // padding, propagated down the tree. Structural, never authored by a theme.
90
+ '--_section-padding-propagated',
88
91
  ]);
89
92
 
90
93
  /**
@@ -164,9 +167,6 @@ function discoverComponents(): ComponentInfo[] {
164
167
  allVars.add(v);
165
168
  }
166
169
  }
167
- if (allVars.size === 0) {
168
- continue;
169
- }
170
170
 
171
171
  // Only check component directories (those with a doc file named after the
172
172
  // directory). Match against the on-disk listing rather than existsSync so
@@ -188,6 +188,16 @@ function discoverComponents(): ComponentInfo[] {
188
188
  /* skip */
189
189
  }
190
190
 
191
+ // A directory earns a check by declaring a var OR by documenting a
192
+ // derived[] entry. Bailing on the var count alone (as this did) hid every
193
+ // component that is themeable purely through an expansion strategy —
194
+ // `{property: 'padding', expand: 'container'}` names no var, so such a
195
+ // component declares nothing and its registry↔doc consistency check
196
+ // silently never ran.
197
+ if (allVars.size === 0 && docDerived.length === 0) {
198
+ continue;
199
+ }
200
+
191
201
  results.push({
192
202
  dir,
193
203
  sourceVars: [...allVars],
@@ -212,6 +222,7 @@ const DIR_TO_REGISTRY_KEY: Record<string, string> = {
212
222
  DropdownMenu: 'dropdown-menu',
213
223
  Field: 'field',
214
224
  HoverCard: 'hovercard',
225
+ NumberInput: 'number-input',
215
226
  Popover: 'popover',
216
227
  ProgressBar: 'progressbar-mark',
217
228
  Section: 'section',
@@ -262,9 +273,12 @@ const VARS_WITHOUT_DERIVED_MAPPING = new Set([
262
273
  '--_avatar-group-overlap',
263
274
  '--_codeblock-gutter-width',
264
275
  '--_tab-indicator-bottom',
265
- // Hit-area outset on a ::after overlay — `inset` on a pseudo-element is not
266
- // a property a theme author sets on the component.
276
+ // Hit-area outset on a ::after overlay, and whether that overlay is
277
+ // generated at all — `inset` and `content` on a pseudo-element are not
278
+ // properties a theme author sets on the component.
267
279
  '--_thumbnail-hit-inset',
280
+ '--_input-clear-hit-inset',
281
+ '--_input-clear-hit-content',
268
282
  // Indentation and row-spacing metrics: --tree-list-indent is the authorable
269
283
  // step, --_tree-indent the per-row distance TreeListItem computes from it.
270
284
  // --tree-list-row-gap is applied as half a padding-block on each row wrapper,
@@ -67,6 +67,10 @@ export const derivedVarRegistry: Record<string, DerivedVarEntry[]> = {
67
67
  ],
68
68
  field: [{property: 'borderRadius', vars: ['--_field-radius']}],
69
69
  hovercard: [{property: 'borderRadius', vars: ['--_hovercard-radius']}],
70
+ 'number-input': [
71
+ {property: 'padding', expand: 'container'},
72
+ {property: 'borderRadius', vars: ['--_field-radius']},
73
+ ],
70
74
  popover: [{property: 'borderRadius', vars: ['--_popover-radius']}],
71
75
  'progressbar-mark': [
72
76
  {property: 'width', vars: ['--_progressbar-mark-width'], replaces: true},
@@ -160,6 +160,45 @@ describe('generateThemeRules', () => {
160
160
  ).toBe(true);
161
161
  });
162
162
 
163
+ // A theme authoring `:hover` is describing the ENABLED control. Without a
164
+ // guard the rule paints a disabled one too, because browsers suppress a
165
+ // disabled control's events, not its hover styling — and a theme override
166
+ // would then reintroduce, on every component at once, the defect the
167
+ // components' own styles were fixed for.
168
+ it('keeps a themed :hover off disabled elements', () => {
169
+ const hoverTheme = defineTheme({
170
+ name: 'hover-guard',
171
+ components: {
172
+ button: {
173
+ base: {
174
+ ':hover': {color: 'red'},
175
+ ':focus-visible': {outline: '2px solid blue'},
176
+ },
177
+ },
178
+ },
179
+ });
180
+ const hoverRules = generateThemeRules(hoverTheme);
181
+ const hoverRule = hoverRules.find(rule => rule.includes(':hover'));
182
+ expect(hoverRule).toBeDefined();
183
+ expect(hoverRule).toContain(
184
+ '.astryx-button:hover:where(:not(:disabled,[aria-disabled="true"]))',
185
+ );
186
+ // Every selector in a comma-separated list carries its own guard —
187
+ // a trailing pseudo does not distribute over a selector list. (Counted,
188
+ // not split: the guard contains a comma of its own.)
189
+ const selectorText = String(hoverRule).split('{')[0];
190
+ const hovers = selectorText.match(/:hover/g) || [];
191
+ const guards = selectorText.match(/:where\(:not\(:disabled/g) || [];
192
+ expect(hovers.length).toBeGreaterThan(0);
193
+ expect(guards.length).toBe(hovers.length);
194
+ // Other pseudo-classes are untouched: a disabled control can still be
195
+ // focused (that is the point of aria-disabled), and :focus-visible on it
196
+ // is correct.
197
+ const focusRule = hoverRules.find(rule => rule.includes(':focus-visible'));
198
+ expect(focusRule).toContain('.astryx-button:focus-visible {');
199
+ expect(focusRule).not.toContain('aria-disabled');
200
+ });
201
+
163
202
  // --- Prose rules ---
164
203
 
165
204
  it('includes prose heading rules with computed values', () => {
@@ -584,3 +623,82 @@ describe('brutalist-style derived expansion', () => {
584
623
  expect(rule).toContain('--_dropdown-menu-padding: 4px');
585
624
  });
586
625
  });
626
+
627
+ describe('physical padding longhands', () => {
628
+ const ruleFor = (
629
+ component: string,
630
+ base: Record<string, string>,
631
+ name = 'test-physical-padding',
632
+ ) =>
633
+ generateThemeRules(
634
+ defineTheme({name, components: {[component]: {base}}}),
635
+ ).find(r => r.includes(`.astryx-${component}`));
636
+
637
+ // `padding-top`/`padding-bottom` ARE the block edges in every horizontal
638
+ // writing mode, so the expansion can normalize them with no direction
639
+ // assumption. Without that, the padding lands raw on the element and the
640
+ // component's internals — the NumberInput stepper column, container bleed —
641
+ // read the default instead of what the theme set.
642
+ it.each([
643
+ ['paddingTop', {paddingTop: '14px'}, 'block-start'],
644
+ ['paddingBottom', {paddingBottom: '14px'}, 'block-end'],
645
+ ])('routes %s through the container expansion', (_label, base, edge) => {
646
+ const rule = ruleFor('card', base);
647
+ expect(rule).toContain(`--astryx-card-padding-${edge}: 14px`);
648
+ expect(rule).not.toMatch(/[{;]\s*padding-(top|bottom):/);
649
+ });
650
+
651
+ it('normalizes both block edges together, asymmetrically', () => {
652
+ const rule = ruleFor('number-input', {
653
+ paddingTop: '14px',
654
+ paddingBottom: '6px',
655
+ });
656
+ expect(rule).toContain('--astryx-number-input-padding-block-start: 14px');
657
+ expect(rule).toContain('--astryx-number-input-padding-block-end: 6px');
658
+ });
659
+
660
+ it.each(['card', 'dialog', 'section', 'number-input'])(
661
+ 'reaches %s, which expands its padding',
662
+ component => {
663
+ expect(ruleFor(component, {paddingTop: '14px'})).toContain(
664
+ `--astryx-${component}-padding-block-start: 14px`,
665
+ );
666
+ },
667
+ );
668
+
669
+ // THE RTL GUARD. `paddingLeft` is inline-start in LTR and inline-end in RTL,
670
+ // and the tokens are consumed by logical properties, so mapping it would
671
+ // silently move the padding to the other edge in RTL. It stays physical —
672
+ // which is what the author wrote — and does not reach the tokens.
673
+ it('leaves the direction-relative inline pair physical', () => {
674
+ const rule = ruleFor('card', {paddingLeft: '20px', paddingRight: '8px'});
675
+ expect(rule).toContain('padding-left: 20px');
676
+ expect(rule).toContain('padding-right: 8px');
677
+ expect(rule).not.toContain('--astryx-card-padding-inline');
678
+ });
679
+
680
+ it('expands the block edges while leaving left physical', () => {
681
+ const rule = ruleFor('card', {paddingTop: '14px', paddingLeft: '20px'});
682
+ expect(rule).toContain('--astryx-card-padding-block-start: 14px');
683
+ expect(rule).toContain('padding-left: 20px');
684
+ });
685
+
686
+ // The shorthand-plus-override case was the worst one: the tokens carried
687
+ // 10px while the element painted 14px on top, so every internal compensated
688
+ // by the wrong amount.
689
+ it('lets a physical longhand override the shorthand per edge', () => {
690
+ const rule = ruleFor('card', {padding: '10px', paddingTop: '14px'});
691
+ expect(rule).toContain('--astryx-card-padding-block-start: 14px');
692
+ expect(rule).toContain('--astryx-card-padding-block-end: 10px');
693
+ expect(rule).toContain('--astryx-card-padding-inline: 10px');
694
+ expect(rule).not.toMatch(/[{;]\s*padding(-top)?:/);
695
+ });
696
+
697
+ // A `vars` entry carries one value for the whole box, so a single physical
698
+ // edge must not feed it — only the container expansion takes these.
699
+ it('does not feed a single edge to a whole-box derived var', () => {
700
+ const rule = ruleFor('dropdown-menu', {paddingTop: '14px'});
701
+ expect(rule).toContain('padding-top: 14px');
702
+ expect(rule).not.toContain('--_dropdown-menu-padding');
703
+ });
704
+ });
@@ -46,12 +46,30 @@ function componentClassSelector(component: string, suffix: string): string {
46
46
  return `.${classPrefix}-${component}${suffix}`;
47
47
  }
48
48
 
49
+ /**
50
+ * Guard appended to a themed `:hover` rule so it cannot match a disabled
51
+ * element.
52
+ *
53
+ * A theme authoring `':hover': {backgroundColor: …}` is describing the
54
+ * enabled control; `:hover` on its own would paint that background on a
55
+ * disabled one too, because browsers suppress a disabled control's events,
56
+ * not its hover styling. `:where()` contributes no specificity, so a themed
57
+ * hover rule still weighs exactly what it weighed before.
58
+ *
59
+ * Mirrors the `@astryx/no-hover-on-disabled` lint rule, which enforces the
60
+ * same guard on the components' own StyleX styles.
61
+ */
62
+ const HOVER_DISABLED_GUARD = ':where(:not(:disabled,[aria-disabled="true"]))';
63
+
49
64
  /**
50
65
  * Append a pseudo-class to every selector in a comma-separated selector list.
51
66
  *
52
67
  * Selector helpers may emit comma-separated lists. CSS does not distribute a
53
68
  * trailing pseudo over selector lists, so `${list}:hover` would only target the
54
69
  * final selector. Rewrite each item so the pseudo applies to all of them.
70
+ *
71
+ * A `:hover` pseudo also picks up the disabled guard. A pseudo-ELEMENT has to
72
+ * end the selector, so the guard is spliced in before it.
55
73
  */
56
74
  function appendPseudoToSelectorList(selector: string, pseudo: string): string {
57
75
  const parts: string[] = [];
@@ -71,7 +89,22 @@ function appendPseudoToSelectorList(selector: string, pseudo: string): string {
71
89
  }
72
90
  parts.push(selector.slice(start).trim());
73
91
 
74
- return parts.map(part => `${part}${pseudo}`).join(', ');
92
+ const guarded = guardHoverPseudo(pseudo);
93
+
94
+ return parts.map(part => `${part}${guarded}`).join(', ');
95
+ }
96
+
97
+ /** Insert the disabled guard into a `:hover` pseudo, keeping any pseudo-element last. */
98
+ function guardHoverPseudo(pseudo: string): string {
99
+ if (!/^:hover(?![-\w])/.test(pseudo) || pseudo.includes('[aria-disabled')) {
100
+ return pseudo;
101
+ }
102
+ const pseudoElement = pseudo.indexOf('::');
103
+ return pseudoElement === -1
104
+ ? pseudo + HOVER_DISABLED_GUARD
105
+ : pseudo.slice(0, pseudoElement) +
106
+ HOVER_DISABLED_GUARD +
107
+ pseudo.slice(pseudoElement);
75
108
  }
76
109
 
77
110
  // =============================================================================
@@ -128,6 +161,34 @@ const PADDING_PROPS = new Set([
128
161
  'paddingInlineEnd',
129
162
  ]);
130
163
 
164
+ /**
165
+ * Physical block-axis longhands, and the logical longhand each one *is* in
166
+ * every horizontal writing mode. Normalizing them costs no direction
167
+ * assumption, which is why they can join the container expansion.
168
+ *
169
+ * `paddingLeft`/`paddingRight` are deliberately absent. They are
170
+ * direction-relative — left is inline-start in LTR and inline-end in RTL — and
171
+ * the expansion's tokens are consumed by logical properties, so mapping them
172
+ * would put the padding on the opposite edge in RTL. They keep their physical
173
+ * meaning and land on the element as `padding-left`/`padding-right`; the cost
174
+ * is that a component's internals cannot see them.
175
+ */
176
+ const PHYSICAL_BLOCK_PADDING_PROPS: Record<string, string> = {
177
+ paddingTop: 'paddingBlockStart',
178
+ paddingBottom: 'paddingBlockEnd',
179
+ };
180
+
181
+ /**
182
+ * Every padding spelling the container expansion consumes. Kept separate from
183
+ * PADDING_PROPS, which also routes longhands to `vars`-style derived entries —
184
+ * those carry one value for the whole box, so a single physical edge must not
185
+ * reach them.
186
+ */
187
+ const CONTAINER_PADDING_PROPS = new Set([
188
+ ...PADDING_PROPS,
189
+ ...Object.keys(PHYSICAL_BLOCK_PADDING_PROPS),
190
+ ]);
191
+
131
192
  interface ParsedPadding {
132
193
  blockStart?: string;
133
194
  blockEnd?: string;
@@ -138,12 +199,14 @@ interface ParsedPadding {
138
199
 
139
200
  /**
140
201
  * Parse CSS padding shorthand/longhand into block/inline values.
141
- * Supports 1-3 value shorthands and logical properties.
202
+ * Supports 1-3 value shorthands, logical properties, and the physical block
203
+ * longhands normalized by PHYSICAL_BLOCK_PADDING_PROPS.
142
204
  */
143
205
  function parsePadding(props: [string, string][]): ParsedPadding {
144
206
  const result: ParsedPadding = {};
145
207
 
146
- for (const [prop, value] of props) {
208
+ for (const [rawProp, value] of props) {
209
+ const prop = PHYSICAL_BLOCK_PADDING_PROPS[rawProp] ?? rawProp;
147
210
  switch (prop) {
148
211
  case 'padding': {
149
212
  const parts = value.trim().split(/\s+/);
@@ -377,13 +440,28 @@ function generateComponentRules(
377
440
  }
378
441
  }
379
442
  }
443
+ // A physical block longhand reaches the container expansion only. It
444
+ // names one edge, so it must not feed a `vars` entry above, which
445
+ // carries the padding for the whole box.
446
+ if (
447
+ prop in PHYSICAL_BLOCK_PADDING_PROPS &&
448
+ getDerivedVars(component, 'padding').some(
449
+ e => e.expand === 'container',
450
+ )
451
+ ) {
452
+ containerExpanded = true;
453
+ }
380
454
  }
381
455
 
382
456
  // Container padding expansion: replace padding props with
383
457
  // component-scoped container tokens for layout integration.
384
458
  if (containerExpanded) {
385
- const paddingProps = props.filter(([p]) => PADDING_PROPS.has(p));
386
- const nonPaddingProps = props.filter(([p]) => !PADDING_PROPS.has(p));
459
+ const paddingProps = props.filter(([p]) =>
460
+ CONTAINER_PADDING_PROPS.has(p),
461
+ );
462
+ const nonPaddingProps = props.filter(
463
+ ([p]) => !CONTAINER_PADDING_PROPS.has(p),
464
+ );
387
465
  const parsed = parsePadding(paddingProps);
388
466
  const containerTokens = expandContainerPadding(component, parsed);
389
467
  finalProps = [...nonPaddingProps, ...containerTokens];
@@ -17,7 +17,7 @@
17
17
 
18
18
  export {Theme} from './Theme';
19
19
  export {MediaTheme} from './MediaTheme';
20
- export type {MediaThemeProps} from './MediaTheme';
20
+ export type {MediaThemeProps, MediaThemeMode} from './MediaTheme';
21
21
  export {
22
22
  defineTheme,
23
23
  generateThemeCSS,
@@ -1,10 +1,14 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
- 'use client';
4
-
5
3
  /**
6
4
  * @file syntax/index.ts
7
5
  * @position Re-exports for the syntax theme subsystem
6
+ *
7
+ * No `'use client'` here on purpose. Most of what this barrel exports is data
8
+ * (presets, token defaults) or pure functions, and a directive on the barrel
9
+ * turns every export into a client reference for a server importer — so
10
+ * `dracula.tokens` reads back `undefined` instead of colors. `SyntaxTheme.tsx`
11
+ * carries its own directive, which is what the provider actually needs.
8
12
  */
9
13
 
10
14
  export {syntaxTokenDefaults} from './tokens';