@astryxdesign/core 0.4.5 → 0.4.6-canary.cea9ecd

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 (538) 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/useListFocus.d.ts +5 -2
  282. package/dist/hooks/useListFocus.d.ts.map +1 -1
  283. package/dist/hooks/useListFocus.js +12 -6
  284. package/dist/hooks/useScrollLock.d.ts +4 -0
  285. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  286. package/dist/hooks/useScrollLock.js +13 -1
  287. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  288. package/dist/i18n/InternationalizationContext.js +2 -0
  289. package/dist/i18n/index.d.ts +6 -0
  290. package/dist/i18n/index.d.ts.map +1 -1
  291. package/dist/i18n/index.js +6 -0
  292. package/dist/i18n/useCollator.d.ts +12 -0
  293. package/dist/i18n/useCollator.d.ts.map +1 -0
  294. package/dist/i18n/useCollator.js +40 -0
  295. package/dist/i18n/useLocale.d.ts +3 -0
  296. package/dist/i18n/useLocale.d.ts.map +1 -0
  297. package/dist/i18n/useLocale.js +31 -0
  298. package/dist/theme/MediaTheme.d.ts +25 -5
  299. package/dist/theme/MediaTheme.d.ts.map +1 -1
  300. package/dist/theme/MediaTheme.js +31 -6
  301. package/dist/theme/defineTheme.d.ts +5 -0
  302. package/dist/theme/defineTheme.d.ts.map +1 -1
  303. package/dist/theme/defineTheme.js +5 -0
  304. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  305. package/dist/theme/derivedVarRegistry.js +7 -0
  306. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  307. package/dist/theme/generateThemeRules.js +66 -5
  308. package/dist/theme/index.d.ts +1 -1
  309. package/dist/theme/index.d.ts.map +1 -1
  310. package/dist/theme/syntax/index.d.ts +6 -0
  311. package/dist/theme/syntax/index.d.ts.map +1 -1
  312. package/dist/theme/syntax/index.js +7 -2
  313. package/dist/utils/dateParser.d.ts +4 -3
  314. package/dist/utils/dateParser.d.ts.map +1 -1
  315. package/dist/utils/dateParser.js +8 -8
  316. package/dist/utils/index.d.ts +1 -1
  317. package/dist/utils/index.d.ts.map +1 -1
  318. package/dist/utils/index.js +1 -1
  319. package/dist/utils/plainDate.d.ts +5 -2
  320. package/dist/utils/plainDate.d.ts.map +1 -1
  321. package/dist/utils/plainDate.js +20 -4
  322. package/locales/en.json +28 -0
  323. package/locales/pseudo.json +21 -0
  324. package/package.json +3 -3
  325. package/src/Avatar/Avatar.doc.mjs +10 -6
  326. package/src/Avatar/Avatar.test.tsx +219 -1
  327. package/src/Avatar/Avatar.tsx +179 -37
  328. package/src/Avatar/AvatarStatusDot.tsx +30 -4
  329. package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
  330. package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
  331. package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
  332. package/src/AvatarGroup/AvatarGroup.tsx +3 -1
  333. package/src/AvatarGroup/AvatarGroupOverflow.tsx +31 -8
  334. package/src/Banner/Banner.doc.mjs +2 -0
  335. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  336. package/src/Blockquote/Blockquote.test.tsx +34 -3
  337. package/src/Blockquote/Blockquote.tsx +11 -9
  338. package/src/BottomSheet/BottomSheet.doc.mjs +3 -3
  339. package/src/BottomSheet/BottomSheet.test.tsx +24 -0
  340. package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
  341. package/src/BottomSheet/BottomSheetPanel.tsx +58 -3
  342. package/src/Breadcrumbs/BreadcrumbItem.tsx +36 -23
  343. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
  344. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
  345. package/src/Button/Button.tsx +12 -9
  346. package/src/Calendar/Calendar.tsx +15 -9
  347. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  348. package/src/Calendar/styles.ts +8 -5
  349. package/src/Center/Center.doc.mjs +48 -0
  350. package/src/Center/Center.test.tsx +158 -0
  351. package/src/Center/Center.tsx +50 -9
  352. package/src/Chat/ChatComposer.tsx +11 -5
  353. package/src/Chat/ChatComposerDrawer.tsx +4 -1
  354. package/src/Chat/ChatToolCalls.tsx +13 -4
  355. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  356. package/src/Chat/useChatDictation.ts +1 -1
  357. package/src/Chat/useSpeechRecognition.ts +5 -3
  358. package/src/Chat/useTriggerMenu.tsx +4 -1
  359. package/src/CheckboxInput/CheckboxInput.tsx +5 -2
  360. package/src/Citation/Citation.test.tsx +6 -1
  361. package/src/Citation/Citation.tsx +11 -5
  362. package/src/ClickableCard/ClickableCard.tsx +7 -4
  363. package/src/CodeBlock/CodeBlock.tsx +4 -1
  364. package/src/Collapsible/Collapsible.tsx +5 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +6 -3
  366. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  367. package/src/ComplexSelector/ComplexSelector.tsx +14 -7
  368. package/src/DateInput/DateInput.test.tsx +17 -1
  369. package/src/DateInput/DateInput.tsx +93 -21
  370. package/src/DateInput/DateInputTouch.test.tsx +2582 -0
  371. package/src/DateInput/MonthScroller.tsx +878 -0
  372. package/src/DateInput/MonthYearWheels.tsx +147 -0
  373. package/src/DateInput/TouchDateField.tsx +1223 -0
  374. package/src/DateInput/Wheel.tsx +473 -0
  375. package/src/DateInput/monthGeometry.ts +118 -0
  376. package/src/DateInput/tokens.stylex.ts +99 -0
  377. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  378. package/src/DateInput/usePointerDragScroll.ts +220 -0
  379. package/src/DateInput/useScrollSettle.ts +164 -0
  380. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  381. package/src/DateRangeInput/DateRangeInput.tsx +31 -13
  382. package/src/DateTimeInput/DateTimeInput.tsx +25 -9
  383. package/src/Dialog/Dialog.test.tsx +24 -0
  384. package/src/Dialog/Dialog.tsx +3 -0
  385. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
  386. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
  387. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
  389. package/src/Field/Field.doc.mjs +2 -0
  390. package/src/Field/FieldLabel.tsx +9 -3
  391. package/src/Field/InputClearButton.test.tsx +110 -3
  392. package/src/Field/InputClearButton.tsx +38 -0
  393. package/src/Field/inputStyles.stylex.ts +17 -13
  394. package/src/FileInput/FileInput.tsx +15 -8
  395. package/src/FormLayout/FormLayout.doc.mjs +1 -1
  396. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  397. package/src/HoverCard/HoverCard.test.tsx +189 -0
  398. package/src/HoverCard/HoverCard.tsx +22 -2
  399. package/src/HoverCard/index.ts +1 -0
  400. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  401. package/src/HoverCard/useHoverCard.tsx +103 -7
  402. package/src/InputGroup/InputGroup.tsx +1 -1
  403. package/src/Item/Item.doc.mjs +1 -0
  404. package/src/Item/Item.test.tsx +50 -0
  405. package/src/Item/Item.tsx +54 -6
  406. package/src/Layer/index.ts +10 -0
  407. package/src/Layer/useLayer.tsx +13 -2
  408. package/src/Layer/useTouchTrigger.test.ts +70 -0
  409. package/src/Layer/useTouchTrigger.ts +366 -0
  410. package/src/Layout/Layout.doc.mjs +18 -0
  411. package/src/Layout/container.stylex.ts +11 -3
  412. package/src/Layout/index.ts +4 -0
  413. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  414. package/src/Layout/padding.stylex.ts +192 -12
  415. package/src/Lightbox/Lightbox.tsx +11 -3
  416. package/src/Link/Link.tsx +9 -6
  417. package/src/MetadataList/MetadataList.tsx +4 -1
  418. package/src/MobileNav/MobileNav.tsx +61 -5
  419. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  420. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  421. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  422. package/src/MultiSelector/MultiSelector.tsx +26 -10
  423. package/src/NavItem/navItemStyles.stylex.ts +7 -4
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +6 -3
  425. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  426. package/src/NumberInput/NumberInput.test.tsx +138 -0
  427. package/src/NumberInput/NumberInput.tsx +47 -8
  428. package/src/Outline/Outline.tsx +5 -2
  429. package/src/Pagination/Pagination.tsx +5 -2
  430. package/src/Popover/Popover.test.tsx +27 -1
  431. package/src/PowerSearch/PowerSearch.tsx +17 -8
  432. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  433. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  434. package/src/PowerSearch/formatFilterValue.ts +31 -6
  435. package/src/RadioList/RadioList.doc.mjs +1 -1
  436. package/src/RadioList/RadioList.test.tsx +170 -2
  437. package/src/RadioList/RadioListItem.tsx +88 -53
  438. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  439. package/src/Resizable/ResizeHandle.tsx +25 -7
  440. package/src/Section/Section.doc.mjs +55 -0
  441. package/src/Section/Section.test.tsx +201 -4
  442. package/src/Section/Section.tsx +69 -0
  443. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  444. package/src/SegmentedControl/SegmentedControlItem.tsx +15 -3
  445. package/src/SelectableCard/SelectableCard.tsx +6 -3
  446. package/src/Selector/Selector.doc.mjs +16 -2
  447. package/src/Selector/Selector.test.tsx +525 -1
  448. package/src/Selector/Selector.tsx +180 -25
  449. package/src/Selector/SelectorOption.doc.mjs +7 -0
  450. package/src/Selector/SelectorOption.tsx +21 -0
  451. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  452. package/src/Selector/types.ts +4 -4
  453. package/src/SideNav/SideNavHeading.tsx +14 -5
  454. package/src/SideNav/SideNavItem.tsx +9 -3
  455. package/src/Slider/Slider.tsx +25 -6
  456. package/src/Stack/Stack.doc.mjs +72 -0
  457. package/src/Stack/Stack.test.tsx +156 -0
  458. package/src/Stack/Stack.tsx +50 -8
  459. package/src/Switch/Switch.tsx +5 -2
  460. package/src/TabList/Tab.tsx +4 -1
  461. package/src/TabList/TabMenu.tsx +9 -3
  462. package/src/Table/TableRow.tsx +4 -4
  463. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +6 -2
  464. package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
  465. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +10 -3
  466. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +6 -3
  467. package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
  468. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  469. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  470. package/src/Table/plugins/tree/useTableTreeData.tsx +10 -4
  471. package/src/TextArea/TextArea.doc.mjs +1 -1
  472. package/src/TextArea/TextArea.tsx +1 -1
  473. package/src/TextInput/TextInput.tsx +1 -1
  474. package/src/Thumbnail/Thumbnail.tsx +9 -3
  475. package/src/TimeInput/TimeInput.tsx +1 -1
  476. package/src/Timestamp/Timestamp.test.tsx +27 -7
  477. package/src/Timestamp/Timestamp.tsx +8 -4
  478. package/src/Timestamp/formatInstant.ts +9 -7
  479. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  480. package/src/Timestamp/tooltipEntries.ts +6 -2
  481. package/src/Toast/Toast.tsx +9 -7
  482. package/src/Token/Token.tsx +14 -5
  483. package/src/Tokenizer/Tokenizer.tsx +4 -1
  484. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  485. package/src/Tooltip/Tooltip.test.tsx +254 -1
  486. package/src/Tooltip/Tooltip.tsx +22 -2
  487. package/src/Tooltip/index.ts +1 -0
  488. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  489. package/src/Tooltip/useTooltip.tsx +99 -21
  490. package/src/TopNav/TopNavHeading.tsx +13 -4
  491. package/src/TopNav/TopNavItem.tsx +6 -3
  492. package/src/TopNav/TopNavMegaMenu.tsx +5 -2
  493. package/src/TopNav/TopNavMegaMenuItem.tsx +5 -2
  494. package/src/TopNav/TopNavMenu.tsx +10 -4
  495. package/src/TreeList/TreeListItem.tsx +22 -7
  496. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  497. package/src/Typeahead/Typeahead.tsx +4 -1
  498. package/src/hooks/containerReveal.stylex.ts +54 -18
  499. package/src/hooks/scrollbarGutter.test.ts +167 -0
  500. package/src/hooks/scrollbarGutter.ts +121 -0
  501. package/src/hooks/useAutoMediaMode.test.ts +109 -0
  502. package/src/hooks/useAutoMediaMode.ts +233 -0
  503. package/src/hooks/useInputStatusIcon.tsx +4 -1
  504. package/src/hooks/useListFocus.doc.mjs +2 -2
  505. package/src/hooks/useListFocus.test.tsx +65 -3
  506. package/src/hooks/useListFocus.ts +15 -7
  507. package/src/hooks/useScrollLock.doc.mjs +4 -4
  508. package/src/hooks/useScrollLock.test.ts +73 -0
  509. package/src/hooks/useScrollLock.ts +14 -0
  510. package/src/i18n/InternationalizationContext.ts +2 -0
  511. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  512. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  513. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  514. package/src/i18n/index.ts +6 -0
  515. package/src/i18n/useCollator.doc.mjs +84 -0
  516. package/src/i18n/useCollator.ts +44 -0
  517. package/src/i18n/useLocale.doc.mjs +70 -0
  518. package/src/i18n/useLocale.ts +34 -0
  519. package/src/i18n/useTranslator.doc.mjs +1 -1
  520. package/src/reset.css +9 -2
  521. package/src/reset.test.ts +22 -0
  522. package/src/theme/MediaTheme.doc.mjs +23 -4
  523. package/src/theme/MediaTheme.dom.test.tsx +97 -0
  524. package/src/theme/MediaTheme.tsx +42 -7
  525. package/src/theme/defineTheme.test.ts +9 -3
  526. package/src/theme/defineTheme.ts +5 -0
  527. package/src/theme/derivedVarRegistry.test.ts +19 -5
  528. package/src/theme/derivedVarRegistry.ts +4 -0
  529. package/src/theme/generateThemeRules.test.ts +118 -0
  530. package/src/theme/generateThemeRules.ts +83 -5
  531. package/src/theme/index.ts +1 -1
  532. package/src/theme/syntax/index.ts +6 -2
  533. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  534. package/src/utils/dateParser.test.ts +20 -0
  535. package/src/utils/dateParser.ts +14 -7
  536. package/src/utils/index.ts +2 -0
  537. package/src/utils/plainDate.test.ts +6 -0
  538. package/src/utils/plainDate.ts +21 -4
@@ -18,11 +18,13 @@ import * as stylex from '@stylexjs/stylex';
18
18
  import {
19
19
  colorVars,
20
20
  typographyVars,
21
+ typeScaleVars,
21
22
  fontWeightVars,
22
23
  radiusVars,
23
24
  spacingVars,
24
25
  } from '../theme/tokens.stylex';
25
26
  import {mergeProps} from '../utils';
27
+ import {resolveSize} from '../Avatar';
26
28
  import {useAvatarGroup} from './AvatarGroupContext';
27
29
  import type {BaseProps} from '../BaseProps';
28
30
  import {themeProps} from '../utils/themeProps';
@@ -57,7 +59,10 @@ export interface AvatarGroupOverflowProps extends Omit<
57
59
  const styles = stylex.create({
58
60
  base: {
59
61
  position: 'relative',
60
- display: 'flex',
62
+ // inline-flex, not flex: outside an AvatarGroup this span is not a flex
63
+ // item, and a block-level flex container stretches to its parent's width
64
+ // instead of staying a circle.
65
+ display: 'inline-flex',
61
66
  alignItems: 'center',
62
67
  justifyContent: 'center',
63
68
  borderRadius: radiusVars['--radius-full'],
@@ -81,14 +86,17 @@ const styles = stylex.create({
81
86
  backgroundImage: `linear-gradient(${colorVars['--color-neutral']}, ${colorVars['--color-neutral']})`,
82
87
  },
83
88
  button: {
84
- cursor: 'pointer',
89
+ cursor: {
90
+ default: 'pointer',
91
+ ':is(:disabled,[aria-disabled="true"])': 'default',
92
+ },
85
93
  // Reset the UA button's block padding only; the inline padding from `base`
86
94
  // provides the pill's breathing room and must be preserved.
87
95
  paddingBlock: 0,
88
96
  // Interactive overlay states layered on top via backgroundImage
89
97
  backgroundImage: {
90
98
  default: `linear-gradient(${colorVars['--color-neutral']}, ${colorVars['--color-neutral']})`,
91
- ':hover': {
99
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
92
100
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']}), linear-gradient(${colorVars['--color-neutral']}, ${colorVars['--color-neutral']})`,
93
101
  },
94
102
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']}), linear-gradient(${colorVars['--color-neutral']}, ${colorVars['--color-neutral']})`,
@@ -96,7 +104,12 @@ const styles = stylex.create({
96
104
  // Focus ring via focus-visible
97
105
  },
98
106
  overlap: {
99
- marginInlineStart: 'var(--_avatar-group-overlap)',
107
+ // Matches Avatar's own overlap rule: the first item in the row must not be
108
+ // pulled outside the group's box.
109
+ marginInlineStart: {
110
+ default: null,
111
+ ':not(:first-child)': 'var(--_avatar-group-overlap)',
112
+ },
100
113
  },
101
114
  });
102
115
 
@@ -113,7 +126,13 @@ const dynamicStyles = stylex.create({
113
126
  height: s + BORDER_WIDTH * 2,
114
127
  }),
115
128
  fontSize: (s: number) => ({
116
- fontSize: s * OVERFLOW_FONT_RATIO,
129
+ // Scales with the avatar, but never below the supporting-text role token,
130
+ // which is the 12px legibility floor. At xsm the bare ratio computes 7px,
131
+ // where the glyph stroke is thinner than a pixel and never reaches its
132
+ // own text colour (measured 1.63:1 against a 4.5:1 requirement).
133
+ fontSize: `max(${typeScaleVars['--text-supporting-size']}, ${
134
+ s * OVERFLOW_FONT_RATIO
135
+ }px)`,
117
136
  }),
118
137
  overlap: (offset: number) => ({
119
138
  '--_avatar-group-overlap': `${offset}px`,
@@ -147,11 +166,15 @@ export function AvatarGroupOverflow({
147
166
  const t = useTranslator();
148
167
  const group = useAvatarGroup();
149
168
  const size = group?.size ?? 'md';
150
- const numericSize = group?.numericSize ?? 36;
169
+ const numericSize = group?.numericSize ?? resolveSize('md');
151
170
  const overlap = group?.overlap ?? 0;
152
171
 
153
- const label = t('@astryx.avatarGroup.overflow', {count});
154
- const content = children ?? `+${count}`;
172
+ // count is a plain number, and the documented shape for it is
173
+ // `total - visibleCount`, which goes negative whenever the list is shorter
174
+ // than the slice. Clamping keeps that from rendering "+-3".
175
+ const safeCount = Math.max(0, count);
176
+ const label = t('@astryx.avatarGroup.overflow', {count: safeCount});
177
+ const content = children ?? `+${safeCount}`;
155
178
 
156
179
  if (onClick) {
157
180
  return (
@@ -221,6 +221,8 @@ export const docsDense = {
221
221
  {guidance: true, description: 'Keep titles short: "Payment failed" not "There was a problem processing your payment."'},
222
222
  {guidance: false, description: 'Use for auto-dismissing messages; use Toast instead.'},
223
223
  {guidance: false, description: 'Stack multiple banners of the same status; combine into one.'},
224
+ {guidance: true, description: 'Error/warning render role="alert", info/success role="status"; mount an alert banner on an event, not first paint, so assistive tech announces it.'},
225
+ {guidance: false, description: 'Rely on status color or icon alone; state the status in the title text, since the icon is decorative to a screen reader.'},
224
226
  ],
225
227
  anatomy: [
226
228
  {name: 'Icon', required: true, description: 'Set automatically from status.'},
@@ -19,7 +19,7 @@ export const docs = {
19
19
  {
20
20
  name: 'cite',
21
21
  type: 'ReactNode',
22
- description: 'Optional attribution for the quote. Rendered in a <footer> with <cite>.',
22
+ description: 'Optional attribution for the quote. Rendered in a <cite> element after the quoted content.',
23
23
  },
24
24
  {
25
25
  name: 'xstyle',
@@ -28,17 +28,31 @@ export const docs = {
28
28
  'StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}.',
29
29
  },
30
30
  ],
31
+ playground: {
32
+ defaults: {
33
+ children:
34
+ 'Design is not just what it looks like and feels like. Design is how it works.',
35
+ cite: 'Steve Jobs',
36
+ },
37
+ },
31
38
  theming: {
32
39
  targets: [
33
40
  {className: 'astryx-blockquote', visualProps: []},
34
41
  ],
35
42
  },
36
43
  usage: {
37
- description: 'A styled quotation block with an accent-colored left border and secondary text color. Use to highlight quoted content, testimonials, or excerpts.',
44
+ description: 'A quotation block with a rule on its inline-start edge and secondary text color. Use to highlight quoted content, testimonials, or excerpts. The rule and padding are logical, so they move to the right edge in right-to-left locales.',
45
+ anatomy: [
46
+ {name: 'Rule', required: true, description: 'The border on the inline-start edge, drawn with --color-border-emphasized.'},
47
+ {name: 'Quotation', required: true, description: 'The quoted content, passed as children. Renders inside the <blockquote> element.'},
48
+ {name: 'Attribution', required: false, description: 'The source, passed as cite. Renders as a <cite> element below the quotation.'},
49
+ ],
38
50
  bestPractices: [
39
51
  { guidance: true, description: 'Use for quoted text, testimonials, or highlighted excerpts from external sources.' },
40
52
  { guidance: true, description: 'Provide a cite prop when the source of the quote is known.' },
53
+ { guidance: true, description: 'Pass the source through cite rather than typing it into children, so it renders as a semantic <cite> element that assistive technology can distinguish from the quotation.' },
41
54
  { guidance: false, description: 'Use for callout boxes or informational notes; use Banner for those.' },
55
+ { guidance: false, description: 'Wrap the attribution in your own <footer>. A <footer> inside a <blockquote> becomes a contentinfo document landmark, and a page with several quotes then reports several page footers.' },
42
56
  ],
43
57
  },
44
58
  };
@@ -57,7 +71,7 @@ export const docsZh = {
57
71
  {
58
72
  name: 'cite',
59
73
  type: 'ReactNode',
60
- description: '引用的可选出处。在 <footer> 中以 <cite> 渲染。',
74
+ description: '引用的可选出处。在引用内容之后以 <cite> 元素渲染。',
61
75
  },
62
76
  {
63
77
  name: 'xstyle',
@@ -72,29 +86,38 @@ export const docsZh = {
72
86
  ],
73
87
  },
74
88
  usage: {
75
- description: '带有强调色左边框和次要文本颜色的引用块。用于突出显示引用内容、推荐语或摘录。',
89
+ description: '带有行首边框和次要文本颜色的引用块。用于突出显示引用内容、推荐语或摘录。边框和内边距使用逻辑属性,因此在从右到左的语言环境中会移到右侧。',
90
+ anatomy: [
91
+ {name: '边框', required: true, description: '行首边缘的边框,使用 --color-border-emphasized 绘制。'},
92
+ {name: '引用内容', required: true, description: '通过 children 传入的引用内容,渲染在 <blockquote> 元素内。'},
93
+ {name: '出处', required: false, description: '通过 cite 传入的来源,在引用内容下方以 <cite> 元素渲染。'},
94
+ ],
76
95
  bestPractices: [
77
96
  { guidance: true, description: '用于引用文本、推荐语或来自外部来源的高亮摘录。' },
78
97
  { guidance: true, description: '当引用来源已知时,提供 cite 属性。' },
98
+ { guidance: true, description: '通过 cite 传入来源,而不是写在 children 里,这样它会渲染为语义化的 <cite> 元素,辅助技术可以将其与引用内容区分开。' },
79
99
  { guidance: false, description: '用于提示框或信息说明,应使用 Banner。' },
100
+ { guidance: false, description: '自行用 <footer> 包裹出处。<blockquote> 内的 <footer> 会成为 contentinfo 文档地标,页面上有多个引用时就会报告多个页脚。' },
80
101
  ],
81
102
  },
82
103
  };
83
104
 
84
105
  /** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
85
106
  export const docsDense = {
86
- description: 'styled quotation block w/ accent left border, secondary text, optional citation',
107
+ description: 'quotation block w/ inline-start rule, secondary text, optional citation',
87
108
  usage: {
88
- description: 'A styled quotation block with an accent-colored left border and secondary text color. Use to highlight quoted content, testimonials, or excerpts.',
109
+ description: 'A quotation block with a rule on its inline-start edge and secondary text color. Use to highlight quoted content, testimonials, or excerpts.',
89
110
  bestPractices: [
90
111
  { guidance: true, description: 'Use for quoted text, testimonials, or highlighted excerpts.' },
91
112
  { guidance: true, description: 'Provide cite when source is known.' },
113
+ { guidance: true, description: 'Pass the source via cite, not inside children.' },
92
114
  { guidance: false, description: 'Use for callouts/notes; use Banner instead.' },
115
+ { guidance: false, description: 'Wrap the attribution in <footer>; it becomes a contentinfo landmark.' },
93
116
  ],
94
117
  },
95
118
  propDescriptions: {
96
119
  children: 'blockquote content',
97
- cite: 'optional attribution rendered in <footer><cite>',
120
+ cite: 'optional attribution rendered as <cite> after the quote',
98
121
  xstyle: 'StyleX styles for layout; must be stylex.create() value',
99
122
  },
100
123
  };
@@ -11,6 +11,7 @@
11
11
 
12
12
  import {describe, it, expect, vi} from 'vitest';
13
13
  import {render, screen} from '@testing-library/react';
14
+ import {renderToString} from 'react-dom/server';
14
15
  import {Blockquote} from './Blockquote';
15
16
 
16
17
  describe('Blockquote', () => {
@@ -31,7 +32,6 @@ describe('Blockquote', () => {
31
32
  it('renders without cite by default', () => {
32
33
  render(<Blockquote data-testid="bq">Quote</Blockquote>);
33
34
  const element = screen.getByTestId('bq');
34
- expect(element.querySelector('footer')).toBeNull();
35
35
  expect(element.querySelector('cite')).toBeNull();
36
36
  });
37
37
 
@@ -42,13 +42,34 @@ describe('Blockquote', () => {
42
42
  </Blockquote>,
43
43
  );
44
44
  const element = screen.getByTestId('bq');
45
- const footer = element.querySelector('footer');
46
- expect(footer).toBeInTheDocument();
47
45
  const cite = element.querySelector('cite');
48
46
  expect(cite).toBeInTheDocument();
49
47
  expect(cite).toHaveTextContent('Steve Jobs');
50
48
  });
51
49
 
50
+ it('never wraps the attribution in a landmark element', () => {
51
+ render(
52
+ <Blockquote cite="Steve Jobs" data-testid="bq">
53
+ Quote
54
+ </Blockquote>,
55
+ );
56
+ const element = screen.getByTestId('bq');
57
+ expect(element.querySelector('footer')).toBeNull();
58
+ expect(screen.queryByRole('contentinfo')).toBeNull();
59
+ });
60
+
61
+ it.each([
62
+ ['false', false],
63
+ ['an empty string', ''],
64
+ ])('renders no cite element when cite is %s', (_label, cite) => {
65
+ render(
66
+ <Blockquote cite={cite} data-testid="bq">
67
+ Quote
68
+ </Blockquote>,
69
+ );
70
+ expect(screen.getByTestId('bq').querySelector('cite')).toBeNull();
71
+ });
72
+
52
73
  it('renders cite as ReactNode', () => {
53
74
  render(
54
75
  <Blockquote
@@ -87,4 +108,14 @@ describe('Blockquote', () => {
87
108
  );
88
109
  expect(screen.getByTestId('child-p')).toBeInTheDocument();
89
110
  });
111
+
112
+ it('renders on the server', () => {
113
+ const html = renderToString(
114
+ <Blockquote cite="Steve Jobs">Design is how it works.</Blockquote>,
115
+ );
116
+ expect(html).toContain('<blockquote');
117
+ expect(html).toContain('astryx-blockquote');
118
+ expect(html).toContain('<cite');
119
+ expect(html).toContain('Steve Jobs');
120
+ });
90
121
  });
@@ -17,7 +17,7 @@ import type {ReactNode} from 'react';
17
17
  import type {BaseProps} from '../BaseProps';
18
18
  import * as stylex from '@stylexjs/stylex';
19
19
  import {colorVars, spacingVars, typeScaleVars} from '../theme/tokens.stylex';
20
- import {mergeProps} from '../utils';
20
+ import {isRenderable, mergeProps} from '../utils';
21
21
  import {themeProps} from '../utils/themeProps';
22
22
 
23
23
  export interface BlockquoteProps extends BaseProps<HTMLQuoteElement> {
@@ -26,7 +26,8 @@ export interface BlockquoteProps extends BaseProps<HTMLQuoteElement> {
26
26
  /** Content of the blockquote */
27
27
  children: ReactNode;
28
28
  /**
29
- * Optional attribution for the quote. Rendered in a <footer> with <cite>.
29
+ * Optional attribution for the quote. Rendered in a `<cite>` element after
30
+ * the quoted content.
30
31
  */
31
32
  cite?: ReactNode;
32
33
  }
@@ -38,6 +39,7 @@ const styles = stylex.create({
38
39
  borderInlineStartColor: colorVars['--color-border-emphasized'],
39
40
  paddingInlineStart: spacingVars['--spacing-4'],
40
41
  color: colorVars['--color-text-secondary'],
42
+ overflowWrap: 'break-word',
41
43
  marginInlineStart: 0,
42
44
  marginInlineEnd: 0,
43
45
  marginBlockStart: 0,
@@ -55,8 +57,8 @@ const styles = stylex.create({
55
57
  /**
56
58
  * Blockquote component for displaying quoted content.
57
59
  *
58
- * Renders a semantic `<blockquote>` with an accent-colored left border
59
- * and secondary text color, matching the Astryx visual language.
60
+ * Renders a semantic `<blockquote>` with an inline-start rule and secondary
61
+ * text color, matching the Astryx visual language.
60
62
  *
61
63
  * @example
62
64
  * ```
@@ -83,11 +85,11 @@ export function Blockquote({
83
85
  )}
84
86
  {...props}>
85
87
  {children}
86
- {cite != null && (
87
- <footer>
88
- <cite {...stylex.props(styles.cite)}>{cite}</cite>
89
- </footer>
90
- )}
88
+ {/*
89
+ A bare <cite>, never wrapped in <footer>: <blockquote> is a sectioning
90
+ root, so a <footer> inside it maps to a contentinfo document landmark.
91
+ */}
92
+ {isRenderable(cite) && <cite {...stylex.props(styles.cite)}>{cite}</cite>}
91
93
  </blockquote>
92
94
  );
93
95
  }
@@ -102,14 +102,14 @@ export const docs = {
102
102
  name: 'height',
103
103
  type: "'hug' | 'capped' | 'tall' | number | string",
104
104
  description:
105
- "How tall the sheet is. Named budgets: 'hug' fits its content up to 92% of the viewport, 'capped' is a scrolling mid-height panel (~62%), and 'tall' is a pinned near-full panel (~92%) for content that streams in. Or pass a number (px) / CSS length for a custom budget. Give snapPoints to let the user drag between heights. On shorter viewports the sheet fills the available height. Only a fully expanded 'tall' sheet provides mobile-keyboard accommodation: it stays put and scrolls each focused control above the keyboard. Hug, Capped, numeric and CSS-length heights never do, and a Tall sheet stops doing it the moment the user drags it to a shorter detent — resuming when they drag it back. Outside that state the sheet neither moves nor adds keyboard scroll space, and the browser's own focus reveal is left in place; on iOS that reveal can shift the whole page.",
105
+ "How tall the sheet is. Named budgets: 'hug' fits its content up to 92% of the viewport, 'capped' is a scrolling mid-height panel (~62%), and 'tall' is a pinned near-full panel (~92%) for content that streams in. Or pass a number (px) / CSS length for a custom budget. Give snapPoints to let the user drag between heights. On shorter viewports the sheet fills the available height. Only a fully expanded 'tall' sheet provides mobile-keyboard accommodation: it stays put and scrolls each focused control above the keyboard. Hug, Capped, numeric and CSS-length heights never do, and a Tall sheet stops doing it the moment the user drags it to a shorter detent, resuming when they drag it back. Outside that state the sheet neither moves nor adds keyboard scroll space, and the browser's own focus reveal is left in place; on iOS that reveal can shift the whole page.",
106
106
  default: "'capped'",
107
107
  },
108
108
  {
109
109
  name: 'snapPoints',
110
110
  type: 'ReadonlyArray<number | string>',
111
111
  description:
112
- "Extra heights the sheet can rest at when dragged; its own height is always the tallest stop, and omitting this gives a sheet that only opens and closes. Each stop is the sheet's visible height: a number is a viewport fraction (0.5 is half the screen), '50%' the same in CSS, '320px' an absolute length. A stop of a quarter of the sheet or less is a peek — it slides away instead of reflowing, and thins the scrim.",
112
+ "Extra heights the sheet can rest at when dragged; its own height is always the tallest stop, and omitting this gives a sheet that only opens and closes. Each stop is the sheet's visible height: a number is a viewport fraction (0.5 is half the screen), '50%' the same in CSS, '320px' an absolute length. A stop of a quarter of the sheet or less is a peek: it slides away instead of reflowing, and thins the scrim.",
113
113
  },
114
114
  {
115
115
  name: 'hasScrim',
@@ -269,7 +269,7 @@ export const docs = {
269
269
  /** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
270
270
  export const docsDense = {
271
271
  description:
272
- 'mobile touch sheet rising from the bottom edge (native <dialog>): grab handle, opt-in transform-based drag-to-resize snap points (snapPoints: viewport fraction, percent or px length), scrolling area resizes to the snapped visible height on release (a peek stop — a quarter of the sheet or less — keeps the full height and slides instead), Dialog-aligned dismissal purpose (info/form/required), purpose-gated swipe-to-dismiss, fully-expanded Tall visual-viewport mobile-keyboard handling, named height scale, modal (default) or non-modal (hasScrim={false}) presentation',
272
+ 'mobile touch sheet rising from the bottom edge (native <dialog>): grab handle, opt-in transform-based drag-to-resize snap points (snapPoints: viewport fraction, percent or px length), scrolling area resizes to the snapped visible height on release (a peek stop, a quarter of the sheet or less, keeps the full height and slides instead), Dialog-aligned dismissal purpose (info/form/required), purpose-gated swipe-to-dismiss, fully-expanded Tall visual-viewport mobile-keyboard handling, named height scale, modal (default) or non-modal (hasScrim={false}) presentation',
273
273
  usage: {
274
274
  description:
275
275
  'Mobile touch surface for filters, actions, forms, and detail views that should rise from the bottom of the viewport; use BottomSheetSwitcher for multi-step flows.',
@@ -2262,4 +2262,28 @@ describe('BottomSheet', () => {
2262
2262
  expect(screen.getByRole('dialog')).toBeInTheDocument();
2263
2263
  });
2264
2264
  });
2265
+
2266
+ describe('container padding isolation', () => {
2267
+ it('resets container padding custom properties to 0px on the sheet panel element', () => {
2268
+ render(
2269
+ <BottomSheet isOpen onOpenChange={() => {}} label="Filters">
2270
+ Content
2271
+ </BottomSheet>,
2272
+ );
2273
+ const sheet = getSheet();
2274
+ const computed = window.getComputedStyle(sheet);
2275
+ expect(
2276
+ computed.getPropertyValue('--container-padding-inline-start'),
2277
+ ).toBe('0px');
2278
+ expect(computed.getPropertyValue('--container-padding-inline-end')).toBe(
2279
+ '0px',
2280
+ );
2281
+ expect(computed.getPropertyValue('--container-padding-block-start')).toBe(
2282
+ '0px',
2283
+ );
2284
+ expect(computed.getPropertyValue('--container-padding-block-end')).toBe(
2285
+ '0px',
2286
+ );
2287
+ });
2288
+ });
2265
2289
  });
@@ -237,4 +237,95 @@ describe('BottomSheetPanel', () => {
237
237
  expect(panelRef).toHaveBeenLastCalledWith(null);
238
238
  expect(panelRef).toHaveBeenCalledTimes(2);
239
239
  });
240
+
241
+ it('floats the handle bar over content that starts at the sheet top edge', () => {
242
+ renderPanel({kind: 'open', entering: false});
243
+
244
+ const bar = getPanel().querySelector<HTMLElement>(
245
+ 'div[aria-hidden="true"]',
246
+ );
247
+ if (bar == null) {
248
+ throw new Error('handle bar not found');
249
+ }
250
+ const body = bar.nextElementSibling as HTMLElement;
251
+
252
+ // Out of flow: the bar costs the content no layout space. That is the
253
+ // point of it -- the content sits closer to the sheet's top edge, and
254
+ // scrolled content passes beneath the pill rather than stopping at a
255
+ // hard edge.
256
+ expect(getComputedStyle(bar).position).toBe('absolute');
257
+
258
+ // So the body must not reserve room for it. A top padding here would
259
+ // push the content back down and undo the change.
260
+ expect(getComputedStyle(body).paddingTop).toBe('');
261
+ });
262
+
263
+ // The pill is only legible over the content riding up beneath it because
264
+ // the bar carries a backdrop; without it, text runs straight through the
265
+ // pill. jsdom's StyleX runtime does not inject the rule, so assert on the
266
+ // style definition itself (same approach as AspectRatio's reset.css tests).
267
+ it('backs the floating handle bar with a surface gradient', async () => {
268
+ const fs = await import('fs');
269
+ const path = await import('path');
270
+ const source = fs.readFileSync(
271
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
272
+ 'utf-8',
273
+ );
274
+
275
+ const handleBar = source.match(/handleBar: \{([\s\S]*?)\n {2}\},/);
276
+ expect(handleBar).not.toBeNull();
277
+ expect(handleBar![1]).toContain("position: 'absolute'");
278
+ expect(handleBar![1]).toContain('linear-gradient(to bottom');
279
+ expect(handleBar![1]).toContain("colorVars['--color-background-surface']");
280
+ });
281
+
282
+ // In dark mode the surface fill and the scrim sit a few RGB steps apart and
283
+ // the drop shadow is black on near-black, so the fill alone leaves the
284
+ // sheet's left and right edges invisible against the scrim. A hairline on
285
+ // the scrim-facing edges is what draws them. Asserted on the style
286
+ // definition for the same reason as the test above.
287
+ it('draws a hairline on the three edges that face the scrim', async () => {
288
+ const fs = await import('fs');
289
+ const path = await import('path');
290
+ const source = fs.readFileSync(
291
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
292
+ 'utf-8',
293
+ );
294
+
295
+ const sheet = source.match(/\n {2}sheet: \{([\s\S]*?)\n {2}\},/);
296
+ expect(sheet).not.toBeNull();
297
+ for (const edge of [
298
+ 'borderBlockStart',
299
+ 'borderInlineStart',
300
+ 'borderInlineEnd',
301
+ ]) {
302
+ expect(sheet![1]).toContain(`${edge}Width: borderVars['--border-width']`);
303
+ expect(sheet![1]).toContain(`${edge}Style: 'solid'`);
304
+ expect(sheet![1]).toContain(`${edge}Color: colorVars['--color-border']`);
305
+ }
306
+ // The block-end edge sits below the viewport, under the overscroll
307
+ // padding, so it carries no hairline to draw.
308
+ expect(sheet![1]).not.toContain('borderBlockEndWidth');
309
+ });
310
+
311
+ // A theme that packs an inset ring into --shadow-high (the bundled themes all
312
+ // add one in dark mode) draws it just inside the sheet, where an opaque
313
+ // content wrapper such as Section paints over it -- so it showed only in the
314
+ // gap below the content and the side edges appeared to change width partway
315
+ // down. The scrolling body paints the surface across the whole inner box to
316
+ // hide the ring evenly.
317
+ it('paints the surface across the scrolling body so the edge stays uniform', async () => {
318
+ const fs = await import('fs');
319
+ const path = await import('path');
320
+ const source = fs.readFileSync(
321
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
322
+ 'utf-8',
323
+ );
324
+
325
+ const body = source.match(/\n {2}body: \{([\s\S]*?)\n {2}\},/);
326
+ expect(body).not.toBeNull();
327
+ expect(body![1]).toContain(
328
+ "backgroundColor: colorVars['--color-background-surface']",
329
+ );
330
+ });
240
331
  });
@@ -34,6 +34,7 @@ import * as stylex from '@stylexjs/stylex';
34
34
  import type {BaseProps} from '../BaseProps';
35
35
  import {useDevWarning} from '../hooks';
36
36
  import {
37
+ borderVars,
37
38
  colorVars,
38
39
  durationVars,
39
40
  easeVars,
@@ -43,6 +44,7 @@ import {
43
44
  spacingVars,
44
45
  } from '../theme/tokens.stylex';
45
46
  import {mergeProps, themeProps} from '../utils';
47
+ import {overlayPaddingReset} from '../Layout/padding.stylex';
46
48
  import {
47
49
  isValidSnapPoint,
48
50
  resolveSnapPoints,
@@ -64,6 +66,11 @@ export type {BottomSheetSnapPoint};
64
66
  const OVERSCROLL_PADDING = 48;
65
67
  const MOBILE_KEYBOARD_BOTTOM_CLEARANCE = 48;
66
68
  const TRANSITION_BACKSTOP_BUFFER_MS = 50;
69
+ // The floating handle bar's height. The bar is out of flow, so this is not
70
+ // layout space the content pays for -- the pill (4px, centered) lands 10-14px
71
+ // from the sheet's top edge, inside the space a content wrapper's own top
72
+ // padding already provides.
73
+ const HANDLE_BAR_HEIGHT = spacingVars['--spacing-6'];
67
74
 
68
75
  // Measure the same viewport the height budgets are written against. Those are
69
76
  // `dvh`, which the virtual keyboard does not shrink, so reading
@@ -97,12 +104,31 @@ const styles = stylex.create({
97
104
  sheet: {
98
105
  pointerEvents: 'auto',
99
106
  boxSizing: 'border-box',
107
+ // Containing block for the floating handle bar, which is lifted out of
108
+ // flow so the scrolling body reaches the sheet's top edge.
109
+ position: 'relative',
100
110
  display: 'flex',
101
111
  flexDirection: 'column',
102
112
  minHeight: 0,
103
113
  width: '100%',
104
114
  maxWidth: 640,
105
115
  backgroundColor: colorVars['--color-background-surface'],
116
+ // Hairline on the three edges that face the scrim. The surface fill alone
117
+ // separates sheet from scrim in light mode, but not in dark: there the two
118
+ // sit within a few RGB steps of each other and the drop shadow is black on
119
+ // near-black, so the sheet's left and right edges disappear. Same
120
+ // treatment MobileNav gives its scrim-facing edge. The block-end edge is
121
+ // deliberately left bare; it sits below the viewport, under the overscroll
122
+ // padding.
123
+ borderBlockStartWidth: borderVars['--border-width'],
124
+ borderBlockStartStyle: 'solid',
125
+ borderBlockStartColor: colorVars['--color-border'],
126
+ borderInlineStartWidth: borderVars['--border-width'],
127
+ borderInlineStartStyle: 'solid',
128
+ borderInlineStartColor: colorVars['--color-border'],
129
+ borderInlineEndWidth: borderVars['--border-width'],
130
+ borderInlineEndStyle: 'solid',
131
+ borderInlineEndColor: colorVars['--color-border'],
106
132
  borderStartStartRadius: radiusVars['--radius-container'],
107
133
  borderStartEndRadius: radiusVars['--radius-container'],
108
134
  boxShadow: shadowVars['--shadow-high'],
@@ -133,13 +159,28 @@ const styles = stylex.create({
133
159
  pointerEvents: 'none',
134
160
  },
135
161
  handleBar: {
136
- flexShrink: 0,
162
+ // Floats over the body rather than taking a row in the flex column. The
163
+ // content starts at the sheet's top edge and rides up under the pill --
164
+ // both moving it closer to the top and letting scrolled content pass
165
+ // beneath instead of stopping at a hard edge.
166
+ position: 'absolute',
167
+ insetBlockStart: 0,
168
+ insetInlineStart: 0,
169
+ insetInlineEnd: 0,
170
+ zIndex: 1,
137
171
  display: 'flex',
138
172
  alignItems: 'center',
139
173
  justifyContent: 'center',
140
- height: spacingVars['--spacing-12'],
174
+ height: HANDLE_BAR_HEIGHT,
175
+ // Keeps the pill legible over whatever sits or scrolls under it: opaque
176
+ // surface behind the pill itself, fading out across the lower half so the
177
+ // content emerging below has no visible cut line.
178
+ backgroundImage: `linear-gradient(to bottom, ${colorVars['--color-background-surface']} 60%, transparent)`,
141
179
  touchAction: 'none',
142
- cursor: 'grab',
180
+ cursor: {
181
+ default: 'grab',
182
+ ':is(:disabled,[aria-disabled="true"])': 'default',
183
+ },
143
184
  },
144
185
  handlePill: {
145
186
  width: sizeVars['--size-element-lg'],
@@ -154,6 +195,19 @@ const styles = stylex.create({
154
195
  overflowY: 'auto',
155
196
  overscrollBehavior: 'none',
156
197
  touchAction: 'pan-y',
198
+ // The scrolling area paints the surface itself, covering the sheet's whole
199
+ // inner box. Without it the sheet's edge is not uniform: a theme that packs
200
+ // an inset ring into --shadow-high (the bundled themes all add one in dark
201
+ // mode) draws that ring just inside the sheet, where an opaque content
202
+ // wrapper such as Section paints over it -- so the ring shows only in the
203
+ // gap below where the content ends, and the sheet's side edges appear to
204
+ // change width partway down. Painting the surface here hides the ring
205
+ // evenly, leaving the border below as the sheet's one edge.
206
+ backgroundColor: colorVars['--color-background-surface'],
207
+ // No reserve for the handle bar: it floats, so the content starts at the
208
+ // sheet's top edge and rides up under the pill. The pill is 4px centered
209
+ // in a 24px band, so it occupies only 10-14px from the edge -- inside the
210
+ // space a content wrapper's own top padding already provides.
157
211
  paddingBlockEnd: 0,
158
212
  },
159
213
  tallKeyboardBody: {
@@ -556,6 +610,7 @@ export function BottomSheetPanel({
556
610
  themeProps('bottom-sheet'),
557
611
  stylex.props(
558
612
  styles.sheet,
613
+ overlayPaddingReset.reset,
559
614
  height === 'hug' ? styles.hugHeight : styles.budget,
560
615
  isClosing && styles.sheetClosing,
561
616
  isFading && styles.sheetFading,