@astryxdesign/core 0.7.5 → 0.7.6-canary.00507ba

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 (595) 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/AppShell/AppShell.d.ts.map +1 -1
  5. package/dist/AppShell/AppShell.js +183 -5
  6. package/dist/AppShell/observeMainOverflow.d.ts +9 -0
  7. package/dist/AppShell/observeMainOverflow.d.ts.map +1 -0
  8. package/dist/AppShell/observeMainOverflow.js +125 -0
  9. package/dist/Avatar/Avatar.d.ts.map +1 -1
  10. package/dist/Avatar/Avatar.js +12 -3
  11. package/dist/Badge/Badge.d.ts.map +1 -1
  12. package/dist/Badge/Badge.js +7 -1
  13. package/dist/BottomSheet/BottomSheet.js +1 -1
  14. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  15. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  16. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  17. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  18. package/dist/BottomSheet/useSheetGestures.js +30 -3
  19. package/dist/Button/Button.js +4 -4
  20. package/dist/Carousel/Carousel.d.ts +3 -1
  21. package/dist/Carousel/Carousel.d.ts.map +1 -1
  22. package/dist/Carousel/Carousel.js +12 -3
  23. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  24. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  25. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  26. package/dist/ClickableCard/ClickableCard.js +37 -4
  27. package/dist/DateInput/DateInput.d.ts.map +1 -1
  28. package/dist/DateInput/DateInput.js +12 -4
  29. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  30. package/dist/DateInput/MonthScroller.js +101 -15
  31. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  32. package/dist/DateInput/TouchDateField.js +8 -3
  33. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  34. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  35. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  36. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  37. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  38. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  39. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  40. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  41. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  42. package/dist/Dialog/Dialog.d.ts.map +1 -1
  43. package/dist/Dialog/Dialog.js +34 -9
  44. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  45. package/dist/Dialog/DialogHeader.js +5 -4
  46. package/dist/Divider/Divider.d.ts.map +1 -1
  47. package/dist/Divider/Divider.js +2 -1
  48. package/dist/Drawer/Drawer.d.ts +1 -0
  49. package/dist/Drawer/Drawer.d.ts.map +1 -1
  50. package/dist/Drawer/Drawer.js +7 -2
  51. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  52. package/dist/DropdownMenu/DropdownMenu.js +7 -3
  53. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  54. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  55. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
  56. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  57. package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
  58. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  59. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  60. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  61. package/dist/EmptyState/EmptyState.js +2 -1
  62. package/dist/Field/FieldLabel.d.ts +1 -1
  63. package/dist/Field/FieldLabel.d.ts.map +1 -1
  64. package/dist/Field/FieldLabel.js +4 -4
  65. package/dist/Field/InputClearButton.js +3 -3
  66. package/dist/Field/PanelSearchInput.js +2 -2
  67. package/dist/FieldStatus/FieldStatus.js +2 -2
  68. package/dist/FileInput/FileInput.d.ts.map +1 -1
  69. package/dist/FileInput/FileInput.js +7 -1
  70. package/dist/Heading/Heading.js +1 -1
  71. package/dist/Icon/Icon.js +1 -1
  72. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  73. package/dist/Indicator/CheckIndicator.js +2 -1
  74. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  75. package/dist/Indicator/CheckboxIndicator.js +3 -1
  76. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  77. package/dist/Indicator/RadioIndicator.js +3 -1
  78. package/dist/Item/Item.d.ts.map +1 -1
  79. package/dist/Item/Item.js +2 -1
  80. package/dist/Item/ItemSwipeLayer.js +1 -1
  81. package/dist/Layer/popoverOperations.d.ts +27 -0
  82. package/dist/Layer/popoverOperations.d.ts.map +1 -0
  83. package/dist/Layer/popoverOperations.js +159 -0
  84. package/dist/Layer/useLayer.d.ts +1 -1
  85. package/dist/Layer/useLayer.d.ts.map +1 -1
  86. package/dist/Layer/useLayer.js +93 -59
  87. package/dist/Layout/Layout.js +1 -1
  88. package/dist/Layout/LayoutContent.js +1 -1
  89. package/dist/Layout/LayoutFooter.js +1 -1
  90. package/dist/Layout/LayoutHeader.js +1 -1
  91. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  92. package/dist/Lightbox/Lightbox.js +5 -0
  93. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  94. package/dist/List/List.js +1 -1
  95. package/dist/List/ListItem.js +3 -3
  96. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  97. package/dist/MobileNav/MobileNav.js +7 -2
  98. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
  99. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  100. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  101. package/dist/MultiSelector/MultiSelector.js +26 -10
  102. package/dist/NumberInput/NumberInput.d.ts +3 -1
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +3 -3
  105. package/dist/NumberInput/numberParser.d.ts +3 -0
  106. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  107. package/dist/NumberInput/numberParser.js +7 -44
  108. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  109. package/dist/ProgressBar/ProgressBar.js +16 -12
  110. package/dist/Resizable/useResizable.d.ts.map +1 -1
  111. package/dist/Resizable/useResizable.js +28 -2
  112. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  113. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  114. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  115. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  116. package/dist/Selector/useSelectorPresentation.js +20 -4
  117. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  118. package/dist/Skeleton/Skeleton.js +1 -1
  119. package/dist/Slider/Slider.d.ts.map +1 -1
  120. package/dist/Slider/Slider.js +10 -2
  121. package/dist/Spinner/Spinner.d.ts.map +1 -1
  122. package/dist/Spinner/Spinner.js +3 -3
  123. package/dist/Stack/Stack.js +1 -1
  124. package/dist/Stack/StackItem.js +1 -1
  125. package/dist/TabList/TabList.d.ts.map +1 -1
  126. package/dist/TabList/TabList.js +16 -1
  127. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  128. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  129. package/dist/Text/Text.js +1 -1
  130. package/dist/Text/useTruncation.d.ts.map +1 -1
  131. package/dist/Text/useTruncation.js +23 -5
  132. package/dist/TextArea/TextArea.d.ts +1 -1
  133. package/dist/TextArea/TextArea.d.ts.map +1 -1
  134. package/dist/TextArea/TextArea.js +13 -2
  135. package/dist/TextInput/TextInput.d.ts.map +1 -1
  136. package/dist/TextInput/TextInput.js +3 -3
  137. package/dist/TimeInput/TimeInput.d.ts +6 -2
  138. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  139. package/dist/TimeInput/TimeInput.js +21 -14
  140. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  141. package/dist/TimeInput/TouchTimeField.js +5 -4
  142. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  143. package/dist/Timestamp/Timestamp.js +2 -2
  144. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  145. package/dist/Toast/Toast.d.ts.map +1 -1
  146. package/dist/Toast/Toast.js +60 -20
  147. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  148. package/dist/Toast/ToastViewport.js +82 -51
  149. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  150. package/dist/Tokenizer/Tokenizer.js +13 -2
  151. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  152. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  153. package/dist/Toolbar/Toolbar.js +2 -1
  154. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  155. package/dist/TopNav/TopNavMegaMenu.js +3 -0
  156. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  157. package/dist/TopNav/TopNavMenu.js +1 -0
  158. package/dist/Typeahead/Typeahead.d.ts +1 -1
  159. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  160. package/dist/Typeahead/Typeahead.js +9 -1
  161. package/dist/astryx.css +21 -4
  162. package/dist/gestures/GestureArena.d.ts +6 -0
  163. package/dist/gestures/GestureArena.d.ts.map +1 -1
  164. package/dist/gestures/GestureArena.js +39 -0
  165. package/dist/gestures/GestureHost.d.ts +352 -0
  166. package/dist/gestures/GestureHost.d.ts.map +1 -0
  167. package/dist/gestures/GestureHost.js +1773 -0
  168. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  169. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  170. package/dist/gestures/GestureRecognizer.js +16 -0
  171. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  172. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  173. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  174. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  175. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  176. package/dist/gestures/PanGestureRecognizer.js +3 -0
  177. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  178. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  179. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  180. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  181. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  182. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  183. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  184. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  185. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  186. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  187. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  188. package/dist/gestures/TapGestureRecognizer.js +3 -0
  189. package/dist/gestures/VelocityTracker.d.ts +6 -2
  190. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  191. package/dist/gestures/VelocityTracker.js +7 -3
  192. package/dist/gestures/haptics.d.ts +65 -0
  193. package/dist/gestures/haptics.d.ts.map +1 -0
  194. package/dist/gestures/haptics.js +182 -0
  195. package/dist/gestures/nativeScroll.d.ts +42 -0
  196. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  197. package/dist/gestures/nativeScroll.js +228 -0
  198. package/dist/gestures/touchAction.d.ts +60 -0
  199. package/dist/gestures/touchAction.d.ts.map +1 -0
  200. package/dist/gestures/touchAction.js +157 -0
  201. package/dist/gestures/touchActionRule.d.ts +34 -0
  202. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  203. package/dist/gestures/touchActionRule.js +140 -0
  204. package/dist/gestures/types.d.ts +18 -7
  205. package/dist/gestures/types.d.ts.map +1 -1
  206. package/dist/gestures/types.js +22 -2
  207. package/dist/hooks/index.d.ts +1 -1
  208. package/dist/hooks/index.d.ts.map +1 -1
  209. package/dist/hooks/modalAnnounce.d.ts.map +1 -1
  210. package/dist/hooks/modalAnnounce.js +107 -61
  211. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -1
  212. package/dist/hooks/scrollKeyboardDelegation.js +7 -3
  213. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  214. package/dist/hooks/useAdaptivePresentation.js +4 -1
  215. package/dist/hooks/useAnnounce.d.ts +101 -42
  216. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  217. package/dist/hooks/useAnnounce.js +161 -75
  218. package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
  219. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  220. package/dist/hooks/useAnnounceRenderedText.js +5 -3
  221. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  222. package/dist/hooks/useBusyAnnouncement.js +6 -12
  223. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  224. package/dist/hooks/useMenuHover.js +12 -1
  225. package/dist/hooks/useScrollOverflow.d.ts +2 -1
  226. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  227. package/dist/hooks/useScrollOverflow.js +56 -10
  228. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  229. package/dist/i18n/generated-locales/en.generated.js +6 -0
  230. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  231. package/dist/i18n/generated-locales/pseudo.generated.js +6 -0
  232. package/dist/motion/animator.d.ts +7 -1
  233. package/dist/motion/animator.d.ts.map +1 -1
  234. package/dist/motion/animator.js +119 -27
  235. package/dist/theme/defineTheme.d.ts +19 -5
  236. package/dist/theme/defineTheme.d.ts.map +1 -1
  237. package/dist/theme/defineTheme.js +64 -4
  238. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  239. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  240. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  241. package/dist/theme/domainTokens/index.d.ts +5 -4
  242. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  243. package/dist/theme/domainTokens/index.js +5 -9
  244. package/dist/theme/index.d.ts +1 -0
  245. package/dist/theme/index.d.ts.map +1 -1
  246. package/dist/theme/motionKnobs.d.ts +56 -0
  247. package/dist/theme/motionKnobs.d.ts.map +1 -0
  248. package/dist/theme/motionKnobs.js +48 -0
  249. package/dist/theme/resolveThemeValues.d.ts +11 -4
  250. package/dist/theme/resolveThemeValues.d.ts.map +1 -1
  251. package/dist/theme/resolveThemeValues.js +40 -4
  252. package/dist/theme/themeAdaptations.d.ts +7 -3
  253. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  254. package/dist/theme/themeAdaptations.js +36 -7
  255. package/dist/theme/themeMotion.d.ts +96 -0
  256. package/dist/theme/themeMotion.d.ts.map +1 -0
  257. package/dist/theme/themeMotion.js +177 -0
  258. package/dist/theme/tokenReferences.d.ts +31 -0
  259. package/dist/theme/tokenReferences.d.ts.map +1 -0
  260. package/dist/theme/tokenReferences.js +132 -0
  261. package/dist/theme/tokens.d.ts +1 -0
  262. package/dist/theme/tokens.d.ts.map +1 -1
  263. package/dist/theme/tokens.js +9 -129
  264. package/dist/theme/tokens.stylex.d.ts +12 -0
  265. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  266. package/dist/theme/tokens.stylex.js +12 -0
  267. package/dist/utils/focusReturn.d.ts +17 -1
  268. package/dist/utils/focusReturn.d.ts.map +1 -1
  269. package/dist/utils/focusReturn.js +11 -4
  270. package/dist/utils/getNodeText.d.ts +8 -0
  271. package/dist/utils/getNodeText.d.ts.map +1 -0
  272. package/dist/utils/getNodeText.js +37 -0
  273. package/dist/utils/interactionModality.d.ts +11 -2
  274. package/dist/utils/interactionModality.d.ts.map +1 -1
  275. package/dist/utils/interactionModality.js +58 -5
  276. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  277. package/dist/utils/interactionOverlay.stylex.js +17 -8
  278. package/dist/utils/plainDate.d.ts +2 -0
  279. package/dist/utils/plainDate.d.ts.map +1 -1
  280. package/dist/utils/plainDate.js +46 -3
  281. package/dist/utils/presentation.d.ts +50 -0
  282. package/dist/utils/presentation.d.ts.map +1 -0
  283. package/dist/utils/presentation.js +125 -0
  284. package/dist/utils/pressFeedback.d.ts.map +1 -1
  285. package/dist/utils/pressFeedback.js +20 -1
  286. package/dist/utils/primaryClick.d.ts +11 -0
  287. package/dist/utils/primaryClick.d.ts.map +1 -0
  288. package/dist/utils/primaryClick.js +25 -0
  289. package/dist/utils/sharedMutationObserver.d.ts +19 -11
  290. package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
  291. package/dist/utils/sharedMutationObserver.js +66 -26
  292. package/dist/utils/timeFormat.d.ts +47 -0
  293. package/dist/utils/timeFormat.d.ts.map +1 -0
  294. package/dist/utils/timeFormat.js +240 -0
  295. package/dist/utils/touchPresentation.d.ts.map +1 -1
  296. package/dist/utils/touchPresentation.js +5 -3
  297. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  298. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  299. package/dist/utils/touchPresentation.stylex.js +33 -0
  300. package/dist/utils/unicodeDigits.d.ts +20 -0
  301. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  302. package/dist/utils/unicodeDigits.js +70 -0
  303. package/locales/en.json +27 -3
  304. package/locales/pseudo.json +18 -0
  305. package/package.json +5 -4
  306. package/src/AlertDialog/AlertDialog.tsx +6 -5
  307. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  308. package/src/AppShell/AppShell.doc.mjs +1 -1
  309. package/src/AppShell/AppShell.spec.md +9 -12
  310. package/src/AppShell/AppShell.test.tsx +164 -2
  311. package/src/AppShell/AppShell.tsx +225 -5
  312. package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +534 -0
  313. package/src/AppShell/observeMainOverflow.test.ts +173 -0
  314. package/src/AppShell/observeMainOverflow.ts +143 -0
  315. package/src/Avatar/Avatar.spec.md +22 -22
  316. package/src/Avatar/Avatar.tsx +18 -1
  317. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  318. package/src/Badge/Badge.doc.mjs +1 -1
  319. package/src/Badge/Badge.tsx +19 -2
  320. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  321. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  322. package/src/BottomSheet/BottomSheet.tsx +1 -1
  323. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  324. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  325. package/src/BottomSheet/useSheetGestures.ts +37 -6
  326. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  327. package/src/Button/Button.tsx +4 -4
  328. package/src/Calendar/Calendar.spec.md +87 -48
  329. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  330. package/src/Carousel/Carousel.doc.mjs +1 -1
  331. package/src/Carousel/Carousel.spec.md +9 -9
  332. package/src/Carousel/Carousel.test.tsx +124 -3
  333. package/src/Carousel/Carousel.tsx +10 -2
  334. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  335. package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +1 -16
  336. package/src/Chat/ChatComposerTokenElement.spec.md +45 -33
  337. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  338. package/src/ClickableCard/ClickableCard.test.tsx +96 -0
  339. package/src/ClickableCard/ClickableCard.tsx +48 -4
  340. package/src/Collapsible/Collapsible.spec.md +2 -0
  341. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  342. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  343. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  344. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  345. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  346. package/src/DateInput/DateInput.test.tsx +115 -3
  347. package/src/DateInput/DateInput.tsx +29 -14
  348. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  349. package/src/DateInput/MonthScroller.tsx +106 -20
  350. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  351. package/src/DateInput/TouchDateField.tsx +14 -3
  352. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  353. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  354. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  355. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  356. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  357. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  358. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  359. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  360. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  361. package/src/Dialog/Dialog.test.tsx +216 -2
  362. package/src/Dialog/Dialog.tsx +42 -9
  363. package/src/Dialog/DialogHeader.tsx +5 -4
  364. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  365. package/src/Divider/Divider.tsx +2 -1
  366. package/src/Drawer/Drawer.spec.md +111 -80
  367. package/src/Drawer/Drawer.test.tsx +35 -0
  368. package/src/Drawer/Drawer.tsx +13 -2
  369. package/src/Drawer/DrawerHeader.spec.md +181 -0
  370. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  371. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  372. package/src/DropdownMenu/DropdownMenu.tsx +10 -3
  373. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  374. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  375. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  376. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  377. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
  378. package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
  379. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  380. package/src/EmptyState/EmptyState.tsx +2 -1
  381. package/src/Field/FieldLabel.tsx +5 -4
  382. package/src/Field/InputClearButton.tsx +3 -3
  383. package/src/Field/PanelSearchInput.tsx +2 -2
  384. package/src/FieldStatus/FieldStatus.tsx +2 -2
  385. package/src/FileInput/FileInput.test.tsx +21 -0
  386. package/src/FileInput/FileInput.tsx +4 -1
  387. package/src/FormLayout/FormLayout.spec.md +7 -8
  388. package/src/Heading/Heading.tsx +1 -1
  389. package/src/Icon/Icon.tsx +1 -1
  390. package/src/Indicator/CheckIndicator.spec.md +47 -42
  391. package/src/Indicator/CheckIndicator.tsx +2 -1
  392. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  393. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  394. package/src/Indicator/Indicator.spec.md +188 -0
  395. package/src/Indicator/RadioIndicator.tsx +3 -1
  396. package/src/Item/Item.tsx +2 -1
  397. package/src/Item/ItemSwipeLayer.tsx +1 -1
  398. package/src/Kbd/Kbd.spec.md +81 -38
  399. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
  400. package/src/Layer/popoverOperations.test.ts +205 -0
  401. package/src/Layer/popoverOperations.ts +172 -0
  402. package/src/Layer/useLayer.test.tsx +348 -1
  403. package/src/Layer/useLayer.tsx +118 -71
  404. package/src/Layout/Layout.spec.md +127 -131
  405. package/src/Layout/Layout.tsx +1 -1
  406. package/src/Layout/LayoutContent.spec.md +205 -0
  407. package/src/Layout/LayoutContent.tsx +1 -1
  408. package/src/Layout/LayoutFooter.spec.md +190 -0
  409. package/src/Layout/LayoutFooter.tsx +1 -1
  410. package/src/Layout/LayoutHeader.spec.md +193 -0
  411. package/src/Layout/LayoutHeader.tsx +1 -1
  412. package/src/Layout/LayoutPanel.spec.md +194 -0
  413. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  414. package/src/Lightbox/Lightbox.test.tsx +18 -0
  415. package/src/Lightbox/Lightbox.tsx +5 -0
  416. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  417. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  418. package/src/List/List.tsx +1 -1
  419. package/src/List/ListItem.spec.md +205 -0
  420. package/src/List/ListItem.tsx +3 -3
  421. package/src/MetadataList/MetadataList.spec.md +187 -0
  422. package/src/MetadataList/MetadataListItem.spec.md +165 -0
  423. package/src/MobileNav/MobileNav.test.tsx +15 -0
  424. package/src/MobileNav/MobileNav.tsx +9 -1
  425. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  426. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  427. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
  428. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  429. package/src/MultiSelector/MultiSelector.test.tsx +270 -1
  430. package/src/MultiSelector/MultiSelector.tsx +25 -10
  431. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  432. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  433. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  434. package/src/NumberInput/NumberInput.spec.md +59 -37
  435. package/src/NumberInput/NumberInput.tsx +13 -3
  436. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  437. package/src/NumberInput/numberParser.ts +7 -45
  438. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  439. package/src/ProgressBar/ProgressBar.tsx +58 -10
  440. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  441. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  442. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  443. package/src/Resizable/useResizable.test.ts +43 -0
  444. package/src/Resizable/useResizable.ts +32 -9
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  446. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  447. package/src/Selector/Selector.test.tsx +90 -1
  448. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  449. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  450. package/src/Selector/useSelectorPresentation.ts +31 -3
  451. package/src/SideNav/SideNav.test.tsx +27 -0
  452. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  453. package/src/Skeleton/Skeleton.tsx +3 -1
  454. package/src/Slider/Slider.test.tsx +31 -0
  455. package/src/Slider/Slider.tsx +8 -3
  456. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  457. package/src/Spinner/Spinner.tsx +5 -3
  458. package/src/Stack/Stack.tsx +1 -1
  459. package/src/Stack/StackItem.tsx +1 -1
  460. package/src/TabList/TabList.test.tsx +58 -4
  461. package/src/TabList/TabList.tsx +16 -1
  462. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  463. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  464. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  465. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  466. package/src/Text/Text.tsx +1 -1
  467. package/src/Text/useTruncation.test.tsx +128 -1
  468. package/src/Text/useTruncation.ts +23 -5
  469. package/src/TextArea/TextArea.spec.md +10 -13
  470. package/src/TextArea/TextArea.test.tsx +60 -0
  471. package/src/TextArea/TextArea.tsx +15 -1
  472. package/src/TextInput/TextInput.test.tsx +21 -0
  473. package/src/TextInput/TextInput.tsx +3 -1
  474. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  475. package/src/TimeInput/Presentation.test.tsx +21 -0
  476. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  477. package/src/TimeInput/TimeInput.spec.md +45 -48
  478. package/src/TimeInput/TimeInput.test.tsx +76 -0
  479. package/src/TimeInput/TimeInput.tsx +36 -18
  480. package/src/TimeInput/TouchTimeField.tsx +4 -6
  481. package/src/Timestamp/Timestamp.spec.md +6 -12
  482. package/src/Timestamp/Timestamp.test.tsx +30 -5
  483. package/src/Timestamp/Timestamp.tsx +6 -2
  484. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  485. package/src/Toast/Toast.spec.md +14 -20
  486. package/src/Toast/Toast.tsx +104 -36
  487. package/src/Toast/ToastViewport.test.tsx +719 -1
  488. package/src/Toast/ToastViewport.tsx +100 -50
  489. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  490. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  491. package/src/Tokenizer/Tokenizer.tsx +14 -2
  492. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  493. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  494. package/src/Toolbar/Toolbar.spec.md +5 -6
  495. package/src/Toolbar/Toolbar.test.tsx +30 -0
  496. package/src/Toolbar/Toolbar.tsx +2 -1
  497. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  498. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  499. package/src/TopNav/TopNav.test.tsx +27 -0
  500. package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
  501. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  502. package/src/TopNav/TopNavMenu.test.tsx +17 -0
  503. package/src/TopNav/TopNavMenu.tsx +1 -0
  504. package/src/Typeahead/Typeahead.test.tsx +40 -0
  505. package/src/Typeahead/Typeahead.tsx +12 -1
  506. package/src/__tests__/pressState.ts +8 -4
  507. package/src/gestures/GestureArena.ts +35 -0
  508. package/src/gestures/GestureHost.ts +2065 -0
  509. package/src/gestures/GestureRecognizer.ts +18 -0
  510. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  511. package/src/gestures/PanGestureRecognizer.ts +4 -0
  512. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  513. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  514. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  515. package/src/gestures/TapGestureRecognizer.ts +4 -0
  516. package/src/gestures/VelocityTracker.ts +10 -3
  517. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  518. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  519. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  520. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  521. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  522. package/src/gestures/__tests__/arena.test.ts +5 -0
  523. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  524. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  525. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  526. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  527. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  528. package/src/gestures/__tests__/haptics.test.ts +187 -0
  529. package/src/gestures/__tests__/harness.ts +21 -2
  530. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  531. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  532. package/src/gestures/haptics.ts +203 -0
  533. package/src/gestures/nativeScroll.ts +277 -0
  534. package/src/gestures/touchAction.ts +191 -0
  535. package/src/gestures/touchActionRule.ts +157 -0
  536. package/src/gestures/types.ts +30 -9
  537. package/src/hooks/index.ts +5 -1
  538. package/src/hooks/modalAnnounce.ts +129 -72
  539. package/src/hooks/scrollKeyboardDelegation.test.ts +33 -0
  540. package/src/hooks/scrollKeyboardDelegation.ts +9 -3
  541. package/src/hooks/useAdaptivePresentation.ts +4 -1
  542. package/src/hooks/useAnnounce.doc.mjs +35 -3
  543. package/src/hooks/useAnnounce.replace.test.tsx +516 -0
  544. package/src/hooks/useAnnounce.ts +209 -102
  545. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
  546. package/src/hooks/useAnnounceRenderedText.ts +17 -3
  547. package/src/hooks/useBusyAnnouncement.ts +7 -10
  548. package/src/hooks/useMenuHover.ts +15 -1
  549. package/src/hooks/useScrollOverflow.doc.mjs +1 -1
  550. package/src/hooks/useScrollOverflow.test.tsx +102 -1
  551. package/src/hooks/useScrollOverflow.ts +59 -10
  552. package/src/i18n/generated-locales/en.generated.ts +7 -0
  553. package/src/i18n/generated-locales/pseudo.generated.ts +7 -0
  554. package/src/motion/README.md +11 -1
  555. package/src/motion/animator.test.ts +151 -3
  556. package/src/motion/animator.ts +149 -33
  557. package/src/theme/Theme.doc.mjs +1 -1
  558. package/src/theme/defineTheme.ts +117 -8
  559. package/src/theme/derivedVarRegistry.test.ts +4 -0
  560. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  561. package/src/theme/domainTokens/index.ts +6 -10
  562. package/src/theme/expandMotionScale.test.ts +1 -1
  563. package/src/theme/index.ts +5 -0
  564. package/src/theme/motionKnobs.ts +82 -0
  565. package/src/theme/resolveThemeValues.ts +68 -6
  566. package/src/theme/themeAdaptations.ts +52 -12
  567. package/src/theme/themeMotion.test.ts +620 -0
  568. package/src/theme/themeMotion.ts +221 -0
  569. package/src/theme/tokenReferences.ts +149 -0
  570. package/src/theme/tokens.stylex.ts +10 -0
  571. package/src/theme/tokens.ts +15 -158
  572. package/src/utils/focusReturn.ts +17 -6
  573. package/src/utils/getNodeText.test.ts +24 -0
  574. package/src/utils/getNodeText.ts +37 -0
  575. package/src/utils/interactionModality.test.ts +45 -1
  576. package/src/utils/interactionModality.ts +64 -5
  577. package/src/utils/interactionOverlay.stylex.ts +12 -3
  578. package/src/utils/plainDate.test.ts +90 -0
  579. package/src/utils/plainDate.ts +58 -3
  580. package/src/utils/presentation.test.ts +380 -0
  581. package/src/utils/presentation.ts +180 -0
  582. package/src/utils/pressFeedback.test.ts +138 -0
  583. package/src/utils/pressFeedback.ts +34 -11
  584. package/src/utils/pressPaintPriority.test.ts +80 -0
  585. package/src/utils/primaryClick.test.ts +38 -0
  586. package/src/utils/primaryClick.ts +28 -0
  587. package/src/utils/sharedMutationObserver.test.ts +54 -1
  588. package/src/utils/sharedMutationObserver.ts +87 -29
  589. package/src/utils/timeFormat.test.ts +204 -0
  590. package/src/utils/timeFormat.ts +314 -0
  591. package/src/utils/touchPresentation.stylex.ts +43 -0
  592. package/src/utils/touchPresentation.test.ts +140 -1
  593. package/src/utils/touchPresentation.ts +5 -3
  594. package/src/utils/unicodeDigits.test.ts +44 -0
  595. package/src/utils/unicodeDigits.ts +71 -0
@@ -1,19 +1,18 @@
1
1
  ---
2
2
  schema_version: 3
3
- template_version: 5
3
+ template_version: 7
4
4
  kind: component
5
5
  id: component:Drawer
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-30
11
11
  owners: [cixzhang, imdreamrunner]
12
12
  review_triggers: [public-api, behavior, layout, theming, accessibility]
13
13
  verified_by:
14
14
  [
15
15
  packages/core/src/Drawer/Drawer.test.tsx,
16
- packages/core/src/Drawer/DrawerHeader.test.tsx,
17
16
  packages/core/src/Layer/useLayerDismissal.test.tsx,
18
17
  packages/core/src/Layer/layerDismissalFamilies.test.tsx,
19
18
  .github/scripts/modal-close-visibility.js,
@@ -22,7 +21,7 @@ verified_by:
22
21
  ]
23
22
  modules: []
24
23
  families: [family:overlay-dismissal]
25
- design_specs: []
24
+ design_specs: [design:reduced-motion]
26
25
  architecture:
27
26
  [
28
27
  architecture:component-theming-surface,
@@ -32,24 +31,24 @@ architecture:
32
31
  architecture:react-component-runtime,
33
32
  ]
34
33
  contributing: []
35
- system_specs: [spec:AST-027/DEC-3]
34
+ system_specs: [spec:AST-027/DEC-3, spec:AST-094]
36
35
  ---
37
36
 
38
37
  # Drawer component contract
39
38
 
40
39
  ## Contract at a glance
41
40
 
42
- | Area | Contract |
43
- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
- | Current Core surface | `@astryxdesign/core` exports `Drawer`, `DrawerProps`, `DrawerHeader`, and `DrawerHeaderProps` from the package root and the `./Drawer` subpath (DEC-3); `@astryxdesign/lab` does not export them. `DrawerProps` extends `BaseProps<HTMLDialogElement>`, separately declares `ref`, requires `isOpen`, `onOpenChange`, `label`, and `children`, accepts `purpose` (`required`, `form`, or `info`; default `info`), an optional `padding` spacing step (FR16), and an optional `experimental` object (DEC-4), and has no `hasCloseButton`. Stable API admission and compatibility belong to `architecture:public-component-api`. |
45
- | Behavior | Drawer-owned visibility is controlled by the caller for a viewport-relative, full-height logical-side overlay. `hasScrim` at native open selects `showModal()` or a manual Popover API host; changing it while open is unsupported. The shared layer dismissal stack gates Escape and platform close for the full rendered lifetime, and `purpose` decides, as in Dialog, whether Escape, platform close, and scrim click request close. Drawer renders no close control of its own; `DrawerHeader` renders one only when given `onOpenChange`. The browser top layer owns paint order in both presentations, exit renders current children, and completed close returns focus to the element focused at open. |
46
- | End-user impact | A drawer paints no close control of its own; a close button appears in a `DrawerHeader` title row where the caller composes one. `form` drawers ignore scrim clicks, and `required` drawers ignore Escape, platform close, and scrim clicks; a modal `required` drawer is announced as an alert dialog. Shared topmost/IME Escape routing, top-layer paint order, one dismissal per request, and focus return apply in both presentations. |
47
- | Builder impact | Import from `@astryxdesign/core/Drawer` or the Core root. For a visible close action, compose `<DrawerHeader title onOpenChange>` in a `Layout` header slot, as `DialogHeader` is composed in Dialog. The Content area is inset by `--spacing-4` by default; `padding={0}` gives a full-bleed Content area. Set `purpose` where Dialog would. State and content remain caller-owned; Drawer renders the caller's current children during exit. Sibling composition is current consumer guidance, and a consumer `onKeyDown` that prevents default cancels Escape dismissal. |
48
- | Compatibility/readiness | Drawer is a Core component imported from `@astryxdesign/core/Drawer` (DEC-3). This record remains `draft`, and its candidate statements require approval. FR13–FR14 record shared-family and top-layer conformance; FR7 and FR15 record the Dialog alignment; FR16 records container padding. |
49
- | Review checks | Reject regional, docked, or block-axis models; claims that modality and scrim are currently independent; reintroduction of a Drawer-local Escape registry, a page-level z-index band, or a Drawer-owned close button; a dismissal policy that diverges from Dialog's `purpose`; claims that mixed-presentation or nested stacking is guaranteed beyond the shared stack's contract; a surviving `@astryxdesign/lab` export; or claims of stable API/theming compatibility that `architecture:public-component-api` has not admitted. |
50
- | Record context | `component:Drawer` FR1–FR16 and AR1–AR6 are draft candidate statements. Linked records govern only within their own declared authority and scope. |
41
+ | Area | Contract |
42
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | Public contract | `@astryxdesign/core` exports `Drawer`, `DrawerProps`, `DrawerHeader`, and `DrawerHeaderProps` from the package root and the `./Drawer` subpath (DEC-3); `@astryxdesign/lab` does not export them. `DrawerProps` extends `BaseProps<HTMLDialogElement>`, separately declares `ref`, requires `isOpen`, `onOpenChange`, `label`, and `children`, accepts `purpose` (`required`, `form`, or `info`; default `info`), an optional `padding` spacing step (FR16), and an optional `experimental` object (DEC-4), and has no `hasCloseButton`. Stable API admission and compatibility belong to `architecture:public-component-api`; `DrawerHeader` is recorded in `component:DrawerHeader`. |
44
+ | Behavior | Drawer-owned visibility is controlled by the caller for a viewport-relative, full-height logical-side overlay. `hasScrim` at native open selects `showModal()` or a manual Popover API host; changing it while open is unsupported. The shared layer dismissal stack gates Escape and platform close for the full rendered lifetime, and `purpose` decides, as in Dialog, whether Escape, platform close, and scrim click request close. Drawer renders no close control of its own. The browser top layer owns paint order in both presentations, exit renders current children, and completed close returns focus to the element focused at open. |
45
+ | End-user impact | A drawer paints no close control of its own; a close button appears in a `DrawerHeader` title row where the caller composes one. `form` drawers ignore scrim clicks, and `required` drawers ignore Escape, platform close, and scrim clicks; a modal `required` drawer is announced as an alert dialog. Shared topmost/IME Escape routing, top-layer paint order, one dismissal per request, and focus return apply in both presentations. |
46
+ | Builder impact | Import from `@astryxdesign/core/Drawer` or the Core root. For a visible close action, compose `<DrawerHeader title onOpenChange>` in a `Layout` header slot, as `DialogHeader` is composed in Dialog. The Content area is inset by `--spacing-4` by default; `padding={0}` gives a full-bleed Content area. Set `purpose` where Dialog would. State and content remain caller-owned; Drawer renders the caller's current children during exit. Sibling composition is current consumer guidance, and a consumer `onKeyDown` that prevents default cancels Escape dismissal. |
47
+ | Compatibility/readiness | Records shipped behavior; no public change. Drawer is a Core component imported from `@astryxdesign/core/Drawer` (DEC-3). Five implementation gaps, listed under Current-state impact. |
48
+ | Review checks | Reject regional, docked, or block-axis models; claims that modality and scrim are currently independent; reintroduction of a Drawer-local Escape registry, a page-level z-index band, or a Drawer-owned close button; a dismissal policy that diverges from Dialog's `purpose`; claims that mixed-presentation or nested stacking is guaranteed beyond the shared stack's contract; a surviving `@astryxdesign/lab` export; or claims of stable API/theming compatibility that `architecture:public-component-api` has not admitted. |
49
+ | Governing rules | `family:overlay-dismissal`; `architecture:layer-runtime`; `architecture:container-padding`; `spec:AST-027/DEC-3`; `design:reduced-motion`. |
51
50
 
52
- This table summarizes the draft body below; it does not change this record's declared authority.
51
+ This table is a review projection; the body below is authoritative.
53
52
 
54
53
  ## Intent
55
54
 
@@ -58,17 +57,17 @@ floats over the current page without reflowing it. It supports modal inspection
58
57
  with a scrim and non-modal master-detail inspection that leaves the page behind
59
58
  available.
60
59
 
61
- This draft records the viewport-only Core component: its shared layer
60
+ This record covers the viewport-only Core component: its shared layer
62
61
  lifecycle, its Dialog-aligned close control and dismissal policy (DEC-1), and its
63
62
  container padding (DEC-2). It introduces no scope concept. In particular, it
64
63
  does not revive a regional, pane-scoped, or container-targeted Drawer model.
65
64
 
66
65
  ## Compatibility and migration
67
66
 
68
- - Released default preserved: `not yet released` in Core.
69
- - Compatibility class: additive in Core (root and `./Drawer` subpath exports);
70
- `@astryxdesign/lab`, which publishes only canary builds, does not export
71
- Drawer.
67
+ - Released default preserved: `yes`.
68
+ - Compatibility class: records shipped behavior; no public change. Core
69
+ exports Drawer from the root and the `./Drawer` subpath; `@astryxdesign/lab`
70
+ does not.
72
71
  - Container padding (FR16, DEC-2): the `padding` prop or a theme's `padding` on
73
72
  `drawer` insets the Content area through container tokens. With neither set
74
73
  the inset is `--spacing-4`, as in Dialog; `padding={0}` keeps a full-bleed
@@ -80,12 +79,32 @@ does not revive a regional, pane-scoped, or container-targeted Drawer model.
80
79
  - Controlled/uncontrolled behavior: unchanged for Drawer-owned paths; callers
81
80
  provide `isOpen`, while direct native mutation through the public dialog ref is
82
81
  outside that guarantee.
83
- - Migration decision: no codemod, because `@astryxdesign/lab` publishes only
84
- canary builds. Canary callers import Drawer and DrawerHeader from
85
- `@astryxdesign/core/Drawer` instead of `@astryxdesign/lab`.
82
+ - Migration decision: none.
86
83
 
87
84
  Consumer migration instructions belong in consumer docs and release notes.
88
85
 
86
+ ## Current-state impact
87
+
88
+ - **Drawer builds its own top layer, exit, and backdrop.** Drawer mounts its own
89
+ modal and popover hosts, runs its own exit hold, and paints its own scrim.
90
+ `architecture:layer-runtime` INV14 has a layer use the runtime's top-layer
91
+ mounting, presentation hold, exit copy, and backdrop tint.
92
+ - **Reduced motion keeps a 0.01 s slide on every pointer.** Under reduced
93
+ motion the slide and the scrim transition still run for 0.01 s. For a fine
94
+ pointer, `design:reduced-motion` DR1 forbids the travel and DR3 allows no
95
+ animated property; a touch user gets no opacity fade, which DR2 calls for.
96
+ - **A tap during the entrance is not held.** Under a finger, a scrim tap that
97
+ lands while the drawer is still entering closes an `info` drawer, and a tap
98
+ or drag reaches the panel. `spec:AST-094` FR35 holds pointer input until the
99
+ entrance finishes.
100
+ - **A consumer `onCancel` never runs.** Drawer sets its own `onCancel` after
101
+ the caller's props, so a caller's `onCancel` is dropped.
102
+ `architecture:public-component-api` INV5 has accepted event inputs reach the
103
+ element.
104
+ - **The container-padding record does not list Drawer.** Drawer's Content
105
+ area publishes its inset (FR16, DEC-2), but `architecture:container-padding`
106
+ does not name Drawer among its container publishers.
107
+
89
108
  ## Ownership boundary
90
109
 
91
110
  **Owns**
@@ -100,8 +119,6 @@ Consumer migration instructions belong in consumer docs and release notes.
100
119
  - The Content area's container padding, published per
101
120
  `architecture:container-padding`; the root dialog remains the overlay
102
121
  boundary owner.
103
- - `DrawerHeader`: a title row with optional subtitle and start/end content, and a
104
- close action rendered only when `onOpenChange` is passed.
105
122
  - Registering the layer with the shared dismissal stack for its rendered
106
123
  lifetime. Escape and platform-close routing belong to the shared owner; paint
107
124
  order in both presentations belongs to the browser top layer.
@@ -116,45 +133,45 @@ Consumer migration instructions belong in consumer docs and release notes.
116
133
  modality and scrim axes. Those are not current Drawer concepts.
117
134
  - Cross-family Escape and platform-close ordering — owned by
118
135
  `family:overlay-dismissal`; Drawer participates through the shared owner.
119
- - Button painting for the `DrawerHeader` close action — delegated to
120
- `component:Button`; header row layout is delegated to `LayoutHeader`.
136
+ - `DrawerHeader`, its title row, close action, and targets — owned by
137
+ `component:DrawerHeader`.
121
138
 
122
139
  ## Public concepts
123
140
 
124
141
  Consumer prop syntax and examples remain in `Drawer.doc.mjs`.
125
142
 
126
- | Concept | Closed values or states | Meaning | Availability by state | Default | Owner | Stability | Invalid-value behavior |
127
- | ------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------- | ------------------ | ----------------------------------------------- | ----------------------------------------------------- |
128
- | visibility | open, closed | whether caller-controlled Drawer presentation is requested | all presentations | caller-controlled | `component:Drawer` | draft Core contract | required controlled value |
129
- | logical edge | inline start, inline end | viewport edge from which the panel enters and exits | open and exiting | inline end | `component:Drawer` | draft Core contract | closed type rejects other values |
130
- | presentation | modal with scrim, non-modal without scrim | native host mode and associated document semantics | chosen when the native host opens; live changes are unsupported | modal with scrim | `component:Drawer` | draft Core contract | one current boolean selects the initial mode |
131
- | inline-size budget | pixel number or valid CSS length | desktop inline size and reveal-mode mobile cap | desktop and mobile page-reveal mode | `400px` | `component:Drawer` | draft Core contract | invalid CSS lengths are unsupported |
132
- | mobile coverage | page reveal, full viewport | whether narrow viewports retain a visible page strip | viewports at or below the mobile boundary | 56px page reveal | `component:Drawer` | experimental (DEC-4) | closed boolean |
133
- | dismissal purpose | info, form, required | which implicit requests close: `info` allows Escape, platform close, and scrim click; `form` allows Escape and platform close; `required` allows none | all presentations; scrim click applies to modal only | info | `component:Drawer` | draft Core contract matching `component:Dialog` | closed type rejects other values |
134
- | header close action | rendered, absent | whether `DrawerHeader` renders its close button | wherever `DrawerHeader` is composed | absent unless `onOpenChange` is passed | `component:Drawer` | draft Core contract | an omitted callback renders no button |
135
- | content inset | spacing step, theme `drawer` padding, or the default | container padding of the scrolling Content area, published to descendants per `architecture:container-padding` | all presentations | `--spacing-4`, as in Dialog | `component:Drawer` | draft Core contract matching `component:Dialog` | closed numeric type rejects other values |
136
- | sibling order | earlier opened, later opened, exiting, closed | shared-stack Escape eligibility and browser top-layer paint order | sibling Drawers in either presentation | later opened is topmost | `component:Drawer` | draft Core contract | nested composition is outside the documented contract |
143
+ | Concept | Closed values or states | Meaning | Availability by state | Default | Owner | Stability | Invalid-value behavior |
144
+ | ------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------- | ------------------------ | ----------------------------- | ----------------------------------------------------- |
145
+ | visibility | open, closed | whether caller-controlled Drawer presentation is requested | all presentations | caller-controlled | `component:Drawer` | Core contract | required controlled value |
146
+ | logical edge | inline start, inline end | viewport edge from which the panel enters and exits | open and exiting | inline end | `component:Drawer` | Core contract | closed type rejects other values |
147
+ | presentation | modal with scrim, non-modal without scrim | native host mode and associated document semantics | chosen when the native host opens; live changes are unsupported | modal with scrim | `component:Drawer` | Core contract | one current boolean selects the initial mode |
148
+ | inline-size budget | pixel number or valid CSS length | desktop inline size and reveal-mode mobile cap | desktop and mobile page-reveal mode | `400px` | `component:Drawer` | Core contract | invalid CSS lengths are unsupported |
149
+ | mobile coverage | page reveal, full viewport | whether narrow viewports retain a visible page strip | viewports at or below the mobile boundary | 56px page reveal | `component:Drawer` | experimental (DEC-4) | closed boolean |
150
+ | dismissal purpose | info, form, required | which implicit requests close: `info` allows Escape, platform close, and scrim click; `form` allows Escape and platform close; `required` allows none | all presentations; scrim click applies to modal only | info | `component:Drawer` | Core contract matching Dialog | closed type rejects other values |
151
+ | header close action | rendered, absent | whether `DrawerHeader` renders its close button | wherever `DrawerHeader` is composed | absent unless `onOpenChange` is passed | `component:DrawerHeader` | Core contract | an omitted callback renders no button |
152
+ | content inset | spacing step, theme `drawer` padding, or the default | container padding of the scrolling Content area, published to descendants per `architecture:container-padding` | all presentations | `--spacing-4`, as in Dialog | `component:Drawer` | Core contract matching Dialog | closed numeric type rejects other values |
153
+ | sibling order | earlier opened, later opened, exiting, closed | shared-stack Escape eligibility and browser top-layer paint order | sibling Drawers in either presentation | later opened is topmost | `component:Drawer` | Core contract | nested composition is outside the documented contract |
137
154
 
138
155
  ## Behavioral and layout contract
139
156
 
140
- | ID | Candidate invariant | Basis | Draft review state |
141
- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
142
- | FR1 | Drawer MUST remain a viewport-relative, full-block-size overlay on the logical inline-start or inline-end edge and MUST NOT reserve layout space or reflow the page beneath it. | Current source, docs, stories, and tests | Verified current behavior; no new behavior decided |
143
- | FR2 | The logical edge selected for an open Drawer MUST remain latched through its exit even when caller state changes the live edge prop during close. | Current exit-side test and implementation | Verified current behavior |
144
- | FR3 | The desktop inline-size budget MUST accept pixel numbers or CSS lengths and remain bounded by the viewport. At or below 640px, page-reveal mode MUST preserve 56px and use `width` as a cap; full-mobile mode MUST override `width` with `100dvw`. | Current docs, styles, and width tests | Verified current behavior |
145
- | FR4 | Visibility through Drawer-owned behavior MUST follow `isOpen`. Uncanceled Escape routed by the shared stack and uncanceled modal-backdrop activation request `false` when `purpose` allows (FR15), and a `DrawerHeader` close action requests `false` through the callback it is given; Drawer prevents its handled native `cancel` event from closing the dialog directly. During controlled exit, Drawer keeps its host and content region mounted while rendering the caller's current `children`. | Current prop contract and lifecycle tests | Verified for Drawer-owned paths; direct native mutation through the public ref is outside this guarantee |
146
- | FR5 | At open time, `hasScrim={true}` MUST select native `showModal()` with `aria-modal`, a focus trap, body scroll locking, and a visible backdrop; `hasScrim={false}` MUST select a manual Popover API host that leaves the page behind interactive. Changing `hasScrim` while open is not a supported native-mode transition. | Current source, docs, and initial-mode tests | Initial modes verified; live presentation changes unsupported |
147
- | FR6 | An uncanceled modal root/backdrop click (`target === currentTarget` while live `hasScrim` is true) MUST request close when `purpose` is `info`, and MUST NOT for `form` or `required`. Descendant clicks and dialog-root clicks while live `hasScrim` is false MUST NOT request close, and non-modal presentation MUST NOT install an invisible outside-pointer dismissal plane. | Current click tests | Verified current behavior |
148
- | FR7 | Drawer MUST NOT render a close control of its own. `DrawerHeader` MUST render a close action with an accessible name only when `onOpenChange` is passed and call it with `false` without taking open-state ownership. It renders its title as an h2 and neither moves focus on mount nor names the Drawer. | DEC-1; close-control and `DrawerHeader.test.tsx` suites | Owner decision (DEC-1); pending approval |
149
- | FR8 | Opening MUST present the native host, capture the element focused before opening, and attempt to focus the first rendered `[data-autofocus]` descendant, if any. After a completed controlled close releases the native host, Drawer MUST attempt to restore focus to the captured element. The non-modal host's synthetic `close` event MUST dispatch after that restore attempt — matching the task-queued native `dialog.close()` event — so a consumer close listener keeps the last word on final focus. | Current presence hook and focus tests | Verified for completed controlled close |
150
- | FR9 | Closing MUST retain the panel host, content region, edge, and native presentation while the exit remains visible and continue rendering the caller's current `children`. The transform transition is authoritative, a computed-duration backstop prevents a lost event from stranding the host, and native release and React hiding land together so no frame paints outside the top layer. | Current lifecycle tests and browser close-visibility guard | Observable outcome verified |
151
- | FR10 | Sibling Drawers MUST be composed as siblings. The last-opened present sibling owns the first Escape request through the shared stack; a closing sibling retains that ownership through its visible exit, and reopening registers a new active cycle so one request never closes two surfaces. Paint order in both presentations follows the browser top layer's chronological order. | Current docs, shared stack, and tests | Verified current behavior |
152
- | FR11 | At this commit, `DrawerProps` extends `BaseProps<HTMLDialogElement>` and separately declares `ref`. The root filters `open`, merges `xstyle`, `className`, and `style`, forwards remaining unclaimed props, composes consumer `onClick`, forwards consumer `onKeyDown` ahead of the shared document-level Escape owner, and owns `aria-label`, `aria-modal`, `onCancel`, and, for a modal `required` drawer, `role="alertdialog"`. A consumer `onKeyDown` that prevents default cancels shared-stack Escape dismissal for the press. | Current source and focused keyboard tests | Recorded current behavior; not a stable compatibility decision |
153
- | FR12 | At this commit, the painted root dialog emits the documented `drawer` target and `side` selector axis and applies the container-padding reset. `DrawerHeader` emits `drawer-header`, `drawer-header-start-content`, `drawer-header-title-block`, `drawer-header-end-content`, and `drawer-header-close-icon`, mirroring `DialogHeader`. No separate Drawer target is currently emitted for content or scrim; future target qualification remains owned by `architecture:component-theming-surface`. | Current source, docs, and structural target metadata | Current reachability only; no target-admission decision |
154
- | FR13 | Drawer MUST join the shared dismissal stack for its full rendered lifetime, provide logical depth to descendant layers, route platform close through the same topmost/IME decision, and preserve consumer `preventDefault()` ownership. | Shared family contract and focused tests | Verified current conformance |
155
- | FR14 | Non-modal Drawer MUST use a manual Popover API host so cross-surface order comes from the browser top layer rather than a page-level z-index band. The reduced fallback may use `dialog.show()` only below the Popover API support floor. | Current source, browser guard, and `spec:AST-027` | Verified current conformance |
156
- | FR15 | `purpose` MUST match `component:Dialog`. `info` (default) lets Escape, platform close, and a modal scrim click request close; `form` lets Escape and platform close request close and ignores scrim clicks; `required` lets none request close, registers with the shared stack as `block` so the request does not reach a lower layer, and exposes a modal drawer as `role="alertdialog"`. Without a scrim, `form` and `info` behave the same. | DEC-1; purpose suite; `family:overlay-dismissal` Escape behavior | Owner decision (DEC-1); pending approval |
157
- | FR16 | The Content area pads caller content by the drawer's container inset, `--spacing-4` by default as in Dialog, and publishes the applied inset to descendants as a container publisher. Its block-end edge applies the container inset plus the home-indicator safe area while publishing the inset alone, so a bleed child subtracts only the inset and the safe area survives below it. The root dialog keeps the container-padding reset (FR12). | `architecture:container-padding`; container padding suite | Candidate (DEC-2); prototype pending owner approval |
157
+ | ID | Invariant | Basis |
158
+ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
159
+ | FR1 | Drawer MUST remain a viewport-relative, full-block-size overlay on the logical inline-start or inline-end edge and MUST NOT reserve layout space or reflow the page beneath it. | Current source, docs, stories, and tests |
160
+ | FR2 | The logical edge selected for an open Drawer MUST remain latched through its exit even when caller state changes the live edge prop during close. | Current exit-side test and implementation |
161
+ | FR3 | The desktop inline-size budget MUST accept pixel numbers or CSS lengths and remain bounded by the viewport. At or below 640px, page-reveal mode MUST preserve 56px and use `width` as a cap; full-mobile mode MUST override `width` with `100dvw`. | Current docs, styles, and width tests |
162
+ | FR4 | Visibility through Drawer-owned behavior MUST follow `isOpen`. Uncanceled Escape routed by the shared stack and uncanceled modal-backdrop activation request `false` when `purpose` allows (FR15), and a `DrawerHeader` close action requests `false` through the callback it is given; Drawer prevents its handled native `cancel` event from closing the dialog directly. During controlled exit, Drawer keeps its host and content region mounted while rendering the caller's current `children`. | Current prop contract and lifecycle tests |
163
+ | FR5 | At open time, `hasScrim={true}` MUST select native `showModal()` with `aria-modal`, a focus trap, body scroll locking, and a visible backdrop; `hasScrim={false}` MUST select a manual Popover API host that leaves the page behind interactive. Changing `hasScrim` while open is not a supported native-mode transition. | Current source, docs, and initial-mode tests |
164
+ | FR6 | An uncanceled modal root/backdrop click (`target === currentTarget` while live `hasScrim` is true) MUST request close when `purpose` is `info`, and MUST NOT for `form` or `required`. Descendant clicks and dialog-root clicks while live `hasScrim` is false MUST NOT request close, and non-modal presentation MUST NOT install an invisible outside-pointer dismissal plane. | Current click tests |
165
+ | FR7 | Drawer MUST NOT render a close control of its own. A visible close action is caller composition, normally `DrawerHeader` with `onOpenChange` (`component:DrawerHeader` FR3), which requests `false` through that callback without taking open-state ownership. | DEC-1; close-control suite |
166
+ | FR8 | Opening MUST present the native host, capture the element focused before opening, and attempt to focus the first rendered `[data-autofocus]` descendant, if any. After a completed controlled close releases the native host, Drawer MUST attempt to restore focus to the captured element. The non-modal host's synthetic `close` event MUST dispatch after that restore attempt — matching the task-queued native `dialog.close()` event — so a consumer close listener keeps the last word on final focus. | Current presence hook and focus tests |
167
+ | FR9 | Closing MUST retain the panel host, content region, edge, and native presentation while the exit remains visible and continue rendering the caller's current `children`. The transform transition is authoritative, a computed-duration backstop prevents a lost event from stranding the host, and native release and React hiding land together so no frame paints outside the top layer. | Current lifecycle tests and browser close-visibility guard |
168
+ | FR10 | Sibling Drawers MUST be composed as siblings. The last-opened present sibling owns the first Escape request through the shared stack; a closing sibling retains that ownership through its visible exit, and reopening registers a new active cycle so one request never closes two surfaces. Paint order in both presentations follows the browser top layer's chronological order. | Current docs, shared stack, and tests |
169
+ | FR11 | `DrawerProps` extends `BaseProps<HTMLDialogElement>` and separately declares `ref`. The root filters `open`, merges `xstyle`, `className`, and `style`, forwards remaining unclaimed props, composes consumer `onClick`, forwards consumer `onKeyDown` ahead of the shared document-level Escape owner, and owns `aria-label`, `aria-modal`, and, for a modal `required` drawer, `role="alertdialog"`. Implementation gap: a consumer `onCancel` is dropped (Current-state impact). A consumer `onKeyDown` that prevents default cancels shared-stack Escape dismissal for the press. | Current source and focused keyboard tests |
170
+ | FR12 | The painted root dialog emits the documented `drawer` target and `side` selector axis and applies the container-padding reset. `DrawerHeader`'s targets are owned by `component:DrawerHeader` FR5. No separate Drawer target is currently emitted for content or scrim; future target qualification remains owned by `architecture:component-theming-surface`. | Current source, docs, and structural target metadata |
171
+ | FR13 | Drawer MUST join the shared dismissal stack for its full rendered lifetime, provide logical depth to descendant layers, route platform close through the same topmost/IME decision, and preserve consumer `preventDefault()` ownership. | Shared family contract and focused tests |
172
+ | FR14 | Non-modal Drawer MUST use a manual Popover API host so cross-surface order comes from the browser top layer rather than a page-level z-index band. The reduced fallback may use `dialog.show()` only below the Popover API support floor. | Current source, browser guard, and `spec:AST-027` |
173
+ | FR15 | `purpose` takes the same values and default as Dialog's prop. `info` (default) lets Escape, platform close, and a modal scrim click request close; `form` lets Escape and platform close request close and ignores scrim clicks; `required` lets none request close, registers with the shared stack as `block` so the request does not reach a lower layer, and exposes a modal drawer as `role="alertdialog"`. Without a scrim, `form` and `info` behave the same. | DEC-1; purpose suite; `family:overlay-dismissal` Escape behavior |
174
+ | FR16 | The Content area pads caller content by the drawer's container inset, `--spacing-4` by default as in Dialog, and publishes the applied inset to descendants as a container publisher would; `architecture:container-padding` does not yet admit Drawer as one (Current-state impact). Its block-end edge applies the container inset plus the home-indicator safe area while publishing the inset alone, so a bleed child subtracts only the inset and the safe area survives below it. The root dialog keeps the container-padding reset (FR12). | `architecture:container-padding`; container padding suite |
158
175
 
159
176
  ### Allowed variation
160
177
 
@@ -236,31 +253,34 @@ Consumer prop syntax and examples remain in `Drawer.doc.mjs`.
236
253
  - **AR4 — Focus lifecycle.** Focus enters visible Drawer content through the
237
254
  documented autofocus/native path and, after a completed controlled close, Drawer
238
255
  attempts to restore focus to the element that was active when the Drawer opened.
239
- - **AR5 — Close affordance.** Drawer provides no close control of its own. When
240
- rendered, the `DrawerHeader` close action keeps its translated accessible name
241
- and Button-owned keyboard/focus behavior in both presentations.
256
+ - **AR5 — Close affordance.** Drawer provides no close control of its own; the
257
+ `DrawerHeader` close action's name and behavior are owned by
258
+ `component:DrawerHeader`.
242
259
  - **AR6 — Direction and motion.** Logical inset placement and slide-direction
243
260
  mirroring follow DOM directionality from the nearest effective `dir` attribute,
244
261
  including an explicit local override or an inherited direction. As required by
245
262
  `design:bidirectionality`, a region whose direction differs from its ancestor
246
263
  describes that direction in DOM `dir`; a CSS-only `direction` declaration is
247
- not a supported substitute. Motion reduces under `prefers-reduced-motion`
248
- without changing the final state.
264
+ not a supported substitute. Under reduced motion the entrance and exit are an
265
+ opacity-only fade of 200 ms or less on a coarse pointer
266
+ (`design:reduced-motion` DR2) and immediate otherwise (DR3), without changing
267
+ the final state, apart from the reduced-motion gap (Current-state impact).
249
268
 
250
269
  ## Design relationships
251
270
 
252
- | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
253
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------- | ------------------ |
254
- | Panel | Paints the full-height side surface and owns edge, width, border, shadow, and motion. | Current source and public docs | Prominent | FR1–FR3, FR9, FR12 |
255
- | Content region | Provides full-height scrolling and the container inset for caller-owned inspector content. | Current source and public docs | Prominent | FR4, FR16, AV1 |
256
- | Header | `DrawerHeader` supplies the title row and, when given `onOpenChange`, the close action. | `component:Button`, `LayoutHeader` | Supporting | FR7, FR12, AR5 |
257
- | Modal scrim | At initial modal open, communicates the scrim-backed presentation and provides root-click activation behind the panel. | Current source and public docs | Supporting | FR5, FR6, AR2 |
258
- | Page reveal | Preserves overlay context on narrow viewports unless full coverage is requested. | Current public docs | Supporting | FR3 |
271
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
272
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------ | -------------- | ------------------ |
273
+ | Panel | Paints the full-height side surface and owns edge, width, border, shadow, and motion. | Current source and public docs | Prominent | FR1–FR3, FR9, FR12 |
274
+ | Content region | Provides full-height scrolling and the container inset for caller-owned inspector content. | Current source and public docs | Prominent | FR4, FR16, AV1 |
275
+ | Header | `DrawerHeader` supplies the title row and, when given `onOpenChange`, the close action. | `component:DrawerHeader` | Supporting | FR7, AR5 |
276
+ | Modal scrim | At initial modal open, communicates the scrim-backed presentation and provides root-click activation behind the panel. | Current source and public docs | Supporting | FR5, FR6, AR2 |
277
+ | Page reveal | Preserves overlay context on narrow viewports unless full coverage is requested. | Current public docs | Supporting | FR3 |
259
278
 
260
- The root Panel carries the current `drawer` target and reflects `side`, and
261
- `DrawerHeader` carries its five `drawer-header` targets. No separate Drawer target
262
- is currently reachable for Content region or Modal scrim. This draft does not
263
- decide future target qualification.
279
+ The root Panel carries the current `drawer` target and reflects `side`;
280
+ `DrawerHeader`'s targets are owned by `component:DrawerHeader`. No separate
281
+ Drawer target is reachable for Content region or Modal scrim, and future target
282
+ qualification is not decided here. No anatomy-theming map is recorded yet; the
283
+ block is optional during migration, so the map is deferred.
264
284
 
265
285
  ## Family and system relationships
266
286
 
@@ -276,13 +296,12 @@ decide future target qualification.
276
296
  FR11 records current DOM/ref/event reachability without making a stable
277
297
  compatibility decision.
278
298
  - `architecture:react-component-runtime` owns effect/resource cleanup, native-host
279
- synchronization, and node/lifecycle safety. This draft records Drawer-owned
299
+ synchronization, and node/lifecycle safety. This record covers Drawer-owned
280
300
  visible close and focus-handoff outcomes.
281
301
  - `architecture:component-theming-surface` owns target qualification and future
282
- anatomy mapping; this draft records the existing `drawer` target only.
302
+ anatomy mapping; this record covers the existing `drawer` target only.
283
303
  - `architecture:container-padding` owns the shared container inset protocol.
284
- Admitting the Content area as a container publisher (FR16) amends that
285
- `current` record — an owner-approved change outside this draft.
304
+ It does not yet admit Drawer's Content area as a container publisher (FR16).
286
305
  - `spec:AST-027/DEC-3` requires equivalent floating interactions to use an
287
306
  applicable native top-layer host. Modal `showModal()` and non-modal manual
288
307
  Popover hosting satisfy that cross-surface route; the documented reduced
@@ -292,17 +311,26 @@ decide future target qualification.
292
311
 
293
312
  | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
294
313
  | --------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
295
- | FR1–FR7 | `Drawer.test.tsx` render, initial-mode, click, sizing, side, and control suites plus `DrawerHeader.test.tsx` | closed/open, both edges, initial modal/non-modal modes, mobile widths | Layout reflows; an initial mode gains the wrong semantics; uncanceled root-click or full-mobile sizing becomes nondeterministic. | `audit:Drawer/behavior` |
314
+ | FR1–FR7 | `Drawer.test.tsx` render, initial-mode, click, sizing, side, and control suites | closed/open, both edges, initial modal/non-modal modes, mobile widths | Layout reflows; an initial mode gains the wrong semantics; uncanceled root-click or full-mobile sizing becomes nondeterministic. | `audit:Drawer/behavior` |
296
315
  | FR11 | Drawer source and focused keyboard composition/cancellation tests | filtered `open`, merged styling/ref, forwarded unclaimed props, owned ARIA/cancel, composed click, forwarded keydown | Current forwarding changes without review or shared-stack Escape ignores documented consumer cancellation. | `audit:Drawer/public-api` |
297
316
  | FR8, AR1–AR5 | `Drawer.test.tsx` label forwarding, autofocus, close, and focus-return suites plus the browser guard's final-focus checks | initial modal/non-modal modes, autofocus target, connected previously focused element, close listener retargeting focus | Focus moves before presentation, completed controlled close fails to restore focus, the synthetic close overrides a consumer listener's focus target, or label forwarding/dismissal naming breaks. | `audit:Drawer/accessibility` |
298
317
  | FR9, PR1–PR2 | close timing tests, browser close-visibility guard, and presence-hook source inspection | controlled close, transform end, unrelated transition, lost-event backstop | Native presentation ends before visible exit, the host is stranded, cleanup paths retain resources, or an intermediate frame paints outside native presentation. | `audit:Drawer/motion` |
299
318
  | FR10, FR13, AR3 | Drawer shared-stack suites plus `useLayerDismissal` and family tests | siblings, closing top, reopen, unmount, nested descendant | One Escape closes two surfaces, a closing host drops ownership, or a reopened sibling keeps stale order. | `audit:Drawer/layers` |
300
319
  | FR14 | Drawer source plus `spec:AST-027` impact inventory and native-host browser guard | modal host, manual-popover host, reduced fallback | Non-modal presentation returns to a page-level band or loses native-host ordering. | `audit:Drawer/layers` |
301
320
  | FR15 | `Drawer.test.tsx` purpose suite | `info`, `form`, `required`; modal and non-modal; a sibling behind a required drawer | A `form` drawer closes on a scrim click, a `required` drawer closes or lets Escape reach the drawer behind it, or its role semantics drift. | `audit:Drawer/behavior` |
302
- | FR12 | source, `Drawer.doc.mjs`, `DrawerHeader.doc.mjs`, the `DrawerHeader` target test, and current target discovery | start/end root Panel and inherited container context | Current root target/axis or padding reset changes, or the record claims an unreachable child target or decides future admission. | `audit:Drawer/theming` |
321
+ | FR12 | source, `Drawer.doc.mjs`, and current target discovery | start/end root Panel and inherited container context | Current root target/axis or padding reset changes, or the record claims an unreachable child target or decides future admission. | `audit:Drawer/theming` |
303
322
  | FR16 | `Drawer.test.tsx` container padding suites; `defineTheme.test.ts` drawer mapping; browser evidence | default, `padding` prop, theme `padding`, lone padded Section child, Layout composition | The published inset differs from the applied padding, the default drawer changes geometry, or the block-end safe area is lost or double-subtracted. | `audit:Drawer/theming` |
304
323
  | AR6 | `DrawerDirection.test.tsx` DOM-direction and closed/entry parity suites plus exact-head Storybook RTL evidence | start/end; self-applied, inherited, and nested opposite `dir`; closed and opening transforms | Any effective DOM direction or local override moves the panel from a different edge than its logical placement, or closed and opening transforms diverge. | `audit:Drawer/accessibility` |
305
324
 
325
+ Read from source, with no focused test yet:
326
+
327
+ - FR1: not reserving layout space or reflowing the page.
328
+ - FR5: body scroll locking for a modal drawer.
329
+ - FR9: the computed-duration backstop.
330
+ - FR13: logical depth for descendant layers, and platform-close routing.
331
+ - FR14: the `dialog.show()` fallback below the Popover API support floor.
332
+ - PR1–PR2: listener, timer, presentation, and scroll-lock cleanup.
333
+
306
334
  ## Decision log
307
335
 
308
336
  - **DEC-1 — The close control and dismissal policy match Dialog** (owner
@@ -345,6 +373,9 @@ decide future target qualification.
345
373
  - Should an explicit `padding` prop keep adding the block-end safe area
346
374
  (DEC-2), or adopt Dialog's fullscreen rule, where an explicit value replaces
347
375
  the safe-area fallback entirely?
376
+ - How `DrawerHeader`'s `endContentEdgeCompensation` axis values map onto
377
+ `endContentEdge` treatment values is carried in `component:DrawerHeader`'s
378
+ open questions.
348
379
 
349
380
  Regional placement, independent modality/scrim axes, and block-axis sheets
350
381
  are outside the current component boundary. Any future proposal for them requires
@@ -60,6 +60,19 @@ afterEach(() => {
60
60
  });
61
61
 
62
62
  describe('Drawer', () => {
63
+ it('clips its panel with `clip`, not `hidden`: the panel is no scroll container', () => {
64
+ // `hidden` clips exactly as well, so it reads as a harmless tidy-up, but it
65
+ // makes the panel a scroll container, and in Chromium its overscroll
66
+ // containment then holds a drag on the page beside a non-modal drawer
67
+ // (__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts).
68
+ render(
69
+ <Drawer isOpen onOpenChange={() => {}} label="Details" hasScrim={false}>
70
+ Content
71
+ </Drawer>,
72
+ );
73
+ expect(getComputedStyle(screen.getByRole('dialog')).overflow).toBe('clip');
74
+ });
75
+
63
76
  it('renders children when open', () => {
64
77
  render(
65
78
  <Drawer isOpen onOpenChange={() => {}} label="Host details">
@@ -240,6 +253,28 @@ describe('Drawer', () => {
240
253
  expect(cancelEvent.defaultPrevented).toBe(true);
241
254
  });
242
255
 
256
+ // A file input fires a bubbling `cancel` at itself when its picker is
257
+ // dismissed (Escape, or Cancel in the chooser). It reaches the <dialog>
258
+ // like a close request but is not one.
259
+ it('stays open when a file picker inside it is dismissed', () => {
260
+ const handleOpenChange = vi.fn();
261
+ render(
262
+ <Drawer isOpen onOpenChange={handleOpenChange} label="Details">
263
+ <input type="file" aria-label="Picture" />
264
+ </Drawer>,
265
+ );
266
+ const fromPicker = new Event('cancel', {bubbles: true, cancelable: true});
267
+ screen.getByLabelText('Picture').dispatchEvent(fromPicker);
268
+ expect(handleOpenChange).not.toHaveBeenCalled();
269
+ expect(fromPicker.defaultPrevented).toBe(false);
270
+ // Its own close request still closes it.
271
+ screen
272
+ .getByRole('dialog')
273
+ .dispatchEvent(new Event('cancel', {cancelable: true}));
274
+ expect(handleOpenChange).toHaveBeenCalledTimes(1);
275
+ expect(handleOpenChange).toHaveBeenCalledWith(false);
276
+ });
277
+
243
278
  it('ignores other keys', () => {
244
279
  const handleOpenChange = vi.fn();
245
280
  render(
@@ -52,6 +52,7 @@
52
52
  * SYNC: When modified, update these files to stay in sync:
53
53
  * - /packages/core/src/Drawer/Drawer.doc.mjs (props table, features, usage)
54
54
  * - /packages/core/src/Drawer/Drawer.test.tsx (tests for new/changed behavior)
55
+ * - /packages/core/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts (the page beside a non-modal drawer scrolls)
55
56
  * - /packages/core/src/Drawer/Drawer.spec.md (component contract)
56
57
  * - /packages/core/src/Drawer/index.ts (exports if types change)
57
58
  * - /packages/cli/assets/templates/blocks/components/Drawer/ (showcase blocks)
@@ -121,8 +122,13 @@ const styles = stylex.create({
121
122
  flexDirection: 'column',
122
123
  backgroundColor: colorVars['--color-background-surface'],
123
124
  boxShadow: shadowVars['--shadow-high'],
124
- overflow: 'hidden',
125
- overscrollBehavior: 'contain',
125
+ // `clip`, not `hidden`: `hidden` makes the panel a scroll container, and
126
+ // Chromium then holds a finger's drag on the page beside an open
127
+ // non-modal drawer in that container's overscroll containment, so the
128
+ // page stops scrolling. `clip` clips the off-screen panel the same way
129
+ // without a scroll container (MobileNav's dialog does the same). The
130
+ // content area keeps its own scrolling and containment.
131
+ overflow: 'clip',
126
132
  outline: 'none',
127
133
  // Full-height side panel, pinned across the block axis.
128
134
  insetBlockStart: 0,
@@ -496,6 +502,11 @@ export function Drawer({
496
502
  // same top-most/IME rules as the shared Escape listener.
497
503
  const handleCancel = useCallback(
498
504
  (event: React.SyntheticEvent<HTMLDialogElement>) => {
505
+ // A cancel that bubbles up from inside (a dismissed file picker) is not
506
+ // a close request; the platform's is fired at the dialog itself.
507
+ if (event.target !== event.currentTarget) {
508
+ return;
509
+ }
499
510
  event.preventDefault();
500
511
  if (shouldDismissOnCloseRequest() && allowEscape) {
501
512
  handleDismiss();