@astryxdesign/core 0.6.2 → 0.6.3-canary.01fa439

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 (384) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +3 -1
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +56 -19
  8. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  9. package/dist/Chat/ChatComposer.js +10 -5
  10. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  11. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  12. package/dist/Chat/ChatComposerDrawer.js +48 -23
  13. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  14. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  15. package/dist/Chat/ChatComposerInput.js +63 -16
  16. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  17. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  18. package/dist/Chat/ChatDictationButton.js +6 -4
  19. package/dist/Chat/ChatSendButton.js +1 -1
  20. package/dist/Chat/index.d.ts +1 -1
  21. package/dist/Chat/index.d.ts.map +1 -1
  22. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  23. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  24. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  25. package/dist/Collapsible/Collapsible.js +6 -1
  26. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  27. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  28. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  29. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  30. package/dist/DateInput/DateInput.d.ts.map +1 -1
  31. package/dist/DateInput/DateInput.js +4 -4
  32. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  33. package/dist/DateInput/NativeDateField.js +16 -16
  34. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  35. package/dist/DateInput/TouchDateField.js +2 -2
  36. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  37. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  40. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  41. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  42. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  43. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  44. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  45. package/dist/Dialog/DialogHeader.d.ts +8 -1
  46. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  47. package/dist/Dialog/DialogHeader.js +10 -1
  48. package/dist/Field/InputClearButton.d.ts +4 -2
  49. package/dist/Field/InputClearButton.d.ts.map +1 -1
  50. package/dist/Field/InputClearButton.js +4 -2
  51. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  52. package/dist/Field/PanelSearchInput.js +1 -1
  53. package/dist/FileInput/FileInput.d.ts.map +1 -1
  54. package/dist/FileInput/FileInput.js +7 -7
  55. package/dist/Kbd/Kbd.d.ts +5 -3
  56. package/dist/Kbd/Kbd.d.ts.map +1 -1
  57. package/dist/Kbd/Kbd.js +36 -42
  58. package/dist/Link/Link.d.ts.map +1 -1
  59. package/dist/Link/Link.js +6 -2
  60. package/dist/Markdown/Markdown.d.ts +8 -2
  61. package/dist/Markdown/Markdown.d.ts.map +1 -1
  62. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  63. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  64. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  65. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  66. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  67. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  68. package/dist/Markdown/Markdown.js +262 -109
  69. package/dist/Markdown/ast.d.ts +162 -0
  70. package/dist/Markdown/ast.d.ts.map +1 -0
  71. package/dist/Markdown/ast.js +74 -0
  72. package/dist/Markdown/index.d.ts +5 -3
  73. package/dist/Markdown/index.d.ts.map +1 -1
  74. package/dist/Markdown/index.js +3 -2
  75. package/dist/Markdown/parser/index.d.ts +10 -0
  76. package/dist/Markdown/parser/index.d.ts.map +1 -0
  77. package/dist/Markdown/parser/index.js +10 -0
  78. package/dist/Markdown/parser.d.ts +126 -58
  79. package/dist/Markdown/parser.d.ts.map +1 -1
  80. package/dist/Markdown/parser.js +716 -167
  81. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  82. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  83. package/dist/Markdown/plugins/frontmatter.js +132 -0
  84. package/dist/Markdown/plugins/index.d.ts +16 -0
  85. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  86. package/dist/Markdown/plugins/index.js +13 -0
  87. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  88. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  89. package/dist/Markdown/plugins/protocol.js +957 -0
  90. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  91. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  92. package/dist/Markdown/plugins/semanticFence.js +213 -0
  93. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  94. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  95. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  96. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  97. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  98. package/dist/Markdown/plugins/textTransform.js +374 -0
  99. package/dist/Markdown/remark.d.ts +196 -0
  100. package/dist/Markdown/remark.d.ts.map +1 -0
  101. package/dist/Markdown/remark.js +1062 -0
  102. package/dist/Markdown/url.d.ts +4 -0
  103. package/dist/Markdown/url.d.ts.map +1 -0
  104. package/dist/Markdown/url.js +25 -0
  105. package/dist/Markdown/utils.d.ts +4 -2
  106. package/dist/Markdown/utils.d.ts.map +1 -1
  107. package/dist/Markdown/utils.js +2 -1
  108. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  109. package/dist/NumberInput/NumberInput.js +4 -4
  110. package/dist/Outline/index.d.ts +1 -0
  111. package/dist/Outline/index.d.ts.map +1 -1
  112. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  113. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  114. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  115. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  116. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  117. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  118. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  119. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  120. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  121. package/dist/ProgressBar/ProgressBar.js +36 -31
  122. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  123. package/dist/RadioList/RadioListItem.js +13 -1
  124. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  125. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  126. package/dist/Selector/Selector.d.ts +9 -7
  127. package/dist/Selector/Selector.d.ts.map +1 -1
  128. package/dist/Selector/Selector.js +7 -5
  129. package/dist/SideNav/SideNav.d.ts +2 -1
  130. package/dist/SideNav/SideNav.d.ts.map +1 -1
  131. package/dist/SideNav/SideNav.js +9 -3
  132. package/dist/Slider/Slider.d.ts.map +1 -1
  133. package/dist/Slider/Slider.js +28 -12
  134. package/dist/Spinner/Spinner.d.ts.map +1 -1
  135. package/dist/Spinner/Spinner.js +23 -7
  136. package/dist/Switch/Switch.d.ts.map +1 -1
  137. package/dist/Switch/Switch.js +11 -0
  138. package/dist/TabList/Tab.d.ts +1 -1
  139. package/dist/TabList/Tab.d.ts.map +1 -1
  140. package/dist/TabList/Tab.js +20 -7
  141. package/dist/Table/Table.d.ts +1 -0
  142. package/dist/Table/Table.d.ts.map +1 -1
  143. package/dist/Table/Table.js +29 -13
  144. package/dist/TextArea/TextArea.d.ts.map +1 -1
  145. package/dist/TextArea/TextArea.js +1 -1
  146. package/dist/TextInput/TextInput.d.ts.map +1 -1
  147. package/dist/TextInput/TextInput.js +2 -2
  148. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  149. package/dist/TimeInput/TimeInput.js +4 -4
  150. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  151. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  152. package/dist/ToggleButton/ToggleButton.js +15 -27
  153. package/dist/TreeList/TreeList.d.ts.map +1 -1
  154. package/dist/TreeList/TreeList.js +3 -0
  155. package/dist/TreeList/TreeListItem.d.ts +6 -1
  156. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  157. package/dist/TreeList/TreeListItem.js +12 -2
  158. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  159. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  160. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  161. package/dist/Typeahead/BaseTypeahead.js +16 -7
  162. package/dist/astryx.css +18 -7
  163. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  164. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  165. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  166. package/dist/hooks/useScrollableArea.d.ts +6 -2
  167. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  168. package/dist/hooks/useScrollableArea.js +17 -5
  169. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  170. package/dist/theme/generateThemeRules.js +10 -0
  171. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  172. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  173. package/dist/utils/interactionOverlay.stylex.js +9 -0
  174. package/locales/af-ZA.json +530 -1
  175. package/locales/ar-SA.json +490 -1
  176. package/locales/ca-ES.json +514 -1
  177. package/locales/cs-CZ.json +498 -1
  178. package/locales/da-DK.json +522 -1
  179. package/locales/de-DE.json +522 -1
  180. package/locales/el-GR.json +498 -1
  181. package/locales/es-ES.json +510 -1
  182. package/locales/fi-FI.json +498 -1
  183. package/locales/fr-FR.json +534 -1
  184. package/locales/he-IL.json +498 -1
  185. package/locales/hu-HU.json +498 -1
  186. package/locales/it-IT.json +510 -1
  187. package/locales/ja-JP.json +490 -1
  188. package/locales/ko-KR.json +498 -1
  189. package/locales/nl-NL.json +534 -1
  190. package/locales/no-NO.json +510 -1
  191. package/locales/pl-PL.json +506 -1
  192. package/locales/pt-BR.json +506 -1
  193. package/locales/pt-PT.json +506 -1
  194. package/locales/ro-RO.json +506 -1
  195. package/locales/ru-RU.json +498 -1
  196. package/locales/sr-SP.json +498 -1
  197. package/locales/sv-SE.json +502 -1
  198. package/locales/tr-TR.json +498 -1
  199. package/locales/uk-UA.json +498 -1
  200. package/locales/vi-VN.json +502 -1
  201. package/locales/zh-CN.json +490 -1
  202. package/locales/zh-TW.json +490 -1
  203. package/package.json +19 -4
  204. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  205. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  206. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  207. package/src/BottomSheet/BottomSheet.tsx +3 -1
  208. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  209. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  210. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  211. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  212. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  213. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  214. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  215. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  216. package/src/Chat/ChatComposer.doc.mjs +14 -0
  217. package/src/Chat/ChatComposer.spec.md +230 -0
  218. package/src/Chat/ChatComposer.test.tsx +57 -0
  219. package/src/Chat/ChatComposer.tsx +21 -9
  220. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  221. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  222. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  223. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  224. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  225. package/src/Chat/ChatComposerInput.spec.md +185 -0
  226. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  227. package/src/Chat/ChatComposerInput.tsx +78 -13
  228. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  229. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  230. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  231. package/src/Chat/ChatDictationButton.spec.md +160 -0
  232. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  233. package/src/Chat/ChatDictationButton.tsx +8 -6
  234. package/src/Chat/ChatSendButton.test.tsx +1 -4
  235. package/src/Chat/ChatSendButton.tsx +1 -1
  236. package/src/Chat/index.ts +1 -0
  237. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  238. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  239. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  240. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  241. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  242. package/src/Collapsible/Collapsible.test.tsx +21 -0
  243. package/src/Collapsible/Collapsible.tsx +5 -0
  244. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  245. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  246. package/src/DateInput/DateInput.tsx +6 -1
  247. package/src/DateInput/NativeDateField.tsx +24 -8
  248. package/src/DateInput/TouchDateField.tsx +6 -2
  249. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  250. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  251. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  252. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  253. package/src/Dialog/DialogHeader.doc.mjs +12 -0
  254. package/src/Dialog/DialogHeader.test.tsx +97 -0
  255. package/src/Dialog/DialogHeader.tsx +25 -3
  256. package/src/Dialog/modules/DialogHeader.spec.md +75 -38
  257. package/src/Field/InputClearButton.test.tsx +17 -3
  258. package/src/Field/InputClearButton.tsx +5 -2
  259. package/src/Field/PanelSearchInput.tsx +5 -2
  260. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  261. package/src/FileInput/FileInput.tsx +11 -2
  262. package/src/Kbd/Kbd.doc.mjs +3 -3
  263. package/src/Kbd/Kbd.test.tsx +57 -1
  264. package/src/Kbd/Kbd.tsx +57 -37
  265. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  266. package/src/Link/Link.doc.mjs +11 -0
  267. package/src/Link/Link.test.tsx +24 -0
  268. package/src/Link/Link.tsx +5 -0
  269. package/src/List/List.spec.md +200 -0
  270. package/src/Markdown/Markdown.doc.mjs +272 -0
  271. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  272. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  273. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  274. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  275. package/src/Markdown/Markdown.public.test.ts +126 -3
  276. package/src/Markdown/Markdown.spec.md +197 -44
  277. package/src/Markdown/Markdown.test.tsx +11 -0
  278. package/src/Markdown/Markdown.tsx +414 -135
  279. package/src/Markdown/ast.test.ts +307 -0
  280. package/src/Markdown/ast.ts +337 -0
  281. package/src/Markdown/incremental.test.ts +12 -0
  282. package/src/Markdown/index.ts +37 -1
  283. package/src/Markdown/modules/remark.spec.md +269 -0
  284. package/src/Markdown/parser/index.ts +64 -0
  285. package/src/Markdown/parser.public.test.ts +103 -0
  286. package/src/Markdown/parser.ts +1180 -235
  287. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  288. package/src/Markdown/plugins/frontmatter.ts +198 -0
  289. package/src/Markdown/plugins/index.ts +53 -0
  290. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  291. package/src/Markdown/plugins/protocol.ts +1618 -0
  292. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  293. package/src/Markdown/plugins/semanticFence.ts +424 -0
  294. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  295. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  296. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  297. package/src/Markdown/plugins/textTransform.ts +554 -0
  298. package/src/Markdown/remark.test.tsx +1877 -0
  299. package/src/Markdown/remark.ts +1574 -0
  300. package/src/Markdown/url.ts +30 -0
  301. package/src/Markdown/utils.ts +13 -1
  302. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  303. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  304. package/src/NumberInput/NumberInput.tsx +6 -1
  305. package/src/Outline/Outline.doc.mjs +7 -5
  306. package/src/Outline/Outline.spec.md +1 -1
  307. package/src/Outline/index.ts +1 -0
  308. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  309. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  310. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  311. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  312. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  313. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  314. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  315. package/src/ProgressBar/ProgressBar.tsx +59 -72
  316. package/src/RadioList/RadioList.doc.mjs +11 -0
  317. package/src/RadioList/RadioList.test.tsx +32 -0
  318. package/src/RadioList/RadioListItem.tsx +25 -1
  319. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  320. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  321. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  322. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  323. package/src/Selector/Selector.doc.mjs +2 -2
  324. package/src/Selector/Selector.spec.md +51 -49
  325. package/src/Selector/Selector.test.tsx +61 -55
  326. package/src/Selector/Selector.tsx +23 -14
  327. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  328. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  329. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  330. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  331. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  332. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  333. package/src/SideNav/SideNav.doc.mjs +1 -1
  334. package/src/SideNav/SideNav.test.tsx +10 -0
  335. package/src/SideNav/SideNav.tsx +14 -2
  336. package/src/Slider/Slider.doc.mjs +17 -3
  337. package/src/Slider/Slider.test.tsx +221 -0
  338. package/src/Slider/Slider.tsx +40 -9
  339. package/src/Spinner/Spinner.test.tsx +4 -0
  340. package/src/Spinner/Spinner.tsx +31 -18
  341. package/src/Switch/Switch.doc.mjs +11 -0
  342. package/src/Switch/Switch.test.tsx +16 -0
  343. package/src/Switch/Switch.tsx +28 -0
  344. package/src/TabList/Tab.tsx +35 -7
  345. package/src/TabList/TabList.doc.mjs +11 -0
  346. package/src/TabList/TabList.spec.md +204 -0
  347. package/src/TabList/TabList.test.tsx +66 -0
  348. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  349. package/src/Table/Table.doc.mjs +1 -1
  350. package/src/Table/Table.spec.md +54 -40
  351. package/src/Table/Table.test.tsx +83 -10
  352. package/src/Table/Table.tsx +30 -22
  353. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  354. package/src/TextArea/TextArea.tsx +6 -1
  355. package/src/TextInput/TextInput.test.tsx +20 -1
  356. package/src/TextInput/TextInput.tsx +6 -1
  357. package/src/TimeInput/TimeInput.tsx +6 -1
  358. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  359. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  360. package/src/ToggleButton/ToggleButton.tsx +26 -33
  361. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  362. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  363. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  364. package/src/TreeList/TreeList.doc.mjs +3 -3
  365. package/src/TreeList/TreeList.spec.md +123 -75
  366. package/src/TreeList/TreeList.test.tsx +29 -0
  367. package/src/TreeList/TreeList.tsx +3 -0
  368. package/src/TreeList/TreeListItem.tsx +15 -1
  369. package/src/TreeList/TreeListTypes.ts +11 -1
  370. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  371. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  372. package/src/Typeahead/Typeahead.test.tsx +53 -0
  373. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  374. package/src/__tests__/pressState.ts +93 -0
  375. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  376. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  377. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  378. package/src/hooks/useScrollableArea.test.tsx +59 -1
  379. package/src/hooks/useScrollableArea.ts +34 -10
  380. package/src/inputFontFloor.test.ts +106 -0
  381. package/src/tailwind-theme.css +0 -6
  382. package/src/theme/generateThemeRules.test.ts +30 -0
  383. package/src/theme/generateThemeRules.ts +10 -0
  384. package/src/utils/interactionOverlay.stylex.ts +20 -0
@@ -27,7 +27,7 @@ export const docs = {
27
27
  name: 'keys',
28
28
  type: 'string',
29
29
  description:
30
- 'Keyboard shortcut string. Use "+" to separate keys. Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape, tab, up, down, left, right.',
30
+ 'Keyboard shortcut string. Use "+" to separate keys. Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape, tab, up, down, left, right, plus. Aliases: esc for escape and return for enter.',
31
31
  required: true,
32
32
  },
33
33
  {
@@ -72,7 +72,7 @@ export const docsZh = {
72
72
  name: 'keys',
73
73
  type: 'string',
74
74
  description:
75
- '键盘快捷键字符串。使用 "+" 分隔各按键。特殊按键:mod(Mac 上为 Cmd)、ctrl、alt、shift、enter、backspace、escape、tab、up、down、left、right。',
75
+ '键盘快捷键字符串。使用 "+" 分隔各按键。特殊按键:mod(Mac 上为 Cmd)、ctrl、alt、shift、enter、backspace、escape、tab、up、down、left、right、plus。别名:esc 等同于 escape,return 等同于 enter。',
76
76
  required: true,
77
77
  },
78
78
  {
@@ -122,7 +122,7 @@ export const docsDense = {
122
122
  ],
123
123
  },
124
124
  propDescriptions: {
125
- keys: 'Shortcut string. "+" separates keys. Special: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape, tab, up, down, left, right.',
125
+ keys: 'Shortcut string. "+" separates keys. Special: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape, tab, up, down, left, right, plus. Aliases: esc for escape, return for enter.',
126
126
  xstyle: 'StyleX styles for layout customization. Must be stylex.create() value.',
127
127
  className: 'CSS class for root element. Prefer xstyle; className for non-StyleX integration.',
128
128
  style: 'Inline styles for root element. Prefer xstyle; inline styles bypass StyleX optimization.',
@@ -10,13 +10,14 @@
10
10
  */
11
11
 
12
12
  import {render, screen} from '@testing-library/react';
13
- import {describe, it, expect, afterEach} from 'vitest';
13
+ import {describe, it, expect, afterEach, vi} from 'vitest';
14
14
  import {Kbd} from './Kbd';
15
15
 
16
16
  describe('Kbd', () => {
17
17
  const originalPlatform = navigator.platform;
18
18
 
19
19
  afterEach(() => {
20
+ vi.restoreAllMocks();
20
21
  // Restore platform after any test that spoofs it \u2014 ensures no
21
22
  // test pollution even if an assertion fails mid-test.
22
23
  Object.defineProperty(navigator, 'platform', {
@@ -72,6 +73,61 @@ describe('Kbd', () => {
72
73
  expect(screen.getByText('Esc')).toBeInTheDocument();
73
74
  });
74
75
 
76
+ it.each([
77
+ {alias: 'esc', display: 'Esc', label: 'Escape'},
78
+ {alias: 'return', display: '\u21B5', label: 'Enter'},
79
+ ])('normalizes the $alias key alias', ({alias, display, label}) => {
80
+ render(<Kbd keys={alias} />);
81
+ expect(screen.getByText(display)).toBeInTheDocument();
82
+ expect(screen.getByRole('img')).toHaveAttribute('aria-label', label);
83
+ });
84
+
85
+ it.each([
86
+ {alias: 'esc', canonical: 'escape'},
87
+ {alias: 'return', canonical: 'enter'},
88
+ ])('$alias renders identically to $canonical', ({alias, canonical}) => {
89
+ const {container, rerender} = render(<Kbd keys={canonical} />);
90
+ const text = container.textContent;
91
+ const label = screen.getByRole('img').getAttribute('aria-label');
92
+ rerender(<Kbd keys={alias} />);
93
+ expect(container.textContent).toBe(text);
94
+ expect(screen.getByRole('img').getAttribute('aria-label')).toBe(label);
95
+ });
96
+
97
+ it.each([
98
+ {keys: 'ctrl + ESC', display: 'Esc', label: 'Control + Escape'},
99
+ {keys: 'shift + RETURN', display: '\u21B5', label: 'Shift + Enter'},
100
+ ])('normalizes aliases inside the $keys combo', ({keys, display, label}) => {
101
+ render(<Kbd keys={keys} />);
102
+ expect(screen.getByText(display)).toBeInTheDocument();
103
+ expect(screen.getByRole('img')).toHaveAttribute('aria-label', label);
104
+ });
105
+
106
+ it('keeps child keys unique after alias normalization', () => {
107
+ const consoleError = vi
108
+ .spyOn(console, 'error')
109
+ .mockImplementation(() => {});
110
+ const {container} = render(
111
+ <Kbd keys="escape+esc+ESC+enter+return+return" />,
112
+ );
113
+ expect(consoleError).not.toHaveBeenCalled();
114
+ expect(
115
+ Array.from(container.querySelectorAll('kbd'), key => key.textContent),
116
+ ).toEqual(['Esc', 'Esc', 'Esc', '↵', '↵', '↵']);
117
+ });
118
+
119
+ it.each(['constructor', '__proto__'])(
120
+ 'renders the unknown %s key without consulting object prototypes',
121
+ key => {
122
+ render(<Kbd keys={key} />);
123
+ expect(screen.getByText(key.toUpperCase())).toBeInTheDocument();
124
+ expect(screen.getByRole('img')).toHaveAttribute(
125
+ 'aria-label',
126
+ key.toUpperCase(),
127
+ );
128
+ },
129
+ );
130
+
75
131
  it('exposes a spoken accessible name and hides the glyphs (obs-1)', () => {
76
132
  render(<Kbd keys="mod+shift+k" />);
77
133
  // The wrapper carries a screen-reader name built from spoken key labels
package/src/Kbd/Kbd.tsx CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file Kbd.tsx
7
- * @input Uses React, StyleX, theme tokens
8
- * @output Exports Kbd component and KbdProps
7
+ * @input Uses React, StyleX, theme tokens, shortcut key names and aliases
8
+ * @output Exports Kbd component and KbdProps with canonical shortcut labels
9
9
  * @position Core implementation; renders styled keyboard shortcut indicators
10
10
  *
11
11
  * SYNC: When modified, update:
@@ -61,20 +61,25 @@ const styles = stylex.create({
61
61
  * Note: `mod` is not in this map — it resolves dynamically via platform
62
62
  * detection inside the component.
63
63
  */
64
- const KEY_DISPLAY: Record<string, string> = {
65
- ctrl: '\u2303', // ⌃
66
- alt: '\u2325', // ⌥
67
- shift: '\u21E7', // ⇧
68
- enter: '\u21B5', // ↵
69
- backspace: '\u232B', // ⌫
70
- escape: 'Esc',
71
- tab: '\u21E5', // ⇥
72
- up: '\u2191',
73
- down: '\u2193',
74
- left: '\u2190',
75
- right: '\u2192',
76
- plus: '+',
77
- };
64
+ const KEY_DISPLAY = new Map<string, string>([
65
+ ['ctrl', '\u2303'], // ⌃
66
+ ['alt', '\u2325'], // ⌥
67
+ ['shift', '\u21E7'], // ⇧
68
+ ['enter', '\u21B5'], // ↵
69
+ ['backspace', '\u232B'], // ⌫
70
+ ['escape', 'Esc'],
71
+ ['tab', '\u21E5'], // ⇥
72
+ ['up', '\u2191'],
73
+ ['down', '\u2193'],
74
+ ['left', '\u2190'],
75
+ ['right', '\u2192'],
76
+ ['plus', '+'],
77
+ ]);
78
+
79
+ const KBD_KEY_ALIASES = new Map<string, string>([
80
+ ['esc', 'escape'],
81
+ ['return', 'enter'],
82
+ ]);
78
83
 
79
84
  /**
80
85
  * Resolves a key name to its display string. Handles the platform-aware
@@ -85,7 +90,7 @@ function getKeyDisplay(key: string, isMac: boolean): string {
85
90
  if (key === 'mod') {
86
91
  return isMac ? '\u2318' : 'Ctrl';
87
92
  }
88
- return KEY_DISPLAY[key] ?? key.toUpperCase();
93
+ return KEY_DISPLAY.get(key) ?? key.toUpperCase();
89
94
  }
90
95
 
91
96
  /**
@@ -93,26 +98,26 @@ function getKeyDisplay(key: string, isMac: boolean): string {
93
98
  * (⌘, ⇧, ↵, …) that assistive tech cannot announce meaningfully, so the
94
99
  * accessible name for the shortcut is built from these words instead.
95
100
  */
96
- const KEY_LABEL: Record<string, string> = {
97
- ctrl: 'Control',
98
- alt: 'Alt',
99
- shift: 'Shift',
100
- enter: 'Enter',
101
- backspace: 'Backspace',
102
- escape: 'Escape',
103
- tab: 'Tab',
104
- up: 'Up arrow',
105
- down: 'Down arrow',
106
- left: 'Left arrow',
107
- right: 'Right arrow',
108
- plus: 'Plus',
109
- };
101
+ const KEY_LABEL = new Map<string, string>([
102
+ ['ctrl', 'Control'],
103
+ ['alt', 'Alt'],
104
+ ['shift', 'Shift'],
105
+ ['enter', 'Enter'],
106
+ ['backspace', 'Backspace'],
107
+ ['escape', 'Escape'],
108
+ ['tab', 'Tab'],
109
+ ['up', 'Up arrow'],
110
+ ['down', 'Down arrow'],
111
+ ['left', 'Left arrow'],
112
+ ['right', 'Right arrow'],
113
+ ['plus', 'Plus'],
114
+ ]);
110
115
 
111
116
  function getKeyLabel(key: string, isMac: boolean): string {
112
117
  if (key === 'mod') {
113
118
  return isMac ? 'Command' : 'Control';
114
119
  }
115
- return KEY_LABEL[key] ?? key.toUpperCase();
120
+ return KEY_LABEL.get(key) ?? key.toUpperCase();
116
121
  }
117
122
 
118
123
  function subscribeToPlatformChanges(): () => void {
@@ -127,7 +132,9 @@ export interface KbdProps extends BaseProps<HTMLSpanElement> {
127
132
  ref?: React.Ref<HTMLSpanElement>;
128
133
  /**
129
134
  * Keyboard shortcut string. Use "+" to separate keys.
130
- * Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape.
135
+ * Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape,
136
+ * tab, up, down, left, and right.
137
+ * Aliases: "esc" for "escape" and "return" for "enter".
131
138
  * Use "plus" to render a literal "+" key (e.g. "shift+plus").
132
139
  *
133
140
  * @example
@@ -163,11 +170,24 @@ export function Kbd({keys, ref, xstyle, className, style, ...rest}: KbdProps) {
163
170
  getServerPlatformSnapshot,
164
171
  );
165
172
 
166
- const parts = keys.split('+').map(key => key.trim().toLowerCase());
173
+ const keyOccurrences = new Map<string, number>();
174
+ const parts = keys
175
+ .split('+')
176
+ .map(key => key.trim().toLowerCase())
177
+ .map(sourceKey => {
178
+ const occurrence = keyOccurrences.get(sourceKey) ?? 0;
179
+ keyOccurrences.set(sourceKey, occurrence + 1);
180
+ return {
181
+ key: KBD_KEY_ALIASES.get(sourceKey) ?? sourceKey,
182
+ reactKey: `${sourceKey}:${occurrence}`,
183
+ };
184
+ });
167
185
 
168
186
  // Screen-reader name: the joined spoken labels (e.g. "Command + K"), since
169
187
  // the visual glyphs below are announced meaninglessly by assistive tech.
170
- const accessibleName = parts.map(key => getKeyLabel(key, isMac)).join(' + ');
188
+ const accessibleName = parts
189
+ .map(({key}) => getKeyLabel(key, isMac))
190
+ .join(' + ');
171
191
 
172
192
  return (
173
193
  <span
@@ -181,8 +201,8 @@ export function Kbd({keys, ref, xstyle, className, style, ...rest}: KbdProps) {
181
201
  className,
182
202
  style,
183
203
  )}>
184
- {parts.map(key => (
185
- <kbd key={key} aria-hidden="true" {...stylex.props(styles.kbd)}>
204
+ {parts.map(({key, reactKey}) => (
205
+ <kbd key={reactKey} aria-hidden="true" {...stylex.props(styles.kbd)}>
186
206
  {getKeyDisplay(key, isMac)}
187
207
  </kbd>
188
208
  ))}
@@ -8,6 +8,23 @@ export const docs = {
8
8
  displayName: 'Layout Footer',
9
9
  isHiddenFromOverview: true,
10
10
  description: 'Bottom bar for action bars, pagination, and status bars.',
11
+ playground: {
12
+ defaults: {
13
+ children: 'Footer content: status bar or actions',
14
+ hasDivider: true,
15
+ },
16
+ wrapper: {
17
+ component: 'Layout',
18
+ slotProp: 'footer',
19
+ props: {
20
+ content: {
21
+ __element: 'LayoutContent',
22
+ props: {},
23
+ children: 'Main content area',
24
+ },
25
+ },
26
+ },
27
+ },
11
28
  props: [
12
29
  {
13
30
  name: 'children',
@@ -164,6 +164,17 @@ export const docs = {
164
164
  },
165
165
  ],
166
166
  usage: {
167
+ accessibility: [
168
+ {
169
+ name: 'Link text',
170
+ category: 'Color contrast',
171
+ criterion: '1.4.3 Contrast (Minimum)',
172
+ requirement: '4.5:1',
173
+ states: ['Rest', 'Hover', 'Pointer down'],
174
+ description:
175
+ 'Link text must have at least 4.5:1 contrast with the background behind it. For Pointer down, measure against the pressed overlay the link paints behind its text.',
176
+ },
177
+ ],
167
178
  description:
168
179
  'A styled anchor for inline and standalone text navigation. Supports external links, underline variants, tooltips, and custom link components for router integration. Use it for navigating between pages or to external URLs.',
169
180
  bestPractices: [
@@ -12,6 +12,7 @@
12
12
  import {describe, it, expect, vi} from 'vitest';
13
13
  import {fireEvent, render, screen} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
+ import {hasPressedArm} from '../__tests__/pressState';
15
16
  import {Link} from './Link';
16
17
  import {LinkProvider} from './LinkProvider';
17
18
 
@@ -417,3 +418,26 @@ describe('Link', () => {
417
418
  expect(link).toHaveAttribute('data-color', 'secondary');
418
419
  });
419
420
  });
421
+
422
+ describe('pressed state', () => {
423
+ it('paints the pressed overlay behind a link while it is pressed', () => {
424
+ render(<Link href="/docs">Docs</Link>);
425
+ expect(hasPressedArm(screen.getByRole('link', {name: 'Docs'}))).toBe(true);
426
+ });
427
+
428
+ it('paints it on the button form too', () => {
429
+ render(<Link onClick={() => {}}>Open</Link>);
430
+ expect(hasPressedArm(screen.getByRole('button', {name: 'Open'}))).toBe(
431
+ true,
432
+ );
433
+ });
434
+
435
+ it('does not press a disabled link', () => {
436
+ render(
437
+ <Link href="/docs" isDisabled>
438
+ Docs
439
+ </Link>,
440
+ );
441
+ expect(hasPressedArm(screen.getByText('Docs').closest('a')!)).toBe(false);
442
+ });
443
+ });
package/src/Link/Link.tsx CHANGED
@@ -45,6 +45,7 @@ import {computeTargetAndRel} from './computeTargetAndRel';
45
45
  import {useInteractiveRole} from '../hooks/useInteractiveRole';
46
46
  import {themeProps} from '../utils/themeProps';
47
47
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
48
+ import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
48
49
  import {useTranslator} from '../i18n';
49
50
 
50
51
  /**
@@ -368,6 +369,9 @@ export function Link({
368
369
  styles.base,
369
370
  styles.buttonReset,
370
371
  linkColorStyles[color],
372
+ // The system's pressed overlay behind the text; the hover stays the
373
+ // colour change above, so a press is the one background it paints.
374
+ !isDisabled && interactionOverlayStyles.pressedBackgroundColor,
371
375
  hasUnderline && styles.hasUnderline,
372
376
  isStandalone && styles.standalone,
373
377
  isDisabled && styles.disabled,
@@ -427,6 +431,7 @@ export function Link({
427
431
  focusOutlineProps.focusVisible(
428
432
  styles.base,
429
433
  linkColorStyles[color],
434
+ !isDisabled && interactionOverlayStyles.pressedBackgroundColor,
430
435
  hasUnderline && styles.hasUnderline,
431
436
  isStandalone && styles.standalone,
432
437
  isDisabled && styles.disabled,
@@ -0,0 +1,200 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 5
4
+ kind: component
5
+ id: component:List
6
+ authority: current
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: cixzhang
10
+ approved_at: 2026-09-21
11
+ owners: [cixzhang]
12
+ review_triggers: [public-api, behavior, layout, theming]
13
+ verified_by:
14
+ [
15
+ packages/core/src/List/List.test.tsx,
16
+ packages/core/src/Item/Item.test.tsx,
17
+ packages/core/src/theme/derivedVarRegistry.test.ts,
18
+ packages/core/src/theme/generateThemeRules.test.ts,
19
+ apps/storybook/stories/List.stories.tsx,
20
+ scripts/check-knowledge.mjs,
21
+ ]
22
+ modules: []
23
+ families: []
24
+ design_specs: []
25
+ architecture:
26
+ [architecture:container-padding, architecture:public-component-api]
27
+ contributing: [contributing:api-conventions]
28
+ system_specs: [spec:AST-002/DEC-1, spec:AST-002/DEC-2]
29
+ ---
30
+
31
+ # List component contract
32
+
33
+ ## Contract at a glance
34
+
35
+ | Area | Contract |
36
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
37
+ | Public contract | Add optional `edgeCompensation?: 'inline'`; omission preserves current layout. |
38
+ | Behavior | Every ListItem row moves its content toward a neutral host's inline content edges, reaching them when the host inset is at least the Item inset, while retaining Item-owned interaction padding. |
39
+ | End-user impact | List content can reduce or remove the extra inset beside its optional header and adjacent content without shrinking hover, press, selection, or focus paint. |
40
+ | Builder impact | Builders opt in only when the List is placed directly in an eligible container-padding host state. |
41
+ | Compatibility/readiness | Additive API; implementation and required real-browser evidence are pending. |
42
+ | Review checks | Reject header movement, unbounded negative margins, duplicated padding values, physical-edge logic, automatic opt-in, or a ListItem-level public prop. |
43
+ | Governing rules | `architecture:container-padding/INV9–INV11`; `spec:AST-002/DEC-1–DEC-2`. |
44
+
45
+ This table is a review projection; the body below is authoritative.
46
+
47
+ ## Intent
48
+
49
+ List owns collection-wide edge alignment because its optional header is not
50
+ horizontally inset while every ListItem retains Item's inline padding. The caller
51
+ knows whether the List is placed in a neutral host where these content lines
52
+ should align; List cannot derive that design relationship from its children.
53
+
54
+ ## Compatibility and migration
55
+
56
+ - Released default preserved: `yes`; omission keeps current List and ListItem geometry.
57
+ - Compatibility class: additive optional prop with no default behavior change.
58
+ - Controlled/uncontrolled behavior: not applicable.
59
+ - Migration decision: `component:List/DEC-1` and `component:List/DEC-2`.
60
+
61
+ Consumer migration instructions belong in consumer docs and release notes.
62
+
63
+ ## Ownership boundary
64
+
65
+ **Owns**
66
+
67
+ - The collection-wide `edgeCompensation` opt-in and its propagation to every
68
+ ListItem row.
69
+ - Keeping the optional header in place while moving row content toward its line.
70
+ - The supported relationship between ListItem row geometry and Item's normal
71
+ density- and theme-aware inline inset.
72
+
73
+ **Does not own / non-goals**
74
+
75
+ - Container padding values, nesting, or overlay resets — owned by
76
+ `architecture:container-padding`.
77
+ - A ListItem-level compensation prop, automatic host detection, full bleed, or
78
+ arbitrary margin controls.
79
+ - TabList or Button projections.
80
+
81
+ ## Public concepts
82
+
83
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
84
+ | ----------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------- | ---------------- | -------------------------------- | -------------------------------------------- |
85
+ | Edge compensation | omitted, `inline` | Move row content toward both logical host content edges while retaining row-local interaction padding | Every List density and marker style in an eligible host | omitted | `component:List` | accepted; implementation pending | Other values are rejected by the public type |
86
+
87
+ ## Behavioral and layout contract
88
+
89
+ | ID | Invariant | Basis | Status |
90
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------- |
91
+ | FR1 | List MUST own `edgeCompensation?: 'inline'`; ListItem MUST NOT expose a parallel public prop. | `component:List/DEC-1` | Accepted contract; implementation pending |
92
+ | FR2 | Omission MUST preserve current header, list, and row geometry. Enabling `inline` MUST leave the optional header in place and apply one compensation policy to every ListItem, including non-interactive rows. | Owner direction | Accepted contract; implementation pending |
93
+ | FR3 | Each row MUST resolve each logical margin independently as the negative smaller of its own component-owned inline inset and the matching available host inset. Missing host inset MUST resolve to zero. Content reaches the host line only when the host inset is at least the row inset; otherwise it moves toward that line without crossing the host boundary. | `architecture:container-padding/INV9–INV10`; `component:List/DEC-2` | Accepted contract; implementation pending |
94
+ | FR4 | The retained row inset MUST come from the same private density- and theme-derived source as Item's applied inline padding. A consumer override of row padding through `xstyle`, `className`, or `style` is outside the alignment guarantee; `edgeCompensation` MUST NOT mirror or infer that arbitrary CSS. | Shared two-owner geometry contract | Accepted contract; implementation pending |
95
+ | FR5 | Compensation MUST preserve Item's hover, press, selection, focus, content, semantics, and hit target. It MUST NOT make the row a full-bleed surface or pull it past either available host edge. | Owner direction | Accepted contract; implementation pending |
96
+ | FR6 | The opt-in is valid only under a host state whose published inline inset matches its applied padding and whose relevant boundary is visually neutral. Current automatic LayoutContent/LayoutPanel states with known publication mismatch are not eligible until that conformance gap is fixed. | `architecture:container-padding/INV8–INV11` | Accepted contract; implementation pending |
97
+
98
+ ### Allowed variation
99
+
100
+ - Density and `item.paddingInline` theme overrides may change the retained inset;
101
+ compensation follows the same derived value.
102
+ - Container padding may be uniform, asymmetric, zero, or absent; each logical
103
+ edge resolves independently.
104
+ - Labels, descriptions, markers, start/end content, interactivity, and selection
105
+ may vary without changing the collection-wide alignment rule.
106
+
107
+ ### Representative states
108
+
109
+ | State | Required invariant | Allowed variation |
110
+ | ------------------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
111
+ | Default/omitted | Header and every row keep current geometry. | Any current List content and density. |
112
+ | Host inset at least Item inset | Row content reaches the host content line; row-local paint keeps Item padding. | Uniform or asymmetric inline padding. |
113
+ | Smaller, zero, or missing host inset | The row moves only by the available inset and is never pulled beyond the host boundary. | Either logical edge may independently be smaller or zero. |
114
+ | LTR or RTL | Logical start/end mapping follows direction without changing the `inline` meaning. | Writing direction. |
115
+ | Themed Item padding | Row padding and compensation move together. | Supported `item.paddingInline` value. |
116
+
117
+ ### Transformation and precedence order
118
+
119
+ - **ORD1 — Host bound.** Read the matching published host inset for each selected
120
+ logical edge, with missing values falling back to zero.
121
+ - **ORD2 — Local inset.** Resolve Item's component-owned inline inset from density
122
+ and theme through the same private value that supplies applied padding.
123
+ - **ORD3 — Compensation.** Apply the negative minimum of those values to the row;
124
+ then compose supported consumer styles without claiming alignment for a consumer
125
+ override of row inline padding.
126
+
127
+ ### Performance and resources
128
+
129
+ - **PR1 — CSS geometry only.** Edge compensation MUST add no measurement,
130
+ observer, listener, state, Effect, or render pass.
131
+
132
+ ## Accessibility contract
133
+
134
+ - **AR1 — Interaction geometry remains local.** Compensation MUST NOT change the
135
+ interactive element, accessible role/name/state, focus target, tab order, or
136
+ hit target.
137
+ - **AR2 — Direction remains logical.** The same `inline` value MUST produce the
138
+ corresponding mirrored geometry under RTL without changing DOM order.
139
+
140
+ ## Design relationships
141
+
142
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
143
+ | ---------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------ | ------------------ |
144
+ | Optional header | Stays on the host content line and is not moved by row compensation. | `component:List/DEC-1` | Context | FR2 |
145
+ | ListItem row | Retains local interaction paint and hit-target padding. | Item composition | Supporting surface | FR3–FR5 |
146
+ | Row content | Moves toward each host content line and reaches it only when the host inset can fully absorb the row inset. | `component:List/DEC-2` | Prominent | FR2–FR5 |
147
+
148
+ ## Family and system relationships
149
+
150
+ - `architecture:container-padding` owns host publication, the shared
151
+ `edgeCompensation` vocabulary, eligibility, logical-edge semantics, and overlay
152
+ reset.
153
+ - `architecture:public-component-api` and `spec:AST-002` own public API admission.
154
+ - Item remains the implementation owner of row padding and interaction paint;
155
+ List owns only the collection-level opt-in and row application.
156
+
157
+ ## Verification map
158
+
159
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
160
+ | ---------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------- |
161
+ | FR1–FR4 | List, Item, and theme-registry tests | omitted/enabled; all densities; theme override; static/interactive rows | Removing context propagation, duplicating a padding value, or using one host edge for both margins fails | `audit:List/layout` |
162
+ | FR2–FR6, AR1–AR2 | Real-browser List geometry evidence | header; LTR/RTL; uniform/asymmetric/zero/missing host inset; host inset smaller/equal/larger than Item inset; hover/selection/focus | Header movement, over-pull, wrong bounded movement, or lost row-local paint fails | `audit:List/visual` |
163
+ | Documentation | `List.doc.mjs`, Storybook, and `scripts/check-knowledge.mjs` | public prop/default and representative composition | Missing or stale contract, docs, or example fails | `audit:List/docs` |
164
+
165
+ ## Decision log
166
+
167
+ ### DEC-1 — List owns the collection-wide opt-in
168
+
169
+ **Reference:** `component:List/DEC-1`
170
+
171
+ **Decider:** cixzhang, 2026-09-21
172
+
173
+ The List header and ListItem rows otherwise begin on different content lines.
174
+ Only the caller knows when the containing composition requires alignment, while
175
+ List can apply one consistent decision to every row without exposing Item-level
176
+ configuration.
177
+
178
+ ### DEC-2 — Compensation is row-local and bounded
179
+
180
+ **Reference:** `component:List/DEC-2`
181
+
182
+ **Decider:** cixzhang, 2026-09-21
183
+
184
+ Each row cancels no more than its own component-owned inline inset or the
185
+ matching host inset. When the host inset is at least the row inset, content
186
+ reaches the host content line. When it is smaller, content moves toward that line
187
+ without pulling row paint beyond the host boundary.
188
+
189
+ Rejected: `isFullBleed`, because the row may stop inside the host padding and the
190
+ name would promise outer-edge bleed that the component does not provide.
191
+
192
+ ## Open questions
193
+
194
+ None.
195
+
196
+ ## Content boundary
197
+
198
+ This claim-scoped contract does not duplicate List's complete prop table,
199
+ container-padding internals, consumer examples, implementation steps, or future
200
+ TabList/Button projections. Those remain with their named owners.