@astryxdesign/core 0.7.5 → 0.7.6-canary.0ef60dd

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 (559) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/Badge/Badge.d.ts.map +1 -1
  7. package/dist/Badge/Badge.js +7 -1
  8. package/dist/BottomSheet/BottomSheet.js +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  11. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  12. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  13. package/dist/BottomSheet/useSheetGestures.js +30 -3
  14. package/dist/Button/Button.js +4 -4
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  17. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  18. package/dist/ClickableCard/ClickableCard.js +37 -4
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +12 -4
  21. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  22. package/dist/DateInput/MonthScroller.js +101 -15
  23. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  24. package/dist/DateInput/TouchDateField.js +8 -3
  25. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  26. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  27. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  28. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  29. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  30. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  31. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  34. package/dist/Dialog/Dialog.d.ts.map +1 -1
  35. package/dist/Dialog/Dialog.js +34 -9
  36. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  37. package/dist/Dialog/DialogHeader.js +5 -4
  38. package/dist/Divider/Divider.d.ts.map +1 -1
  39. package/dist/Divider/Divider.js +2 -1
  40. package/dist/Drawer/Drawer.d.ts +1 -0
  41. package/dist/Drawer/Drawer.d.ts.map +1 -1
  42. package/dist/Drawer/Drawer.js +7 -2
  43. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  44. package/dist/DropdownMenu/DropdownMenu.js +7 -3
  45. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  46. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  47. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
  48. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
  50. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  51. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +2 -1
  54. package/dist/Field/FieldLabel.d.ts +1 -1
  55. package/dist/Field/FieldLabel.d.ts.map +1 -1
  56. package/dist/Field/FieldLabel.js +4 -4
  57. package/dist/Field/InputClearButton.js +3 -3
  58. package/dist/Field/PanelSearchInput.js +2 -2
  59. package/dist/FieldStatus/FieldStatus.js +2 -2
  60. package/dist/FileInput/FileInput.d.ts.map +1 -1
  61. package/dist/FileInput/FileInput.js +7 -1
  62. package/dist/Heading/Heading.js +1 -1
  63. package/dist/Icon/Icon.js +1 -1
  64. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  65. package/dist/Indicator/CheckIndicator.js +2 -1
  66. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  67. package/dist/Indicator/CheckboxIndicator.js +3 -1
  68. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  69. package/dist/Indicator/RadioIndicator.js +3 -1
  70. package/dist/Item/Item.d.ts.map +1 -1
  71. package/dist/Item/Item.js +2 -1
  72. package/dist/Item/ItemSwipeLayer.js +1 -1
  73. package/dist/Layer/popoverOperations.d.ts +27 -0
  74. package/dist/Layer/popoverOperations.d.ts.map +1 -0
  75. package/dist/Layer/popoverOperations.js +159 -0
  76. package/dist/Layer/useLayer.d.ts +1 -1
  77. package/dist/Layer/useLayer.d.ts.map +1 -1
  78. package/dist/Layer/useLayer.js +93 -59
  79. package/dist/Layout/Layout.js +1 -1
  80. package/dist/Layout/LayoutContent.js +1 -1
  81. package/dist/Layout/LayoutFooter.js +1 -1
  82. package/dist/Layout/LayoutHeader.js +1 -1
  83. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  84. package/dist/Lightbox/Lightbox.js +5 -0
  85. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  86. package/dist/List/List.js +1 -1
  87. package/dist/List/ListItem.js +3 -3
  88. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  89. package/dist/MobileNav/MobileNav.js +7 -2
  90. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
  91. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  92. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  93. package/dist/MultiSelector/MultiSelector.js +26 -10
  94. package/dist/NumberInput/NumberInput.d.ts +3 -1
  95. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  96. package/dist/NumberInput/NumberInput.js +3 -3
  97. package/dist/NumberInput/numberParser.d.ts +3 -0
  98. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  99. package/dist/NumberInput/numberParser.js +7 -44
  100. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  101. package/dist/ProgressBar/ProgressBar.js +16 -12
  102. package/dist/Resizable/useResizable.d.ts.map +1 -1
  103. package/dist/Resizable/useResizable.js +28 -2
  104. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  105. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  106. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  107. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  108. package/dist/Selector/useSelectorPresentation.js +20 -4
  109. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  110. package/dist/Skeleton/Skeleton.js +1 -1
  111. package/dist/Slider/Slider.d.ts.map +1 -1
  112. package/dist/Slider/Slider.js +10 -2
  113. package/dist/Spinner/Spinner.d.ts.map +1 -1
  114. package/dist/Spinner/Spinner.js +3 -3
  115. package/dist/Stack/Stack.js +1 -1
  116. package/dist/Stack/StackItem.js +1 -1
  117. package/dist/TabList/TabList.d.ts.map +1 -1
  118. package/dist/TabList/TabList.js +16 -1
  119. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  120. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  121. package/dist/Text/Text.js +1 -1
  122. package/dist/Text/useTruncation.d.ts.map +1 -1
  123. package/dist/Text/useTruncation.js +23 -5
  124. package/dist/TextArea/TextArea.d.ts +1 -1
  125. package/dist/TextArea/TextArea.d.ts.map +1 -1
  126. package/dist/TextArea/TextArea.js +13 -2
  127. package/dist/TextInput/TextInput.d.ts.map +1 -1
  128. package/dist/TextInput/TextInput.js +3 -3
  129. package/dist/TimeInput/TimeInput.d.ts +6 -2
  130. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  131. package/dist/TimeInput/TimeInput.js +21 -14
  132. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  133. package/dist/TimeInput/TouchTimeField.js +5 -4
  134. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  135. package/dist/Timestamp/Timestamp.js +2 -2
  136. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  137. package/dist/Toast/Toast.d.ts.map +1 -1
  138. package/dist/Toast/Toast.js +60 -20
  139. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  140. package/dist/Toast/ToastViewport.js +82 -51
  141. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  142. package/dist/Tokenizer/Tokenizer.js +13 -2
  143. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  144. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  145. package/dist/Toolbar/Toolbar.js +2 -1
  146. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  147. package/dist/TopNav/TopNavMegaMenu.js +3 -0
  148. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  149. package/dist/TopNav/TopNavMenu.js +1 -0
  150. package/dist/Typeahead/Typeahead.d.ts +1 -1
  151. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  152. package/dist/Typeahead/Typeahead.js +9 -1
  153. package/dist/astryx.css +20 -4
  154. package/dist/gestures/GestureArena.d.ts +6 -0
  155. package/dist/gestures/GestureArena.d.ts.map +1 -1
  156. package/dist/gestures/GestureArena.js +39 -0
  157. package/dist/gestures/GestureHost.d.ts +352 -0
  158. package/dist/gestures/GestureHost.d.ts.map +1 -0
  159. package/dist/gestures/GestureHost.js +1773 -0
  160. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  161. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  162. package/dist/gestures/GestureRecognizer.js +16 -0
  163. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  164. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  165. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  166. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  167. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  168. package/dist/gestures/PanGestureRecognizer.js +3 -0
  169. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  170. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  171. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  172. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  173. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  174. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  175. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  176. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  177. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  178. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  179. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  180. package/dist/gestures/TapGestureRecognizer.js +3 -0
  181. package/dist/gestures/VelocityTracker.d.ts +6 -2
  182. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  183. package/dist/gestures/VelocityTracker.js +7 -3
  184. package/dist/gestures/haptics.d.ts +65 -0
  185. package/dist/gestures/haptics.d.ts.map +1 -0
  186. package/dist/gestures/haptics.js +182 -0
  187. package/dist/gestures/nativeScroll.d.ts +42 -0
  188. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  189. package/dist/gestures/nativeScroll.js +228 -0
  190. package/dist/gestures/touchAction.d.ts +60 -0
  191. package/dist/gestures/touchAction.d.ts.map +1 -0
  192. package/dist/gestures/touchAction.js +157 -0
  193. package/dist/gestures/touchActionRule.d.ts +34 -0
  194. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  195. package/dist/gestures/touchActionRule.js +140 -0
  196. package/dist/gestures/types.d.ts +18 -7
  197. package/dist/gestures/types.d.ts.map +1 -1
  198. package/dist/gestures/types.js +22 -2
  199. package/dist/hooks/index.d.ts +1 -1
  200. package/dist/hooks/index.d.ts.map +1 -1
  201. package/dist/hooks/modalAnnounce.d.ts.map +1 -1
  202. package/dist/hooks/modalAnnounce.js +107 -61
  203. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  204. package/dist/hooks/useAdaptivePresentation.js +4 -1
  205. package/dist/hooks/useAnnounce.d.ts +101 -42
  206. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  207. package/dist/hooks/useAnnounce.js +161 -75
  208. package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
  209. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  210. package/dist/hooks/useAnnounceRenderedText.js +5 -3
  211. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  212. package/dist/hooks/useBusyAnnouncement.js +6 -12
  213. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  214. package/dist/hooks/useMenuHover.js +12 -1
  215. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  216. package/dist/i18n/generated-locales/en.generated.js +5 -0
  217. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  218. package/dist/i18n/generated-locales/pseudo.generated.js +5 -0
  219. package/dist/motion/animator.d.ts +7 -1
  220. package/dist/motion/animator.d.ts.map +1 -1
  221. package/dist/motion/animator.js +119 -27
  222. package/dist/theme/defineTheme.d.ts +19 -5
  223. package/dist/theme/defineTheme.d.ts.map +1 -1
  224. package/dist/theme/defineTheme.js +64 -4
  225. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  226. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  227. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  228. package/dist/theme/domainTokens/index.d.ts +5 -4
  229. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  230. package/dist/theme/domainTokens/index.js +5 -9
  231. package/dist/theme/index.d.ts +1 -0
  232. package/dist/theme/index.d.ts.map +1 -1
  233. package/dist/theme/motionKnobs.d.ts +56 -0
  234. package/dist/theme/motionKnobs.d.ts.map +1 -0
  235. package/dist/theme/motionKnobs.js +48 -0
  236. package/dist/theme/resolveThemeValues.d.ts +11 -4
  237. package/dist/theme/resolveThemeValues.d.ts.map +1 -1
  238. package/dist/theme/resolveThemeValues.js +40 -4
  239. package/dist/theme/themeAdaptations.d.ts +7 -3
  240. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  241. package/dist/theme/themeAdaptations.js +36 -7
  242. package/dist/theme/themeMotion.d.ts +96 -0
  243. package/dist/theme/themeMotion.d.ts.map +1 -0
  244. package/dist/theme/themeMotion.js +177 -0
  245. package/dist/theme/tokenReferences.d.ts +31 -0
  246. package/dist/theme/tokenReferences.d.ts.map +1 -0
  247. package/dist/theme/tokenReferences.js +132 -0
  248. package/dist/theme/tokens.d.ts +1 -0
  249. package/dist/theme/tokens.d.ts.map +1 -1
  250. package/dist/theme/tokens.js +9 -129
  251. package/dist/theme/tokens.stylex.d.ts +12 -0
  252. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  253. package/dist/theme/tokens.stylex.js +12 -0
  254. package/dist/utils/focusReturn.d.ts +17 -1
  255. package/dist/utils/focusReturn.d.ts.map +1 -1
  256. package/dist/utils/focusReturn.js +11 -4
  257. package/dist/utils/getNodeText.d.ts +8 -0
  258. package/dist/utils/getNodeText.d.ts.map +1 -0
  259. package/dist/utils/getNodeText.js +37 -0
  260. package/dist/utils/interactionModality.d.ts +11 -2
  261. package/dist/utils/interactionModality.d.ts.map +1 -1
  262. package/dist/utils/interactionModality.js +58 -5
  263. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  264. package/dist/utils/interactionOverlay.stylex.js +17 -8
  265. package/dist/utils/plainDate.d.ts +2 -0
  266. package/dist/utils/plainDate.d.ts.map +1 -1
  267. package/dist/utils/plainDate.js +46 -3
  268. package/dist/utils/presentation.d.ts +50 -0
  269. package/dist/utils/presentation.d.ts.map +1 -0
  270. package/dist/utils/presentation.js +125 -0
  271. package/dist/utils/pressFeedback.d.ts.map +1 -1
  272. package/dist/utils/pressFeedback.js +20 -1
  273. package/dist/utils/primaryClick.d.ts +11 -0
  274. package/dist/utils/primaryClick.d.ts.map +1 -0
  275. package/dist/utils/primaryClick.js +25 -0
  276. package/dist/utils/timeFormat.d.ts +47 -0
  277. package/dist/utils/timeFormat.d.ts.map +1 -0
  278. package/dist/utils/timeFormat.js +240 -0
  279. package/dist/utils/touchPresentation.d.ts.map +1 -1
  280. package/dist/utils/touchPresentation.js +5 -3
  281. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  282. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  283. package/dist/utils/touchPresentation.stylex.js +33 -0
  284. package/dist/utils/unicodeDigits.d.ts +20 -0
  285. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  286. package/dist/utils/unicodeDigits.js +70 -0
  287. package/locales/en.json +23 -3
  288. package/locales/pseudo.json +15 -0
  289. package/package.json +5 -4
  290. package/src/AlertDialog/AlertDialog.tsx +6 -5
  291. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  292. package/src/Avatar/Avatar.spec.md +22 -22
  293. package/src/Avatar/Avatar.tsx +18 -1
  294. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  295. package/src/Badge/Badge.doc.mjs +1 -1
  296. package/src/Badge/Badge.tsx +19 -2
  297. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  298. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  299. package/src/BottomSheet/BottomSheet.tsx +1 -1
  300. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  301. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  302. package/src/BottomSheet/useSheetGestures.ts +37 -6
  303. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  304. package/src/Button/Button.tsx +4 -4
  305. package/src/Calendar/Calendar.spec.md +87 -48
  306. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  307. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  308. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  309. package/src/ClickableCard/ClickableCard.test.tsx +96 -0
  310. package/src/ClickableCard/ClickableCard.tsx +48 -4
  311. package/src/Collapsible/Collapsible.spec.md +2 -0
  312. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  313. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  314. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  315. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  316. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  317. package/src/DateInput/DateInput.test.tsx +115 -3
  318. package/src/DateInput/DateInput.tsx +29 -14
  319. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  320. package/src/DateInput/MonthScroller.tsx +106 -20
  321. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  322. package/src/DateInput/TouchDateField.tsx +14 -3
  323. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  324. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  325. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  326. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  327. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  328. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  329. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  330. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  331. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  332. package/src/Dialog/Dialog.test.tsx +216 -2
  333. package/src/Dialog/Dialog.tsx +42 -9
  334. package/src/Dialog/DialogHeader.tsx +5 -4
  335. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  336. package/src/Divider/Divider.tsx +2 -1
  337. package/src/Drawer/Drawer.spec.md +111 -80
  338. package/src/Drawer/Drawer.test.tsx +35 -0
  339. package/src/Drawer/Drawer.tsx +13 -2
  340. package/src/Drawer/DrawerHeader.spec.md +181 -0
  341. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  342. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  343. package/src/DropdownMenu/DropdownMenu.tsx +10 -3
  344. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  345. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  346. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  347. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  348. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
  349. package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
  350. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  351. package/src/EmptyState/EmptyState.tsx +2 -1
  352. package/src/Field/FieldLabel.tsx +5 -4
  353. package/src/Field/InputClearButton.tsx +3 -3
  354. package/src/Field/PanelSearchInput.tsx +2 -2
  355. package/src/FieldStatus/FieldStatus.tsx +2 -2
  356. package/src/FileInput/FileInput.test.tsx +21 -0
  357. package/src/FileInput/FileInput.tsx +4 -1
  358. package/src/FormLayout/FormLayout.spec.md +7 -8
  359. package/src/Heading/Heading.tsx +1 -1
  360. package/src/Icon/Icon.tsx +1 -1
  361. package/src/Indicator/CheckIndicator.spec.md +47 -42
  362. package/src/Indicator/CheckIndicator.tsx +2 -1
  363. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  364. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  365. package/src/Indicator/Indicator.spec.md +188 -0
  366. package/src/Indicator/RadioIndicator.tsx +3 -1
  367. package/src/Item/Item.tsx +2 -1
  368. package/src/Item/ItemSwipeLayer.tsx +1 -1
  369. package/src/Kbd/Kbd.spec.md +81 -38
  370. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
  371. package/src/Layer/popoverOperations.test.ts +205 -0
  372. package/src/Layer/popoverOperations.ts +172 -0
  373. package/src/Layer/useLayer.test.tsx +348 -1
  374. package/src/Layer/useLayer.tsx +118 -71
  375. package/src/Layout/Layout.spec.md +127 -131
  376. package/src/Layout/Layout.tsx +1 -1
  377. package/src/Layout/LayoutContent.spec.md +205 -0
  378. package/src/Layout/LayoutContent.tsx +1 -1
  379. package/src/Layout/LayoutFooter.spec.md +190 -0
  380. package/src/Layout/LayoutFooter.tsx +1 -1
  381. package/src/Layout/LayoutHeader.spec.md +193 -0
  382. package/src/Layout/LayoutHeader.tsx +1 -1
  383. package/src/Layout/LayoutPanel.spec.md +194 -0
  384. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  385. package/src/Lightbox/Lightbox.test.tsx +18 -0
  386. package/src/Lightbox/Lightbox.tsx +5 -0
  387. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  388. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  389. package/src/List/List.tsx +1 -1
  390. package/src/List/ListItem.spec.md +205 -0
  391. package/src/List/ListItem.tsx +3 -3
  392. package/src/MetadataList/MetadataList.spec.md +187 -0
  393. package/src/MetadataList/MetadataListItem.spec.md +165 -0
  394. package/src/MobileNav/MobileNav.test.tsx +15 -0
  395. package/src/MobileNav/MobileNav.tsx +9 -1
  396. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  397. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  398. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
  399. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  400. package/src/MultiSelector/MultiSelector.test.tsx +270 -1
  401. package/src/MultiSelector/MultiSelector.tsx +25 -10
  402. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  403. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  404. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  405. package/src/NumberInput/NumberInput.spec.md +59 -37
  406. package/src/NumberInput/NumberInput.tsx +13 -3
  407. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  408. package/src/NumberInput/numberParser.ts +7 -45
  409. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  410. package/src/ProgressBar/ProgressBar.tsx +58 -10
  411. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  412. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  413. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  414. package/src/Resizable/useResizable.test.ts +43 -0
  415. package/src/Resizable/useResizable.ts +32 -9
  416. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  417. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  418. package/src/Selector/Selector.test.tsx +90 -1
  419. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  420. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  421. package/src/Selector/useSelectorPresentation.ts +31 -3
  422. package/src/SideNav/SideNav.test.tsx +27 -0
  423. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  424. package/src/Skeleton/Skeleton.tsx +3 -1
  425. package/src/Slider/Slider.test.tsx +31 -0
  426. package/src/Slider/Slider.tsx +8 -3
  427. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  428. package/src/Spinner/Spinner.tsx +5 -3
  429. package/src/Stack/Stack.tsx +1 -1
  430. package/src/Stack/StackItem.tsx +1 -1
  431. package/src/TabList/TabList.test.tsx +58 -4
  432. package/src/TabList/TabList.tsx +16 -1
  433. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  434. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  435. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  436. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  437. package/src/Text/Text.tsx +1 -1
  438. package/src/Text/useTruncation.test.tsx +128 -1
  439. package/src/Text/useTruncation.ts +23 -5
  440. package/src/TextArea/TextArea.spec.md +10 -13
  441. package/src/TextArea/TextArea.test.tsx +60 -0
  442. package/src/TextArea/TextArea.tsx +15 -1
  443. package/src/TextInput/TextInput.test.tsx +21 -0
  444. package/src/TextInput/TextInput.tsx +3 -1
  445. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  446. package/src/TimeInput/Presentation.test.tsx +21 -0
  447. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  448. package/src/TimeInput/TimeInput.spec.md +45 -48
  449. package/src/TimeInput/TimeInput.test.tsx +76 -0
  450. package/src/TimeInput/TimeInput.tsx +36 -18
  451. package/src/TimeInput/TouchTimeField.tsx +4 -6
  452. package/src/Timestamp/Timestamp.spec.md +6 -12
  453. package/src/Timestamp/Timestamp.test.tsx +30 -5
  454. package/src/Timestamp/Timestamp.tsx +6 -2
  455. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  456. package/src/Toast/Toast.spec.md +14 -20
  457. package/src/Toast/Toast.tsx +104 -36
  458. package/src/Toast/ToastViewport.test.tsx +719 -1
  459. package/src/Toast/ToastViewport.tsx +100 -50
  460. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  461. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  462. package/src/Tokenizer/Tokenizer.tsx +14 -2
  463. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  464. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  465. package/src/Toolbar/Toolbar.spec.md +5 -6
  466. package/src/Toolbar/Toolbar.test.tsx +30 -0
  467. package/src/Toolbar/Toolbar.tsx +2 -1
  468. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  469. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  470. package/src/TopNav/TopNav.test.tsx +27 -0
  471. package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
  472. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  473. package/src/TopNav/TopNavMenu.test.tsx +17 -0
  474. package/src/TopNav/TopNavMenu.tsx +1 -0
  475. package/src/Typeahead/Typeahead.test.tsx +40 -0
  476. package/src/Typeahead/Typeahead.tsx +12 -1
  477. package/src/__tests__/pressState.ts +8 -4
  478. package/src/gestures/GestureArena.ts +35 -0
  479. package/src/gestures/GestureHost.ts +2065 -0
  480. package/src/gestures/GestureRecognizer.ts +18 -0
  481. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  482. package/src/gestures/PanGestureRecognizer.ts +4 -0
  483. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  484. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  485. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  486. package/src/gestures/TapGestureRecognizer.ts +4 -0
  487. package/src/gestures/VelocityTracker.ts +10 -3
  488. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  489. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  490. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  491. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  492. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  493. package/src/gestures/__tests__/arena.test.ts +5 -0
  494. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  495. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  496. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  497. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  498. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  499. package/src/gestures/__tests__/haptics.test.ts +187 -0
  500. package/src/gestures/__tests__/harness.ts +21 -2
  501. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  502. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  503. package/src/gestures/haptics.ts +203 -0
  504. package/src/gestures/nativeScroll.ts +277 -0
  505. package/src/gestures/touchAction.ts +191 -0
  506. package/src/gestures/touchActionRule.ts +157 -0
  507. package/src/gestures/types.ts +30 -9
  508. package/src/hooks/index.ts +5 -1
  509. package/src/hooks/modalAnnounce.ts +129 -72
  510. package/src/hooks/useAdaptivePresentation.ts +4 -1
  511. package/src/hooks/useAnnounce.doc.mjs +35 -3
  512. package/src/hooks/useAnnounce.replace.test.tsx +516 -0
  513. package/src/hooks/useAnnounce.ts +209 -102
  514. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
  515. package/src/hooks/useAnnounceRenderedText.ts +17 -3
  516. package/src/hooks/useBusyAnnouncement.ts +7 -10
  517. package/src/hooks/useMenuHover.ts +15 -1
  518. package/src/i18n/generated-locales/en.generated.ts +6 -0
  519. package/src/i18n/generated-locales/pseudo.generated.ts +6 -0
  520. package/src/motion/README.md +11 -1
  521. package/src/motion/animator.test.ts +151 -3
  522. package/src/motion/animator.ts +149 -33
  523. package/src/theme/Theme.doc.mjs +1 -1
  524. package/src/theme/defineTheme.ts +117 -8
  525. package/src/theme/derivedVarRegistry.test.ts +4 -0
  526. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  527. package/src/theme/domainTokens/index.ts +6 -10
  528. package/src/theme/expandMotionScale.test.ts +1 -1
  529. package/src/theme/index.ts +5 -0
  530. package/src/theme/motionKnobs.ts +82 -0
  531. package/src/theme/resolveThemeValues.ts +68 -6
  532. package/src/theme/themeAdaptations.ts +52 -12
  533. package/src/theme/themeMotion.test.ts +620 -0
  534. package/src/theme/themeMotion.ts +221 -0
  535. package/src/theme/tokenReferences.ts +149 -0
  536. package/src/theme/tokens.stylex.ts +10 -0
  537. package/src/theme/tokens.ts +15 -158
  538. package/src/utils/focusReturn.ts +17 -6
  539. package/src/utils/getNodeText.test.ts +24 -0
  540. package/src/utils/getNodeText.ts +37 -0
  541. package/src/utils/interactionModality.test.ts +45 -1
  542. package/src/utils/interactionModality.ts +64 -5
  543. package/src/utils/interactionOverlay.stylex.ts +12 -3
  544. package/src/utils/plainDate.test.ts +90 -0
  545. package/src/utils/plainDate.ts +58 -3
  546. package/src/utils/presentation.test.ts +380 -0
  547. package/src/utils/presentation.ts +180 -0
  548. package/src/utils/pressFeedback.test.ts +138 -0
  549. package/src/utils/pressFeedback.ts +34 -11
  550. package/src/utils/pressPaintPriority.test.ts +80 -0
  551. package/src/utils/primaryClick.test.ts +38 -0
  552. package/src/utils/primaryClick.ts +28 -0
  553. package/src/utils/timeFormat.test.ts +204 -0
  554. package/src/utils/timeFormat.ts +314 -0
  555. package/src/utils/touchPresentation.stylex.ts +43 -0
  556. package/src/utils/touchPresentation.test.ts +140 -1
  557. package/src/utils/touchPresentation.ts +5 -3
  558. package/src/utils/unicodeDigits.test.ts +44 -0
  559. package/src/utils/unicodeDigits.ts +71 -0
@@ -236,9 +236,12 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
236
236
  '@astryx.sideNav.label': '⟦Šíðé ñàṽíĝàţíóñ⟧',
237
237
  '@astryx.sideNav.resizeSidebar': '⟦Řéšížé šíðéƀàř⟧',
238
238
  '@astryx.sideNav.heading.openMenu': '⟦Óþéñ ɱéñú⟧',
239
+ '@astryx.slider.minimumThumb': '⟦Ṁíñíɱúɱ ṽàłúé⟧',
240
+ '@astryx.slider.maximumThumb': '⟦Ṁàẋíɱúɱ ṽàłúé⟧',
239
241
  '@astryx.tabList.label': '⟦Ţàƀš⟧',
240
242
  '@astryx.table.label': '⟦Ţàƀłé⟧',
241
243
  '@astryx.table.noData': '⟦Ñó ðàţà⟧',
244
+ '@astryx.table.columnResize.resizeColumn': '⟦Řéšížé çółúɱñ {label}⟧',
242
245
  '@astryx.table.filter.allPlaceholder': '⟦Àłł⟧',
243
246
  '@astryx.table.filter.reset': '⟦Řéšéţ⟧',
244
247
  '@astryx.table.filter.apply': '⟦Àþþłý⟧',
@@ -436,6 +439,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
436
439
  '@astryx.tableTree.expandRow': '⟦Éẋþàñð řóŵ⟧',
437
440
  '@astryx.tableTree.collapseAllRows': '⟦Çółłàþšé àłł řóŵš⟧',
438
441
  '@astryx.tableTree.expandAllRows': '⟦Éẋþàñð àłł řóŵš⟧',
442
+ '@astryx.textArea.counter': '⟦{count, number}/{limit, number}⟧',
439
443
  '@astryx.textArea.charactersRemaining':
440
444
  '⟦{count, number} {count, plural, one {character} other {characters}} řéɱàíñíñĝ⟧',
441
445
  '@astryx.textArea.charactersOverLimit':
@@ -458,6 +462,8 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
458
462
  '@astryx.multiSelector.allSelected': '⟦Àłł šéłéçţéð⟧',
459
463
  '@astryx.multiSelector.selectionCount':
460
464
  '⟦{count, number} óƒ {total, number} šéłéçţéð⟧',
465
+ '@astryx.multiSelector.selectedCount':
466
+ '⟦{count, plural, one {# selected} other {# selected}}⟧',
461
467
  '@astryx.multiSelector.createOption': '⟦Çřéàţé "{query}"⟧',
462
468
  '@astryx.multiSelector.optionCreated': '⟦{label} çřéàţéð⟧',
463
469
  '@astryx.multiSelector.emptySearchResults': '⟦Ñó řéšúłţš ƒóúñð⟧',
@@ -81,7 +81,10 @@ await handle.finished; // true if it ran to the end, false if interrupted
81
81
  - Promotion: every animation the animator creates, and every `set`, gives its
82
82
  channels a compositing layer in the same task (inline `will-change`, merged
83
83
  with the element's own), cleared 500 ms after the last motion. Mobile Safari
84
- needs it to run a curve through a blocked main thread.
84
+ needs it to run a curve through a blocked main thread. A pseudo-element is
85
+ promoted through `--astryx-motion-will-change-<pseudo>` on its host; its
86
+ style opts in with
87
+ `willChange: 'var(--astryx-motion-will-change-before, auto)'`.
85
88
  - `atRest(properties)` resolves once none of them is moving, after the last
86
89
  curve ends, whoever started it.
87
90
  - `rebase({y: -shift})` moves properties at once, as a layout shift does; a
@@ -121,6 +124,13 @@ await handle.finished; // true if it ran to the end, false if interrupted
121
124
  - `animatorFor(element)` returns the one animator for an element, so code
122
125
  that animates it from several places (a swipe on x, a removal on y)
123
126
  composes in one `translate` track.
127
+ - `createAnimator(dialog, {pseudoElement: '::backdrop'})` drives a
128
+ pseudo-element; with no inline style to hold it, a static filling animation
129
+ holds its end value until the next animation replaces it, while the
130
+ pseudo-element exists. A dialog's `::backdrop` is made again each time the
131
+ dialog opens, so a presentation `set`s its starting value right after
132
+ `showModal()`, in the same task (a `set` before it lands on the backdrop
133
+ the open replaces).
124
134
 
125
135
  ### How it stays on the compositor
126
136
 
@@ -67,18 +67,23 @@ const requestFrame = (callback: () => void) => {
67
67
  * properties, so the page is modeled here).
68
68
  */
69
69
  let stylesheet: Record<string, string> = {};
70
+ /** The stylesheet's values for a pseudo-element of the element. */
71
+ let pseudoStylesheet: Record<string, string> = {};
70
72
 
71
73
  beforeEach(() => {
72
74
  clock = 1000;
73
75
  recorded = [];
74
76
  frameQueue = [];
75
77
  stylesheet = {};
78
+ pseudoStylesheet = {};
76
79
  vi.spyOn(window, 'getComputedStyle').mockImplementation(
77
- element =>
80
+ (element, pseudo) =>
78
81
  ({
79
82
  getPropertyValue: (property: string) =>
80
- (element as HTMLElement).style.getPropertyValue(property) ||
81
- (stylesheet[property] ?? ''),
83
+ pseudo != null
84
+ ? (pseudoStylesheet[property] ?? '')
85
+ : (element as HTMLElement).style.getPropertyValue(property) ||
86
+ (stylesheet[property] ?? ''),
82
87
  }) as unknown as CSSStyleDeclaration,
83
88
  );
84
89
  vi.useFakeTimers();
@@ -799,6 +804,12 @@ describe('animatorFor: one animator per element', () => {
799
804
  const b = document.createElement('div');
800
805
  expect(animatorFor(a)).toBe(animatorFor(a));
801
806
  expect(animatorFor(a)).not.toBe(animatorFor(b));
807
+ expect(animatorFor(a, {pseudoElement: '::backdrop'})).toBe(
808
+ animatorFor(a, {pseudoElement: '::backdrop'}),
809
+ );
810
+ expect(animatorFor(a, {pseudoElement: '::backdrop'})).not.toBe(
811
+ animatorFor(a),
812
+ );
802
813
  });
803
814
 
804
815
  it('composes two callers’ axes in one translate track', async () => {
@@ -902,6 +913,24 @@ describe('promotion: a compositing layer while it moves', () => {
902
913
  expect(element.style.willChange).toBe('');
903
914
  });
904
915
 
916
+ it('promotes a pseudo-element through a custom property on its host', async () => {
917
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
918
+ animator.set({opacity: 0});
919
+ // set() promotes a pseudo-element too.
920
+ expect(
921
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
922
+ ).toBe('opacity');
923
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
924
+ expect(
925
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
926
+ ).toBe('opacity');
927
+ expect(element.style.willChange).toBe('');
928
+ await advance(5000 + PROMOTION_LINGER_MS);
929
+ expect(
930
+ element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
931
+ ).toBe('');
932
+ });
933
+
905
934
  it('ends promotion on the element it leaves', () => {
906
935
  const {animator, element} = setup();
907
936
  animator.animate({x: 50}, {spring: springs.settle});
@@ -1937,6 +1966,75 @@ describe('Reduced Motion (design:reduced-motion)', () => {
1937
1966
  expect(element.style.translate).toBe('100px 0px');
1938
1967
  });
1939
1968
 
1969
+ it("a pseudo-element's dip borrows its own opacity and hands it back without touching the host", async () => {
1970
+ for (const role of [undefined, 'exit'] as const) {
1971
+ const {animator, element} = setup({
1972
+ reduced: true,
1973
+ coarse: true,
1974
+ pseudoElement: '::before',
1975
+ });
1976
+ element.style.opacity = '0.5'; // the host's own, untouched
1977
+ pseudoStylesheet = {opacity: '0.3'};
1978
+ animator.animate(
1979
+ {x: 100},
1980
+ {spring: springs.navigationPush},
1981
+ role == null ? {} : {crossfadeAs: role},
1982
+ );
1983
+ await commitFrame(clock);
1984
+ const [fadeLayer] = live('opacity');
1985
+ const values = fadeLayer.keyframes.map(k => Number(k.opacity));
1986
+ expect(Math.max(...values)).toBeCloseTo(0.3, 9);
1987
+ await advance(fade * 2 + END_HOLD_MS + 1);
1988
+ expect(animator.get('x')).toBe(100);
1989
+ // The pseudo-element's stylesheet owns its opacity again; the host
1990
+ // keeps its own.
1991
+ expect(live('opacity')).toEqual([]);
1992
+ expect(element.style.opacity).toBe('0.5');
1993
+ }
1994
+ });
1995
+
1996
+ it("a pseudo-element's identity hold follows a node replacement", () => {
1997
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
1998
+ pseudoStylesheet = {opacity: '0'};
1999
+ animator.set({opacity: 1});
2000
+ animator.attach(null);
2001
+ const next = document.createElement('div');
2002
+ document.body.append(next);
2003
+ animator.attach(next);
2004
+ const held = live('opacity').filter(
2005
+ animation => animation.element === next,
2006
+ );
2007
+ expect(held).toHaveLength(1);
2008
+ expect(Number(held[0].keyframes.at(-1)!.opacity)).toBe(1);
2009
+ expect(live('opacity').some(a => a.element === element)).toBe(false);
2010
+ next.remove();
2011
+ });
2012
+
2013
+ it('a pseudo-element shown over a hiding stylesheet keeps its own opacity through a move', async () => {
2014
+ for (const role of [undefined, 'entrance'] as const) {
2015
+ const {animator} = setup({
2016
+ reduced: true,
2017
+ coarse: true,
2018
+ pseudoElement: '::backdrop',
2019
+ });
2020
+ pseudoStylesheet = {opacity: '0'};
2021
+ animator.set({opacity: 1});
2022
+ animator.animate(
2023
+ {x: 100},
2024
+ {spring: springs.navigationPush},
2025
+ role == null ? {} : {crossfadeAs: role},
2026
+ );
2027
+ await commitFrame(clock);
2028
+ for (let step = 0; step < 8; step++) {
2029
+ await advance(fade / 4);
2030
+ }
2031
+ expect(animator.get('x')).toBe(100);
2032
+ expect(animator.get('opacity')).toBe(1);
2033
+ const [held] = live('opacity');
2034
+ expect(Number(held.keyframes.at(-1)?.opacity)).toBe(1);
2035
+ }
2036
+ });
2037
+
1940
2038
  it('a move dips: fades out, lands while transparent, fades back in', async () => {
1941
2039
  const {animator, element} = reduced();
1942
2040
  animator.animate({x: 100}, {spring: springs.navigationPush});
@@ -2555,6 +2653,56 @@ describe('decay: momentum curves (a coast)', () => {
2555
2653
  });
2556
2654
  });
2557
2655
 
2656
+ describe('pseudoElement: a dialog\u2019s ::backdrop', () => {
2657
+ it('targets the pseudo-element, writes no inline style and holds the end state', async () => {
2658
+ const {animator, element} = setup({pseudoElement: '::backdrop'});
2659
+ animator.set({opacity: 0});
2660
+ const [held] = live('opacity');
2661
+ expect(held.options).toMatchObject({
2662
+ fill: 'both',
2663
+ pseudoElement: '::backdrop',
2664
+ });
2665
+ expect(held.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
2666
+ expect(element.style.opacity).toBe('');
2667
+
2668
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
2669
+ // The new animation exists before the hold goes: no frame in between.
2670
+ expect(held.cancelled).toBe(true);
2671
+ const [fade] = live('opacity');
2672
+ expect(fade.options).toMatchObject({
2673
+ fill: 'both',
2674
+ pseudoElement: '::backdrop',
2675
+ });
2676
+ expect(fade.keyframes[fade.keyframes.length - 1].opacity).toBe('1');
2677
+ expect(element.style.opacity).toBe('');
2678
+ await advance(Number(fade.options.duration) + 1000);
2679
+ // Past the curve's end, a static animation holds the end value.
2680
+ expect(fade.cancelled).toBe(true);
2681
+ const [rest] = live('opacity');
2682
+ expect(rest.options).toMatchObject({
2683
+ fill: 'both',
2684
+ pseudoElement: '::backdrop',
2685
+ });
2686
+ expect(rest.keyframes.map(k => k.opacity)).toEqual(['1', '1']);
2687
+ expect(animator.get('opacity')).toBe(1);
2688
+ });
2689
+
2690
+ it('an exit holds 0 until the next animation replaces it', async () => {
2691
+ const {animator} = setup({
2692
+ pseudoElement: '::backdrop',
2693
+ initial: {opacity: 1},
2694
+ });
2695
+ animator.animate({opacity: 0}, {spring: springs.alertDismiss});
2696
+ const [fade] = live('opacity');
2697
+ await advance(Number(fade.options.duration) + 1000);
2698
+ const [rest] = live('opacity');
2699
+ expect(rest.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
2700
+ animator.animate({opacity: 1}, {spring: springs.alertPresent});
2701
+ expect(rest.cancelled).toBe(true);
2702
+ expect(live('opacity')).toHaveLength(1);
2703
+ });
2704
+ });
2705
+
2558
2706
  describe('attach', () => {
2559
2707
  it('keeps state without an element and resumes the curve on attach', () => {
2560
2708
  const animator = createAnimator(null, {
@@ -69,13 +69,17 @@
69
69
  * Chromium starts it after the block.
70
70
  *
71
71
  * Promotion: Mobile Safari runs an animation whose start time is set from
72
- * script on its compositor at its first commit only if the element already
73
- * has a compositing layer; without one, a block right after that commit
74
- * freezes it (iOS 26.2 simulator: 2 of 7 runs kept moving). Every animation
75
- * the animator creates, and every `set`, therefore promotes its channels in
76
- * the same task: inline `will-change` on the element, merged with the
77
- * element's own. That is enough (5 of 5, and 4 of 4 through the animator);
78
- * promotion ends PROMOTION_LINGER_MS after the last motion.
72
+ * script on its compositor at its first commit only if the element (or the
73
+ * pseudo-element) already has a compositing layer; without one, a block
74
+ * right after that commit freezes it (iOS 26.2 simulator: 2 of 7 runs kept
75
+ * moving, 0 of 5 on a ::before). Every animation the animator creates, and
76
+ * every `set`, therefore promotes its channels in the same task: inline
77
+ * `will-change` on the element, merged with the element's own, or the custom
78
+ * property `--astryx-motion-will-change-<pseudo>` on the host for a
79
+ * pseudo-element, whose style opts in with
80
+ * `will-change: var(--astryx-motion-will-change-before, auto)`. That is
81
+ * enough (5 of 5, and 4 of 4 through the animator); promotion ends
82
+ * PROMOTION_LINGER_MS after the last motion.
79
83
  *
80
84
  * Known limits, iOS Safari (measured on the iOS 26.2 simulator):
81
85
  * - a block in the task that starts a motion delays it: it starts when the
@@ -125,6 +129,14 @@
125
129
  * Motion policy above, a cross-fade on a coarse pointer or after a touch
126
130
  * release (`touchRelease`) and at once elsewhere (DR2, DR3).
127
131
  *
132
+ * A pseudo-element (`options.pseudoElement`, a dialog's `::backdrop`) has no
133
+ * inline style: its end value is held by a static filling animation (after a
134
+ * curve ends, and after `set`) until the next animation replaces it, and for
135
+ * as long as the pseudo-element exists. The browser makes a `::backdrop`
136
+ * again each time its dialog opens, without the hold, so a presentation
137
+ * calls `set` with its starting value right after `showModal()`, in the same
138
+ * task: a `set` before it lands on the backdrop the open replaces.
139
+ *
128
140
  * SSR-safe: nothing touches `window` or `document` at module scope, and an
129
141
  * animator created without an element only keeps state until one is attached.
130
142
  *
@@ -220,6 +232,12 @@ export interface MotionCurve {
220
232
  * Options of `createAnimator`.
221
233
  */
222
234
  export interface AnimatorOptions {
235
+ /**
236
+ * Animates a pseudo-element of the element instead (`'::backdrop'` of a
237
+ * native dialog). A pseudo-element has no inline style, so a static filling
238
+ * animation holds its value after a curve ends and after `set`.
239
+ */
240
+ pseudoElement?: string;
223
241
  /** Starting values; identity by default. Never read from the DOM. */
224
242
  initial?: MotionValues;
225
243
  /** Clock in ms on the document timeline's time base. */
@@ -558,10 +576,10 @@ function format(
558
576
  }
559
577
  }
560
578
 
561
- const shared = new WeakMap<HTMLElement, Animator>();
579
+ const shared = new WeakMap<HTMLElement, Map<string, Animator>>();
562
580
 
563
581
  /**
564
- * The one animator for `element`, created on
582
+ * The one animator for `element` (and `options.pseudoElement`), created on
565
583
  * first use. Code that animates one element from several places (a swipe on
566
584
  * x, a removal on y) shares it, so their curves compose in one `translate`
567
585
  * track and retargeting one axis leaves the other on its curve; two
@@ -573,10 +591,16 @@ export function animatorFor(
573
591
  element: HTMLElement,
574
592
  options: AnimatorOptions = {},
575
593
  ): Animator {
576
- let animator = shared.get(element);
594
+ let byPseudo = shared.get(element);
595
+ if (byPseudo == null) {
596
+ byPseudo = new Map();
597
+ shared.set(element, byPseudo);
598
+ }
599
+ const key = options.pseudoElement ?? '';
600
+ let animator = byPseudo.get(key);
577
601
  if (animator == null) {
578
602
  animator = createAnimator(element, options);
579
- shared.set(element, animator);
603
+ byPseudo.set(key, animator);
580
604
  }
581
605
  return animator;
582
606
  }
@@ -712,6 +736,7 @@ export function createAnimator(
712
736
  requestAnimationFrame(() => callback());
713
737
  }
714
738
  : null);
739
+ const pseudoElement = options.pseudoElement;
715
740
  // Reduced Motion reads the person's settings and nothing else: no option
716
741
  // overrides them (design:reduced-motion DR4).
717
742
  const prefersReducedMotion = defaultPrefersReducedMotion;
@@ -762,6 +787,11 @@ export function createAnimator(
762
787
  /** The stylesheet's will-change when there is no inline value. */
763
788
  let sheetWillChange = '';
764
789
  let demoteTimer: ReturnType<typeof setTimeout> | null = null;
790
+ /** A pseudo-element is promoted through a custom property on its host. */
791
+ const promotionProperty =
792
+ pseudoElement == null
793
+ ? null
794
+ : `--astryx-motion-will-change-${pseudoElement.replace(/^:+/, '')}`;
765
795
  /** Channels whose identity the last node held inline (see `attach`). */
766
796
  let heldIdentity = new Set<Channel>();
767
797
 
@@ -843,7 +873,7 @@ export function createAnimator(
843
873
  channel: Channel,
844
874
  value: (property: MotionProperty) => number,
845
875
  ) => {
846
- if (target == null) {
876
+ if (target == null || pseudoElement != null) {
847
877
  return;
848
878
  }
849
879
  touched.add(channel);
@@ -908,14 +938,32 @@ export function createAnimator(
908
938
  return times;
909
939
  };
910
940
 
941
+ /** A pseudo-element's resting value, held by a static filling animation. */
942
+ const hold = (channel: Channel, time: number) => {
943
+ if (target == null) {
944
+ return;
945
+ }
946
+ const value = format(channel, finalValue);
947
+ const animation = target.animate([{[channel]: value}, {[channel]: value}], {
948
+ duration: 1,
949
+ fill: 'both',
950
+ pseudoElement,
951
+ });
952
+ animation.startTime = time;
953
+ running.set(channel, animation);
954
+ };
955
+
911
956
  const rebuild = (channel: Channel, time: number) => {
912
957
  const previous = running.get(channel);
913
958
  running.delete(channel);
914
959
  clearTimeout(retireTimers.get(channel));
915
960
  retireTimers.delete(channel);
916
- // The element's inline style holds the end state, so its old animation
917
- // can go first.
918
- previous?.cancel();
961
+ // An element's inline style holds the end state, so its old animation can
962
+ // go first. A pseudo-element has none: its old animation keeps holding the
963
+ // value until the new one exists (same task, no frame in between).
964
+ if (pseudoElement == null) {
965
+ previous?.cancel();
966
+ }
919
967
  const properties = CHANNELS[channel];
920
968
  // Settled curves become resting values.
921
969
  for (const property of properties) {
@@ -932,6 +980,10 @@ export function createAnimator(
932
980
  return;
933
981
  }
934
982
  if (moving.length === 0) {
983
+ if (pseudoElement != null) {
984
+ hold(channel, time);
985
+ }
986
+ previous?.cancel();
935
987
  return;
936
988
  }
937
989
  // A channel rebuilt by an explicit call (set, rebase, a retarget of
@@ -958,6 +1010,10 @@ export function createAnimator(
958
1010
  ),
959
1011
  );
960
1012
  if (end <= from) {
1013
+ if (pseudoElement != null) {
1014
+ hold(channel, time);
1015
+ }
1016
+ previous?.cancel();
961
1017
  return;
962
1018
  }
963
1019
  if (callGroup != null) {
@@ -1041,6 +1097,7 @@ export function createAnimator(
1041
1097
  // inline style already holds the same values, so that changes no pixel.
1042
1098
  fill: 'both',
1043
1099
  easing: 'linear',
1100
+ ...(pseudoElement != null ? {pseudoElement} : {}),
1044
1101
  });
1045
1102
  if (waiting != null) {
1046
1103
  // From rest: no start time, so the browser starts the animation when
@@ -1061,7 +1118,9 @@ export function createAnimator(
1061
1118
 
1062
1119
  /**
1063
1120
  * Once the main thread runs after the curve's end, the animation leaves the
1064
- * running state: the element's inline style already shows the end values.
1121
+ * running state: an element's inline style already shows the end values; a
1122
+ * pseudo-element's end value moves to a static holding animation, created
1123
+ * before the long one goes.
1065
1124
  */
1066
1125
  const scheduleRetire = (
1067
1126
  channel: Channel,
@@ -1088,6 +1147,15 @@ export function createAnimator(
1088
1147
  return;
1089
1148
  }
1090
1149
  running.delete(channel);
1150
+ // A pseudo-element holds its resting value, unless the stylesheet
1151
+ // takes it back now (a borrowed dip that has ended).
1152
+ const handsBack =
1153
+ channel === 'opacity' &&
1154
+ borrowedOpacity != null &&
1155
+ ![...landings].some(landing => landing.fadeIn?.isBorrowed === true);
1156
+ if (pseudoElement != null && !handsBack) {
1157
+ hold(channel, now());
1158
+ }
1091
1159
  animation.cancel();
1092
1160
  releaseIdentity(channel);
1093
1161
  flushReleases();
@@ -1102,22 +1170,36 @@ export function createAnimator(
1102
1170
  target == null
1103
1171
  ? NaN
1104
1172
  : Number.parseFloat(
1105
- view?.getComputedStyle(target).getPropertyValue('opacity') ?? '',
1173
+ view
1174
+ ?.getComputedStyle(target, pseudoElement ?? null)
1175
+ .getPropertyValue('opacity') ?? '',
1106
1176
  );
1107
1177
  return Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 1;
1108
1178
  };
1109
1179
 
1110
1180
  /**
1111
1181
  * Whether the animator holds an opacity of its own on the element: an inline
1112
- * value it wrote (a `set` over a stylesheet that hides or dims it). A dip
1113
- * returns to that value.
1182
+ * value it wrote, or a pseudo-element's holding animation (a `set` over a
1183
+ * stylesheet that hides or dims it). A dip returns to that value.
1114
1184
  */
1115
1185
  const writesOwnOpacity = (): boolean =>
1116
- (target?.style.getPropertyValue('opacity') ?? '') !== '';
1186
+ pseudoElement == null
1187
+ ? (target?.style.getPropertyValue('opacity') ?? '') !== ''
1188
+ : running.has('opacity');
1117
1189
 
1118
- /** Gives the element's opacity back to its stylesheet: its inline value goes. */
1190
+ /**
1191
+ * Gives the element's opacity back to its stylesheet: an element's inline
1192
+ * value goes; a pseudo-element, which has no inline style, loses the
1193
+ * animation holding its value.
1194
+ */
1119
1195
  const releaseOwnOpacity = () => {
1120
- target?.style.removeProperty('opacity');
1196
+ if (pseudoElement == null) {
1197
+ target?.style.removeProperty('opacity');
1198
+ return;
1199
+ }
1200
+ const held = running.get('opacity');
1201
+ running.delete('opacity');
1202
+ held?.cancel();
1121
1203
  };
1122
1204
 
1123
1205
  /**
@@ -1202,6 +1284,9 @@ export function createAnimator(
1202
1284
  releaseOwnOpacity();
1203
1285
  return;
1204
1286
  }
1287
+ if (pseudoElement != null) {
1288
+ return;
1289
+ }
1205
1290
  if (target.style.getPropertyValue(channel) !== IDENTITY_TEXT[channel]) {
1206
1291
  return;
1207
1292
  }
@@ -1265,9 +1350,17 @@ export function createAnimator(
1265
1350
 
1266
1351
  // -- promotion -------------------------------------------------------------
1267
1352
 
1268
- /** Writes the promoted channels as will-change. */
1353
+ /** Writes the promoted channels as will-change (or the pseudo's property). */
1269
1354
  const writePromotion = (element: HTMLElement) => {
1270
1355
  const list = [...promoted].join(', ');
1356
+ if (promotionProperty != null) {
1357
+ if (list === '') {
1358
+ element.style.removeProperty(promotionProperty);
1359
+ } else {
1360
+ element.style.setProperty(promotionProperty, list);
1361
+ }
1362
+ return;
1363
+ }
1271
1364
  if (list === '') {
1272
1365
  element.style.willChange = ownWillChange ?? '';
1273
1366
  ownWillChange = null;
@@ -1294,7 +1387,7 @@ export function createAnimator(
1294
1387
  };
1295
1388
 
1296
1389
  /**
1297
- * Gives the element a compositing layer for
1390
+ * Gives the element (or its pseudo-element) a compositing layer for
1298
1391
  * `channel` from the task that starts the motion: Mobile Safari runs an
1299
1392
  * animation whose start time is set from script on its compositor at its
1300
1393
  * first commit only if the element already has a layer.
@@ -2226,9 +2319,25 @@ export function createAnimator(
2226
2319
  }
2227
2320
  // After the landing's commit, which can promote the element again.
2228
2321
  demote();
2322
+ // A pseudo-element holds an identity with a static animation, which the
2323
+ // cancel below drops: note the channels to hold again on the next node
2324
+ // (never a borrowed opacity, which is the stylesheet's).
2229
2325
  const isOpacityBorrowed =
2230
2326
  borrowedOpacity != null ||
2231
2327
  [...landings].some(landing => landing.restoreIsBorrowed === true);
2328
+ const pseudoHeld =
2329
+ pseudoElement == null || target == null
2330
+ ? null
2331
+ : new Set(
2332
+ (Object.keys(CHANNELS) as Channel[]).filter(
2333
+ channel =>
2334
+ running.has(channel) &&
2335
+ !(channel === 'opacity' && isOpacityBorrowed) &&
2336
+ CHANNELS[channel].every(
2337
+ p => !segments.has(p) && resting[p] === IDENTITY[p],
2338
+ ),
2339
+ ),
2340
+ );
2232
2341
  for (const animation of running.values()) {
2233
2342
  animation.cancel();
2234
2343
  }
@@ -2248,13 +2357,15 @@ export function createAnimator(
2248
2357
  // differs, kept through a detach for the node that comes next.
2249
2358
  if (target != null) {
2250
2359
  const outgoing = target;
2251
- heldIdentity = new Set(
2252
- (Object.keys(CHANNELS) as Channel[]).filter(
2253
- channel =>
2254
- outgoing.style.getPropertyValue(channel) ===
2255
- IDENTITY_TEXT[channel],
2256
- ),
2257
- );
2360
+ heldIdentity =
2361
+ pseudoHeld ??
2362
+ new Set(
2363
+ (Object.keys(CHANNELS) as Channel[]).filter(
2364
+ channel =>
2365
+ outgoing.style.getPropertyValue(channel) ===
2366
+ IDENTITY_TEXT[channel],
2367
+ ),
2368
+ );
2258
2369
  }
2259
2370
  const held = heldIdentity;
2260
2371
  target = next;
@@ -2284,8 +2395,13 @@ export function createAnimator(
2284
2395
  } else if (held.has(channel)) {
2285
2396
  // An identity the animator held inline over a stylesheet that
2286
2397
  // differs goes onto the new node too (writeInline keeps it only
2287
- // where the new node's stylesheet still differs).
2288
- writeInline(channel, property => resting[property]);
2398
+ // where the new node's stylesheet still differs); a pseudo-element's
2399
+ // is held again.
2400
+ if (pseudoElement != null) {
2401
+ hold(channel, time);
2402
+ } else {
2403
+ writeInline(channel, property => resting[property]);
2404
+ }
2289
2405
  }
2290
2406
  }
2291
2407
  flushReleases();
@@ -69,7 +69,7 @@ export const docs = {
69
69
  type: 'DefinedTheme',
70
70
  required: true,
71
71
  description:
72
- 'Theme object to apply. Prefer built theme objects for static production themes; use runtime `defineTheme()` for dynamic themes.',
72
+ "Theme object to apply. Prefer built theme objects for static production themes; use runtime `defineTheme()` for dynamic themes. Its `motion` can set `tempo` (0.75–1.5) and `character` (`'crisp' | 'standard' | 'expressive'`).",
73
73
  },
74
74
  {
75
75
  name: 'mode',