@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
@@ -1,37 +1,57 @@
1
1
  ---
2
2
  schema_version: 3
3
- template_version: 3
3
+ template_version: 7
4
4
  kind: component
5
5
  id: component:Kbd
6
- authority: draft
6
+ authority: current
7
7
  archive_reason: null
8
8
  superseded_by: null
9
- approved_by: null
10
- approved_at: null
9
+ approved_by: cixzhang
10
+ approved_at: 2026-10-31
11
11
  owners: [cixzhang]
12
- review_triggers: [theming]
12
+ review_triggers: [public-api, behavior, theming, accessibility]
13
13
  verified_by: [packages/core/src/Kbd/Kbd.test.tsx, scripts/check-knowledge.mjs]
14
14
  modules: []
15
15
  families: []
16
16
  design_specs: []
17
- architecture: [architecture:component-theming-surface]
17
+ architecture:
18
+ [
19
+ architecture:component-theming-surface,
20
+ architecture:internationalization,
21
+ architecture:public-component-api,
22
+ ]
18
23
  contributing: []
19
- system_specs: []
24
+ system_specs: [spec:AST-102]
20
25
  ---
21
26
 
22
27
  # Kbd component contract
23
28
 
29
+ ## Contract at a glance
30
+
31
+ | Area | Contract |
32
+ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33
+ | Public contract | None changes. Kbd takes a `keys` string and base props. |
34
+ | Behavior | Kbd splits `keys` on `+`, draws one hidden key badge per key with platform-aware glyphs, and names the whole shortcut once with spoken key names. |
35
+ | End-user impact | Sighted people see familiar key glyphs; screen-reader users hear the shortcut as words. Those words are English in every locale. |
36
+ | Builder impact | None. |
37
+ | Compatibility/readiness | Records shipped behavior; no API, default, or DOM change. Badges hold a fixed height; listed under Current-state impact. |
38
+ | Review checks | Reject a badge that is exposed to assistive technology, a caller `role` or `aria-label` that replaces the computed one, duplicate React keys for repeated keys, or `mod` that ignores the platform. |
39
+ | Governing rules | `architecture:component-theming-surface`; `architecture:internationalization` INV1, INV1a; `architecture:public-component-api` INV5; `spec:AST-102` FR4. |
40
+
41
+ This table is a review projection; the body below is authoritative.
42
+
24
43
  ## Intent
25
44
 
26
- Kbd presents a keyboard shortcut as one or more painted key badges. This draft
27
- records current consumer anatomy and theming reachability without changing
28
- runtime behavior, styling, targets, or public API.
45
+ Kbd presents a keyboard shortcut as one or more painted key badges. It owns
46
+ parsing the shortcut string, the platform-aware glyph for each key, the
47
+ shortcut's accessible name, and the `kbd` target. Consumer usage lives in
48
+ `Kbd.doc.mjs`.
29
49
 
30
50
  ## Compatibility and migration
31
51
 
32
52
  - Released default preserved: `yes`
33
- - Compatibility class: additive documentation only; runtime, DOM, styling,
34
- targets, aliases, and public API remain unchanged
53
+ - Compatibility class: records shipped behavior; no public prop, default,
54
+ export, or DOM change
35
55
  - Controlled/uncontrolled behavior: not applicable
36
56
  - Migration decision: none
37
57
 
@@ -43,54 +63,70 @@ Consumer migration instructions belong in consumer docs and release notes.
43
63
 
44
64
  - The shortcut group, its current `kbd` target, and the painted key badges it
45
65
  renders from the supplied shortcut string.
66
+ - Key parsing, aliases, platform-aware glyphs, and the spoken name.
46
67
 
47
68
  **Does not own / non-goals**
48
69
 
49
70
  - The action associated with the shortcut or how the shortcut is discovered —
50
71
  owned by the product callsite.
51
- - Whether an individual key badge should gain a public target — unresolved by
52
- this factual backfill.
72
+ - Whether an individual key badge should gain a public target — open (OQ1).
73
+ - Listening for or handling the shortcut — not provided; Kbd is display only.
74
+ - Caller DOM props, `ref`, `className`, `style`, and `xstyle` on the group —
75
+ shipped and documented, uncontracted here beyond FR4's role and label
76
+ precedence.
53
77
 
54
78
  ## Public concepts
55
79
 
56
- No new public concept is introduced. Consumer props and usage remain documented
57
- in `Kbd.doc.mjs`.
80
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
81
+ | ------- | ------------------------------------------------------------------- | --------------------- | ----------------------------------------- | -------- | --------------- | --------- | ----------------------------------------- |
82
+ | Keys | `+`-separated key names; `mod`, named keys, aliases, any other text | The shortcut to show. | Always. | required | `component:Kbd` | Stable | An unknown key shows uppercased as typed. |
58
83
 
59
84
  ## Behavioral and layout contract
60
85
 
61
- | ID | Candidate invariant | Basis | Draft review state |
62
- | --- | ------------------------------------------------------------------------------------------------------------ | ------------------------------- | --------------------------------------------------- |
63
- | FR1 | The current render contains one shortcut group and one painted key badge for every parsed key segment. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
64
- | FR2 | The shortcut group carries the current `kbd` target; individual key badges currently carry no public target. | Current source, docs, and tests | Verified current behavior; theming intent unsettled |
86
+ | ID | Invariant | Basis |
87
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
88
+ | FR1 | The current render contains one shortcut group and one painted key badge for every parsed key segment. `keys` splits on `+`; each segment is trimmed and lowercased, `esc` reads as `escape` and `return` as `enter`, and a repeated key gets its own badge. Each badge is at least the `--spacing-5` size and MUST grow with a set text size so its glyph never clips (`spec:AST-102` FR4). Implementation gap: the badge height is fixed at `--spacing-5`. | Current source, docs, and tests; `spec:AST-102` FR4 |
89
+ | FR2 | The shortcut group carries the current `kbd` target; individual key badges currently carry no public target (OQ1). | Current source, docs, and tests |
90
+ | FR3 | Each badge shows a glyph: `mod` is ⌘ on Apple platforms and the catalog's short Control text ("Ctrl" in English) elsewhere and before hydration; `escape` shows the catalog's short Escape text ("Esc"); `ctrl`, `alt`, `shift`, `enter`, `backspace`, `tab`, and the arrows show their symbols; `plus` shows "+"; any other key shows uppercased. | `architecture:internationalization` INV1a; current source and tests |
91
+ | FR4 | The group has `role="img"` and an `aria-label` built from each key's spoken name, taken from the Astryx catalog (`mod` is "Command" on Apple platforms and "Control" elsewhere and before hydration; an unknown key is spoken uppercased), joined pairwise through the catalog's shortcut message (" + " in English, for example "Command + K"); every badge is `aria-hidden`. A caller's `role` and `aria-label` never replace the computed ones; other caller ARIA props, such as `aria-labelledby` and `aria-hidden`, still reach the group. | `architecture:internationalization` INV1, INV1a; current source and tests |
65
92
 
66
93
  ### Allowed variation
67
94
 
68
- - Platform-specific key text and caller-supplied shortcut combinations may vary
69
- without changing the two-part anatomy recorded here.
95
+ - **AV1 — Shortcut content.** Platform-specific key text and caller-supplied
96
+ shortcut combinations may vary without changing the two-part anatomy recorded
97
+ here.
70
98
 
71
99
  ### Representative states
72
100
 
73
- - The same anatomy applies to single-key, multi-key, modifier, and
74
- platform-resolved shortcuts.
101
+ | State | Required invariant | Allowed variation |
102
+ | ------------ | --------------------------------------------------------- | ----------------- |
103
+ | Single key | One badge; the name is the key's spoken name. | Key. |
104
+ | Mod + key | ⌘ or Ctrl by platform; name "Command" or "Control" + key. | Platform. |
105
+ | Repeated key | One badge per occurrence. | — |
75
106
 
76
107
  ### Transformation and precedence order
77
108
 
78
- - No new parsing, layout, or styling precedence rule is introduced.
109
+ - **ORD1 — Key resolution.** Split on `+` → trim → lowercase → alias → glyph
110
+ and spoken name.
79
111
 
80
112
  ### Performance and resources
81
113
 
82
- - No new performance or resource rule is introduced.
114
+ - **PR1 — No listener.** Kbd attaches no event listener.
83
115
 
84
116
  ## Accessibility contract
85
117
 
86
- This draft does not change or extend Kbd's existing accessible-name behavior.
118
+ - **AR1 — One name.** The shortcut is one image named by its spoken keys,
119
+ unless a caller's `aria-labelledby` names it; the glyph badges are hidden
120
+ (FR4).
121
+ - **AR2 — Localization.** Visible short texts and spoken names follow the
122
+ provider locale through the catalog (FR3, FR4).
87
123
 
88
124
  ## Design relationships
89
125
 
90
126
  | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
91
127
  | ---------------- | ------------------------------------------------------------------ | ------------------------------ | -------------- | ------------------ |
92
- | Shortcut | Groups the complete shortcut and carries its accessible name. | Current source and public docs | Supporting | FR1, FR2 |
93
- | Key badge | Paints one visible badge for each key in the supplied combination. | Current source and public docs | Prominent | FR1, FR2 |
128
+ | Shortcut | Groups the complete shortcut and carries its accessible name. | Current source and public docs | Supporting | FR1, FR2, FR4 |
129
+ | Key badge | Paints one visible badge for each key in the supplied combination. | Current source and public docs | Prominent | FR1–FR3 |
94
130
 
95
131
  ### Theming anatomy
96
132
 
@@ -116,25 +152,32 @@ target.
116
152
  - `architecture:component-theming-surface` owns anatomy qualification, target
117
153
  mapping, and the difference between factual reachability and intended public
118
154
  theming API.
155
+ - `architecture:internationalization` owns catalog-backed Astryx text; FR3
156
+ and FR4 take their short texts, spoken names, and joining message from it.
119
157
 
120
158
  ## Verification map
121
159
 
122
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
123
- | ------------------- | ------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------- |
124
- | FR1 | `Kbd.test.tsx` key rendering suites | Single-key and multi-key output | Removing the group or a parsed badge fails existing role, element, or text assertions. | `audit:Kbd/anatomy` |
125
- | FR2 | Source inspection and current target inventories | Root target and child badges | Adding or documenting a current target without updating the anatomy map fails repository checks. | `audit:Kbd/theming` |
126
- | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and targets | Missing, extra, prefixed, stale, or alias-backed mappings fail repository validation. | `audit:Kbd/theming` |
160
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
161
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
162
+ | FR1 | `Kbd.test.tsx` single-key, multi-key, whitespace, alias, and repeated-key cases | Single and multiple keys; spaced and mixed-case input; repeated aliases | A missing or extra badge, an untrimmed or wrongly cased key, or a lost alias fails. Evidence gap: no test asserts badge height or text growth. | `audit:Kbd/anatomy` |
163
+ | FR2 | `Kbd.test.tsx` theme-class case | Root target and child badges | A missing root `kbd` target fails. Evidence gap: no test asserts that badges carry no target. | `audit:Kbd/theming` |
164
+ | FR3, FR4 | `Kbd.test.tsx` glyph, platform, accessible-name, override, and localization cases ("localizes visible key text and the spoken shortcut combination") | Mac and non-Mac; special and unknown keys | A wrong glyph for `mod`, `ctrl`, `alt`, `shift`, `enter`, `escape`, `plus`, or an unknown key, an exposed badge, a caller `role` or `aria-label` that replaces the computed one, or text that skips the catalog fails. Evidence gap: no test asserts the `backspace`, `tab`, or arrow glyphs, or the "Ctrl" text and "Control" name before hydration. | `audit:Kbd/accessibility` |
165
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and targets | Missing, extra, prefixed, stale, or alias-backed mappings fail repository validation. | `audit:Kbd/theming` |
166
+ | ORD1, PR1, AR1, AR2 | ORD1 is covered by the FR1 and FR3 rows and AR1 and AR2 by the FR3, FR4 row. Evidence gap: no test asserts the absence of listeners | — | — | `audit:Kbd/behavior` |
167
+
168
+ ## Current-state impact
169
+
170
+ - **Fixed badge height (FR1).** Badges hold a fixed height instead of growing
171
+ with a set text size, against `spec:AST-102` FR4.
127
172
 
128
173
  ## Decision log
129
174
 
130
- None. This draft records current facts and introduces no component-local design
131
- or theming decision.
175
+ No component-local decision.
132
176
 
133
177
  ## Open questions
134
178
 
135
179
  - **OQ1 — Should each Key badge gain a stable public theming target?**
136
- (`human-api`) Target exposure is unsettled; the current lack of reachability
137
- does not decide the answer.
180
+ (`human-api`) The current lack of reachability does not decide the answer.
138
181
 
139
182
  ## Content boundary
140
183
 
@@ -0,0 +1,126 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ // @pr-a11y scope core/Layer
3
+
4
+ /**
5
+ * @file LayerBrowserHide.a11y.chromium.spec.ts
6
+ * @input The `Core/Layer` BrowserHideWakesAFocusLayer story in a built
7
+ * Storybook, driven by real Chromium pointer and keyboard input.
8
+ * @output Real-engine proof that a show the browser's own hide wakes — its
9
+ * close watcher's Escape returning focus to a trigger whose focus-driven
10
+ * layer asks to show — reaches the page as no error and ends with that
11
+ * layer shown; that a tap outside, which returns no focus, hides the
12
+ * layer with no error and wakes nothing; and that a press which
13
+ * light-dismisses one layer and opens another opens it at once. Only a real engine
14
+ * decides when a popover operation is in flight and refuses a show inside
15
+ * it; the unit suite (useLayer.test.tsx) covers the same rules on a
16
+ * mirrored Popover API. The in-flight rule is Chromium 150+: on the
17
+ * Chromium this repository's Playwright pins (149) the first case passes
18
+ * with or without the fix, so until that pin moves it guards the end state
19
+ * only.
20
+ * @position Run with `pnpm test:a11y-contract`.
21
+ */
22
+
23
+ import {expect, test, type Page} from '@playwright/test';
24
+ import {
25
+ DEFAULT_STORYBOOK_DIR,
26
+ serveStorybook,
27
+ type StaticServer,
28
+ } from '@astryxdesign/a11y-spec/storybook';
29
+
30
+ let storybook: StaticServer;
31
+ test.beforeAll(async () => {
32
+ storybook = await serveStorybook(
33
+ process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
34
+ );
35
+ });
36
+ test.afterAll(async () => {
37
+ await storybook?.close();
38
+ });
39
+
40
+ /** Every error the page reports while the test runs: a refused show is one. */
41
+ function collectPageErrors(page: Page): string[] {
42
+ const errors: string[] = [];
43
+ page.on('pageerror', error => {
44
+ errors.push(`${error.name}: ${error.message}`);
45
+ });
46
+ return errors;
47
+ }
48
+
49
+ async function openStory(page: Page, id: string): Promise<void> {
50
+ await page.goto(`${storybook.origin}/iframe.html?id=${id}&viewMode=story`);
51
+ await page.locator('#storybook-root button').first().waitFor();
52
+ }
53
+
54
+ test('an Escape the browser answers, with focus inside the layer, returns focus to the trigger and shows its focus-driven layer, without an error', async ({
55
+ page,
56
+ }) => {
57
+ const errors = collectPageErrors(page);
58
+ await openStory(page, 'core-layer--browser-hide-wakes-a-focus-layer');
59
+ const trigger = page.getByRole('button', {name: 'Show layer'});
60
+ const inside = page.getByRole('button', {name: 'Inside', exact: true});
61
+ const hint = page.getByRole('tooltip');
62
+
63
+ // Keyboard focus shows the focus-driven layer; the press opens the content
64
+ // and Tab moves into it, which hides the focus-driven layer.
65
+ await page.keyboard.press('Tab');
66
+ await expect(trigger).toBeFocused();
67
+ await expect(hint).toBeVisible();
68
+ await page.keyboard.press('Enter');
69
+ await expect(inside).toBeVisible();
70
+ await page.keyboard.press('Tab');
71
+ await expect(inside).toBeFocused();
72
+ await expect(hint).toBeHidden();
73
+
74
+ // No layer handles Escape here: the browser's own close watcher hides the
75
+ // content layer and returns focus to the trigger inside that hide.
76
+ await page.keyboard.press('Escape');
77
+ await expect(inside).toBeHidden();
78
+ await expect(trigger).toBeFocused();
79
+ await expect(hint).toBeVisible();
80
+ expect(errors).toEqual([]);
81
+ });
82
+
83
+ test('a tap outside the layer, with focus inside it, hides the layer without an error; it returns no focus, so the focus-driven layer stays hidden', async ({
84
+ page,
85
+ }) => {
86
+ const errors = collectPageErrors(page);
87
+ await openStory(page, 'core-layer--browser-hide-wakes-a-focus-layer');
88
+ const trigger = page.getByRole('button', {name: 'Show layer'});
89
+ const inside = page.getByRole('button', {name: 'Inside', exact: true});
90
+ const hint = page.getByRole('tooltip');
91
+
92
+ await page.keyboard.press('Tab');
93
+ await page.keyboard.press('Enter');
94
+ await expect(inside).toBeVisible();
95
+ await page.keyboard.press('Tab');
96
+ await expect(inside).toBeFocused();
97
+
98
+ // A light dismiss returns no focus: the press moves focus to the page
99
+ // before the hide runs, so nothing wakes the focus-driven layer.
100
+ await page.mouse.click(5, 5);
101
+ await expect(inside).toBeHidden();
102
+ await expect(trigger).not.toBeFocused();
103
+ await expect(hint).toBeHidden();
104
+ expect(errors).toEqual([]);
105
+ });
106
+
107
+ test('a press that light-dismisses one layer and opens another opens it at once: focus moves into it, without an error', async ({
108
+ page,
109
+ }) => {
110
+ const errors = collectPageErrors(page);
111
+ await openStory(page, 'core-layer--browser-hide-wakes-a-focus-layer');
112
+ const inside = page.getByRole('button', {name: 'Inside', exact: true});
113
+ const otherTrigger = page.getByRole('button', {name: 'Show the other layer'});
114
+ const otherInside = page.getByRole('button', {name: 'Inside the other'});
115
+
116
+ await page.getByRole('button', {name: 'Show layer'}).click();
117
+ await expect(inside).toBeVisible();
118
+
119
+ // The browser light-dismisses the first layer at pointerup; the click opens
120
+ // the other one in the same gesture and focuses into it right away.
121
+ await otherTrigger.click();
122
+ await expect(inside).toBeHidden();
123
+ await expect(otherInside).toBeVisible();
124
+ await expect(otherInside).toBeFocused();
125
+ expect(errors).toEqual([]);
126
+ });
@@ -0,0 +1,205 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file popoverOperations.test.ts
5
+ * @input Uses vitest, the popoverOperations ledger
6
+ * @output Unit tests for the per-document ledger of popover operations
7
+ * @position Core testing; validates popoverOperations.ts
8
+ */
9
+
10
+ import {afterEach, describe, it, expect, vi} from 'vitest';
11
+ import {
12
+ afterPopoverOperations,
13
+ isBrowserHideInFlight,
14
+ isPopoverOperationInFlight,
15
+ observeBrowserHide,
16
+ runPopoverOperation,
17
+ } from './popoverOperations';
18
+
19
+ /** The replay runs a microtask after the end, then one microtask per callback. */
20
+ async function settle(): Promise<void> {
21
+ await Promise.resolve();
22
+ await Promise.resolve();
23
+ }
24
+
25
+ describe('popoverOperations', () => {
26
+ afterEach(() => {
27
+ vi.useRealTimers();
28
+ });
29
+
30
+ it('runs a callback at once when the document has no operation in flight', () => {
31
+ const callback = vi.fn();
32
+ afterPopoverOperations(document, callback);
33
+ expect(callback).toHaveBeenCalledTimes(1);
34
+ });
35
+
36
+ it('counts one of the layers’ own operations while it runs and replays a show that arrived inside it once the script has unwound', async () => {
37
+ const callback = vi.fn();
38
+ runPopoverOperation(document, () => {
39
+ expect(isPopoverOperationInFlight(document)).toBe(true);
40
+ afterPopoverOperations(document, callback);
41
+ expect(callback).not.toHaveBeenCalled();
42
+ });
43
+ // The operation has ended, but its caller is still on the stack.
44
+ expect(isPopoverOperationInFlight(document)).toBe(false);
45
+ expect(callback).not.toHaveBeenCalled();
46
+ await settle();
47
+ expect(callback).toHaveBeenCalledTimes(1);
48
+ });
49
+
50
+ it('counts the browser’s own hide apart from the layers’ operations, from its start until the element’s toggle', async () => {
51
+ const popover = document.createElement('div');
52
+ document.body.appendChild(popover);
53
+ try {
54
+ const callback = vi.fn();
55
+ observeBrowserHide(popover);
56
+ observeBrowserHide(popover);
57
+ // A show asked now is not refused ahead of time: the engine decides.
58
+ expect(isPopoverOperationInFlight(document)).toBe(false);
59
+ expect(isBrowserHideInFlight(document)).toBe(true);
60
+ afterPopoverOperations(document, callback);
61
+ await settle();
62
+ expect(callback).not.toHaveBeenCalled();
63
+
64
+ popover.dispatchEvent(new Event('toggle'));
65
+ expect(isBrowserHideInFlight(document)).toBe(false);
66
+ await settle();
67
+ expect(callback).toHaveBeenCalledTimes(1);
68
+ // The toggle ended it once; the next task finds nothing to end.
69
+ await new Promise(resolve => setTimeout(resolve, 0));
70
+ expect(isBrowserHideInFlight(document)).toBe(false);
71
+ } finally {
72
+ popover.remove();
73
+ }
74
+ });
75
+
76
+ it('ends each hide exactly once: the loser of toggle and backstop is released, so a second in-flight hide keeps its count', () => {
77
+ vi.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
78
+ const first = document.createElement('div');
79
+ const second = document.createElement('div');
80
+ document.body.append(first, second);
81
+ try {
82
+ const removeListener = vi.spyOn(second, 'removeEventListener');
83
+ observeBrowserHide(first);
84
+ observeBrowserHide(second);
85
+ expect(isBrowserHideInFlight(document)).toBe(true);
86
+ expect(vi.getTimerCount()).toBe(2);
87
+
88
+ // The first hide's toggle wins: its backstop is cleared, not left to
89
+ // run later against another hide's count.
90
+ first.dispatchEvent(new Event('toggle'));
91
+ expect(isBrowserHideInFlight(document)).toBe(true);
92
+ expect(vi.getTimerCount()).toBe(1);
93
+ // The second hide's backstop wins: it ends the hide once and drops the
94
+ // one-shot toggle listener.
95
+ vi.runOnlyPendingTimers();
96
+ expect(isBrowserHideInFlight(document)).toBe(false);
97
+ expect(removeListener).toHaveBeenCalledWith(
98
+ 'toggle',
99
+ expect.any(Function),
100
+ );
101
+
102
+ // A toggle after the backstop won, and a second toggle after the toggle
103
+ // won, change nothing: the one-shot listeners are gone.
104
+ observeBrowserHide(first);
105
+ expect(isBrowserHideInFlight(document)).toBe(true);
106
+ second.dispatchEvent(new Event('toggle'));
107
+ expect(isBrowserHideInFlight(document)).toBe(true);
108
+ first.dispatchEvent(new Event('toggle'));
109
+ first.dispatchEvent(new Event('toggle'));
110
+ expect(isBrowserHideInFlight(document)).toBe(false);
111
+
112
+ // Nothing drifted negative: a later hide is still counted.
113
+ observeBrowserHide(second);
114
+ expect(isBrowserHideInFlight(document)).toBe(true);
115
+ vi.runOnlyPendingTimers();
116
+ expect(isBrowserHideInFlight(document)).toBe(false);
117
+ } finally {
118
+ first.remove();
119
+ second.remove();
120
+ }
121
+ });
122
+
123
+ it('ends a counted hide at the next task when its toggle never arrives (the element left the document)', async () => {
124
+ const popover = document.createElement('div');
125
+ document.body.appendChild(popover);
126
+ const callback = vi.fn();
127
+ observeBrowserHide(popover);
128
+ popover.remove();
129
+ afterPopoverOperations(document, callback);
130
+ await settle();
131
+ expect(callback).not.toHaveBeenCalled();
132
+ await new Promise(resolve => setTimeout(resolve, 0));
133
+ expect(isBrowserHideInFlight(document)).toBe(false);
134
+ await settle();
135
+ expect(callback).toHaveBeenCalledTimes(1);
136
+ });
137
+
138
+ it('postpones the replay when another operation starts before it runs', async () => {
139
+ const callback = vi.fn();
140
+ runPopoverOperation(document, () => {
141
+ afterPopoverOperations(document, callback);
142
+ });
143
+ // Before the replay's microtask: a browser-run hide starts, and ends.
144
+ const second = document.createElement('div');
145
+ document.body.appendChild(second);
146
+ try {
147
+ observeBrowserHide(second);
148
+ await settle();
149
+ expect(callback).not.toHaveBeenCalled();
150
+ second.dispatchEvent(new Event('toggle'));
151
+ await settle();
152
+ expect(callback).toHaveBeenCalledTimes(1);
153
+ } finally {
154
+ second.remove();
155
+ }
156
+ });
157
+
158
+ it('replays each callback in its own microtask, so a throw in one reaches the page alone and leaves the rest to run', async () => {
159
+ const queued = vi.spyOn(globalThis, 'queueMicrotask');
160
+ try {
161
+ const first = vi.fn();
162
+ const second = vi.fn();
163
+ runPopoverOperation(document, () => {
164
+ afterPopoverOperations(document, first);
165
+ afterPopoverOperations(document, second);
166
+ });
167
+ // One microtask replays the batch; it queues one more per callback.
168
+ expect(queued).toHaveBeenCalledTimes(1);
169
+ await Promise.resolve();
170
+ expect(queued).toHaveBeenCalledTimes(3);
171
+ expect(first).not.toHaveBeenCalled();
172
+ await Promise.resolve();
173
+ expect(first).toHaveBeenCalledTimes(1);
174
+ expect(second).toHaveBeenCalledTimes(1);
175
+ expect(first.mock.invocationCallOrder[0]).toBeLessThan(
176
+ second.mock.invocationCallOrder[0] ?? 0,
177
+ );
178
+ } finally {
179
+ queued.mockRestore();
180
+ }
181
+ });
182
+
183
+ it('keeps one ledger per document', async () => {
184
+ const frame = document.createElement('iframe');
185
+ document.body.appendChild(frame);
186
+ const other = frame.contentDocument;
187
+ if (other == null) {
188
+ throw new Error('the iframe has no document');
189
+ }
190
+ try {
191
+ const callback = vi.fn();
192
+ const popover = other.createElement('div');
193
+ other.body.appendChild(popover);
194
+ observeBrowserHide(popover);
195
+ expect(isBrowserHideInFlight(document)).toBe(false);
196
+ afterPopoverOperations(document, callback);
197
+ expect(callback).toHaveBeenCalledTimes(1);
198
+ expect(isBrowserHideInFlight(other)).toBe(true);
199
+ popover.dispatchEvent(new Event('toggle'));
200
+ expect(isBrowserHideInFlight(other)).toBe(false);
201
+ } finally {
202
+ frame.remove();
203
+ }
204
+ });
205
+ });