@astryxdesign/core 0.6.2 → 0.6.3-canary.0899f21

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 (549) 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/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +13 -28
  10. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  11. package/dist/Chat/ChatComposer.js +10 -5
  12. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  13. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposerDrawer.js +48 -23
  15. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  16. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  17. package/dist/Chat/ChatComposerInput.js +63 -16
  18. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  19. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  20. package/dist/Chat/ChatDictationButton.js +6 -4
  21. package/dist/Chat/ChatLayout.d.ts +19 -0
  22. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  23. package/dist/Chat/ChatLayout.js +19 -0
  24. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  25. package/dist/Chat/ChatLayoutScrollButton.js +44 -34
  26. package/dist/Chat/ChatMessageBubble.d.ts +2 -0
  27. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessageBubble.js +5 -3
  29. package/dist/Chat/ChatMessageList.d.ts +2 -1
  30. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  31. package/dist/Chat/ChatMessageList.js +4 -3
  32. package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
  33. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  34. package/dist/Chat/ChatMessageMetadata.js +12 -9
  35. package/dist/Chat/ChatSendButton.js +1 -1
  36. package/dist/Chat/index.d.ts +1 -1
  37. package/dist/Chat/index.d.ts.map +1 -1
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  40. package/dist/Citation/Citation.d.ts +1 -1
  41. package/dist/Citation/Citation.d.ts.map +1 -1
  42. package/dist/Citation/Citation.js +3 -2
  43. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  44. package/dist/CodeBlock/highlightRanges.js +14 -3
  45. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  46. package/dist/Collapsible/Collapsible.js +6 -1
  47. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  48. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  49. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  50. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  51. package/dist/DateInput/DateInput.d.ts.map +1 -1
  52. package/dist/DateInput/DateInput.js +4 -4
  53. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  54. package/dist/DateInput/NativeDateField.js +16 -16
  55. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  56. package/dist/DateInput/TouchDateField.js +2 -2
  57. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  58. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  59. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  60. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  61. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  62. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  63. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  64. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  65. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  66. package/dist/Dialog/DialogHeader.d.ts +8 -1
  67. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  68. package/dist/Dialog/DialogHeader.js +13 -4
  69. package/dist/Field/InputClearButton.d.ts +4 -2
  70. package/dist/Field/InputClearButton.d.ts.map +1 -1
  71. package/dist/Field/InputClearButton.js +4 -2
  72. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  73. package/dist/Field/PanelSearchInput.js +1 -1
  74. package/dist/FileInput/FileInput.d.ts.map +1 -1
  75. package/dist/FileInput/FileInput.js +7 -7
  76. package/dist/Icon/Icon.d.ts +6 -31
  77. package/dist/Icon/Icon.d.ts.map +1 -1
  78. package/dist/Icon/Icon.js +8 -71
  79. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  80. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  81. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  82. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  83. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  84. package/dist/Icon/IconSize.stylex.js +75 -0
  85. package/dist/Item/Item.d.ts +1 -1
  86. package/dist/Item/Item.d.ts.map +1 -1
  87. package/dist/Item/Item.js +4 -3
  88. package/dist/Kbd/Kbd.d.ts +5 -3
  89. package/dist/Kbd/Kbd.d.ts.map +1 -1
  90. package/dist/Kbd/Kbd.js +36 -42
  91. package/dist/Link/Link.d.ts.map +1 -1
  92. package/dist/Link/Link.js +6 -2
  93. package/dist/Link/useLinkComponent.d.ts +5 -15
  94. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  95. package/dist/Link/useLinkComponent.js +34 -51
  96. package/dist/List/List.d.ts +19 -1
  97. package/dist/List/List.d.ts.map +1 -1
  98. package/dist/List/List.js +9 -2
  99. package/dist/List/ListContext.d.ts +1 -0
  100. package/dist/List/ListContext.d.ts.map +1 -1
  101. package/dist/List/ListContext.js +2 -1
  102. package/dist/List/ListItem.d.ts +1 -1
  103. package/dist/List/ListItem.d.ts.map +1 -1
  104. package/dist/List/ListItem.js +9 -2
  105. package/dist/Markdown/Markdown.d.ts +8 -2
  106. package/dist/Markdown/Markdown.d.ts.map +1 -1
  107. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  108. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  109. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  110. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  111. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  112. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  113. package/dist/Markdown/Markdown.js +262 -109
  114. package/dist/Markdown/ast.d.ts +162 -0
  115. package/dist/Markdown/ast.d.ts.map +1 -0
  116. package/dist/Markdown/ast.js +74 -0
  117. package/dist/Markdown/index.d.ts +5 -3
  118. package/dist/Markdown/index.d.ts.map +1 -1
  119. package/dist/Markdown/index.js +3 -2
  120. package/dist/Markdown/parser/index.d.ts +10 -0
  121. package/dist/Markdown/parser/index.d.ts.map +1 -0
  122. package/dist/Markdown/parser/index.js +10 -0
  123. package/dist/Markdown/parser.d.ts +126 -58
  124. package/dist/Markdown/parser.d.ts.map +1 -1
  125. package/dist/Markdown/parser.js +716 -167
  126. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  127. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  128. package/dist/Markdown/plugins/frontmatter.js +132 -0
  129. package/dist/Markdown/plugins/index.d.ts +17 -0
  130. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  131. package/dist/Markdown/plugins/index.js +14 -0
  132. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  133. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  134. package/dist/Markdown/plugins/protocol.js +957 -0
  135. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  136. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  137. package/dist/Markdown/plugins/semanticFence.js +213 -0
  138. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  139. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  140. package/dist/Markdown/plugins/softBreaks.js +172 -0
  141. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  142. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  143. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  144. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  145. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  146. package/dist/Markdown/plugins/textTransform.js +374 -0
  147. package/dist/Markdown/remark.d.ts +196 -0
  148. package/dist/Markdown/remark.d.ts.map +1 -0
  149. package/dist/Markdown/remark.js +1062 -0
  150. package/dist/Markdown/url.d.ts +6 -0
  151. package/dist/Markdown/url.d.ts.map +1 -0
  152. package/dist/Markdown/url.js +31 -0
  153. package/dist/Markdown/utils.d.ts +4 -2
  154. package/dist/Markdown/utils.d.ts.map +1 -1
  155. package/dist/Markdown/utils.js +2 -1
  156. package/dist/MetadataList/MetadataList.js +3 -3
  157. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  158. package/dist/NumberInput/NumberInput.js +4 -4
  159. package/dist/Outline/index.d.ts +1 -0
  160. package/dist/Outline/index.d.ts.map +1 -1
  161. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  162. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  163. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  164. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  165. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  166. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  167. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  169. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +36 -31
  173. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  174. package/dist/RadioList/RadioListItem.js +13 -1
  175. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  176. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  177. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  178. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  179. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  180. package/dist/Selector/Selector.d.ts +9 -7
  181. package/dist/Selector/Selector.d.ts.map +1 -1
  182. package/dist/Selector/Selector.js +16 -7
  183. package/dist/SideNav/SideNav.d.ts +2 -1
  184. package/dist/SideNav/SideNav.d.ts.map +1 -1
  185. package/dist/SideNav/SideNav.js +9 -3
  186. package/dist/Slider/Slider.d.ts.map +1 -1
  187. package/dist/Slider/Slider.js +28 -12
  188. package/dist/Spinner/Spinner.d.ts.map +1 -1
  189. package/dist/Spinner/Spinner.js +23 -7
  190. package/dist/Switch/Switch.d.ts.map +1 -1
  191. package/dist/Switch/Switch.js +11 -0
  192. package/dist/TabList/Tab.d.ts +1 -1
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +20 -7
  195. package/dist/Table/Table.d.ts +1 -0
  196. package/dist/Table/Table.d.ts.map +1 -1
  197. package/dist/Table/Table.js +29 -13
  198. package/dist/TextArea/TextArea.d.ts.map +1 -1
  199. package/dist/TextArea/TextArea.js +1 -1
  200. package/dist/TextInput/TextInput.d.ts.map +1 -1
  201. package/dist/TextInput/TextInput.js +2 -2
  202. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  203. package/dist/TimeInput/TimeInput.js +4 -4
  204. package/dist/Timer/Timer.d.ts +49 -0
  205. package/dist/Timer/Timer.d.ts.map +1 -0
  206. package/dist/Timer/Timer.js +158 -0
  207. package/dist/Timer/index.d.ts +9 -0
  208. package/dist/Timer/index.d.ts.map +1 -0
  209. package/dist/Timer/index.js +10 -0
  210. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  211. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  212. package/dist/ToggleButton/ToggleButton.js +15 -27
  213. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  214. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMenu.js +3 -2
  216. package/dist/TreeList/TreeList.d.ts.map +1 -1
  217. package/dist/TreeList/TreeList.js +3 -0
  218. package/dist/TreeList/TreeListItem.d.ts +6 -1
  219. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  220. package/dist/TreeList/TreeListItem.js +12 -2
  221. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  222. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  223. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  224. package/dist/Typeahead/BaseTypeahead.js +16 -7
  225. package/dist/astryx.css +27 -11
  226. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  227. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  228. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  229. package/dist/hooks/useClickableContainer.d.ts +10 -1
  230. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  231. package/dist/hooks/useClickableContainer.js +15 -4
  232. package/dist/hooks/useScrollableArea.d.ts +6 -2
  233. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  234. package/dist/hooks/useScrollableArea.js +17 -5
  235. package/dist/index.d.ts +1 -0
  236. package/dist/index.d.ts.map +1 -1
  237. package/dist/index.js +3 -0
  238. package/dist/theme/declarationBoundary.d.ts +17 -0
  239. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  240. package/dist/theme/declarationBoundary.js +343 -0
  241. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  242. package/dist/theme/derivedVarRegistry.js +4 -0
  243. package/dist/theme/generateThemeRules.d.ts +13 -7
  244. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  245. package/dist/theme/generateThemeRules.js +140 -34
  246. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  247. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  248. package/dist/utils/interactionOverlay.stylex.js +9 -0
  249. package/dist/utils/safeUrl.d.ts +11 -0
  250. package/dist/utils/safeUrl.d.ts.map +1 -0
  251. package/dist/utils/safeUrl.js +78 -0
  252. package/locales/af-ZA.json +530 -1
  253. package/locales/ar-SA.json +490 -1
  254. package/locales/ca-ES.json +514 -1
  255. package/locales/cs-CZ.json +498 -1
  256. package/locales/da-DK.json +522 -1
  257. package/locales/de-DE.json +522 -1
  258. package/locales/el-GR.json +498 -1
  259. package/locales/es-ES.json +510 -1
  260. package/locales/fi-FI.json +498 -1
  261. package/locales/fr-FR.json +534 -1
  262. package/locales/he-IL.json +498 -1
  263. package/locales/hu-HU.json +498 -1
  264. package/locales/it-IT.json +510 -1
  265. package/locales/ja-JP.json +490 -1
  266. package/locales/ko-KR.json +498 -1
  267. package/locales/nl-NL.json +534 -1
  268. package/locales/no-NO.json +510 -1
  269. package/locales/pl-PL.json +506 -1
  270. package/locales/pt-BR.json +506 -1
  271. package/locales/pt-PT.json +506 -1
  272. package/locales/ro-RO.json +506 -1
  273. package/locales/ru-RU.json +498 -1
  274. package/locales/sr-SP.json +498 -1
  275. package/locales/sv-SE.json +502 -1
  276. package/locales/tr-TR.json +498 -1
  277. package/locales/uk-UA.json +498 -1
  278. package/locales/vi-VN.json +502 -1
  279. package/locales/zh-CN.json +490 -1
  280. package/locales/zh-TW.json +490 -1
  281. package/package.json +24 -4
  282. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  283. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  284. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  285. package/src/BottomSheet/BottomSheet.tsx +3 -1
  286. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  287. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  288. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  289. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  290. package/src/Button/Button.doc.mjs +5 -5
  291. package/src/Button/Button.tsx +15 -13
  292. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  293. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  294. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  295. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  296. package/src/Chat/ChatComposer.doc.mjs +14 -0
  297. package/src/Chat/ChatComposer.spec.md +230 -0
  298. package/src/Chat/ChatComposer.test.tsx +57 -0
  299. package/src/Chat/ChatComposer.tsx +21 -9
  300. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  301. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  302. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  303. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  304. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  305. package/src/Chat/ChatComposerInput.spec.md +185 -0
  306. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  307. package/src/Chat/ChatComposerInput.tsx +78 -13
  308. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  309. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  310. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  311. package/src/Chat/ChatDictationButton.spec.md +160 -0
  312. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  313. package/src/Chat/ChatDictationButton.tsx +8 -6
  314. package/src/Chat/ChatLayout.doc.mjs +12 -4
  315. package/src/Chat/ChatLayout.spec.md +271 -0
  316. package/src/Chat/ChatLayout.test.tsx +28 -0
  317. package/src/Chat/ChatLayout.tsx +19 -0
  318. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  319. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  320. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  321. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  322. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  323. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  324. package/src/Chat/ChatMessageBubble.tsx +8 -3
  325. package/src/Chat/ChatMessageList.spec.md +222 -0
  326. package/src/Chat/ChatMessageList.test.tsx +64 -2
  327. package/src/Chat/ChatMessageList.tsx +4 -3
  328. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  329. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  330. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  331. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  332. package/src/Chat/ChatSendButton.test.tsx +1 -4
  333. package/src/Chat/ChatSendButton.tsx +1 -1
  334. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  335. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  336. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  337. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  338. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  339. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  340. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  341. package/src/Chat/index.ts +1 -0
  342. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  343. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  344. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  345. package/src/Citation/Citation.doc.mjs +2 -2
  346. package/src/Citation/Citation.test.tsx +17 -0
  347. package/src/Citation/Citation.tsx +4 -2
  348. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  349. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  350. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  351. package/src/CodeBlock/highlightRanges.ts +15 -5
  352. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  353. package/src/Collapsible/Collapsible.test.tsx +21 -0
  354. package/src/Collapsible/Collapsible.tsx +5 -0
  355. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  356. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  357. package/src/DateInput/DateInput.tsx +6 -1
  358. package/src/DateInput/NativeDateField.tsx +24 -8
  359. package/src/DateInput/TouchDateField.tsx +6 -2
  360. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  361. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  362. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  363. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  364. package/src/Dialog/Dialog.doc.mjs +2 -0
  365. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  366. package/src/Dialog/DialogHeader.test.tsx +167 -3
  367. package/src/Dialog/DialogHeader.tsx +37 -6
  368. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  369. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  370. package/src/Field/InputClearButton.test.tsx +17 -3
  371. package/src/Field/InputClearButton.tsx +5 -2
  372. package/src/Field/PanelSearchInput.tsx +5 -2
  373. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  374. package/src/FileInput/FileInput.tsx +11 -2
  375. package/src/Icon/Icon.doc.mjs +7 -5
  376. package/src/Icon/Icon.spec.md +81 -40
  377. package/src/Icon/Icon.test.tsx +6 -3
  378. package/src/Icon/Icon.tsx +20 -68
  379. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  380. package/src/Icon/IconSize.stylex.ts +70 -0
  381. package/src/IconButton/IconButton.doc.mjs +3 -3
  382. package/src/IconButton/IconButton.test.tsx +40 -0
  383. package/src/Item/Item.doc.mjs +6 -2
  384. package/src/Item/Item.tsx +9 -3
  385. package/src/Kbd/Kbd.doc.mjs +3 -3
  386. package/src/Kbd/Kbd.test.tsx +57 -1
  387. package/src/Kbd/Kbd.tsx +57 -37
  388. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  389. package/src/Link/Link.doc.mjs +17 -4
  390. package/src/Link/Link.test.tsx +24 -0
  391. package/src/Link/Link.tsx +5 -0
  392. package/src/Link/LinkProvider.doc.mjs +2 -2
  393. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  394. package/src/Link/useLinkComponent.test.tsx +333 -0
  395. package/src/Link/useLinkComponent.ts +29 -57
  396. package/src/List/List.doc.mjs +6 -0
  397. package/src/List/List.spec.md +200 -0
  398. package/src/List/List.test.tsx +117 -1
  399. package/src/List/List.tsx +22 -2
  400. package/src/List/ListContext.tsx +3 -1
  401. package/src/List/ListItem.doc.mjs +2 -2
  402. package/src/List/ListItem.source-build.test.mjs +41 -0
  403. package/src/List/ListItem.tsx +26 -2
  404. package/src/Markdown/Markdown.doc.mjs +300 -2
  405. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  406. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  407. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  408. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  409. package/src/Markdown/Markdown.public.test.ts +130 -3
  410. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  411. package/src/Markdown/Markdown.spec.md +203 -45
  412. package/src/Markdown/Markdown.test.tsx +63 -0
  413. package/src/Markdown/Markdown.tsx +414 -135
  414. package/src/Markdown/ast.test.ts +307 -0
  415. package/src/Markdown/ast.ts +337 -0
  416. package/src/Markdown/incremental.test.ts +12 -0
  417. package/src/Markdown/index.ts +37 -1
  418. package/src/Markdown/modules/remark.spec.md +269 -0
  419. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  420. package/src/Markdown/parser/index.ts +64 -0
  421. package/src/Markdown/parser.public.test.ts +103 -0
  422. package/src/Markdown/parser.test.ts +20 -0
  423. package/src/Markdown/parser.ts +1180 -235
  424. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  425. package/src/Markdown/plugins/frontmatter.ts +198 -0
  426. package/src/Markdown/plugins/index.ts +54 -0
  427. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  428. package/src/Markdown/plugins/protocol.ts +1618 -0
  429. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  430. package/src/Markdown/plugins/semanticFence.ts +424 -0
  431. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  432. package/src/Markdown/plugins/softBreaks.ts +169 -0
  433. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  434. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  435. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  436. package/src/Markdown/plugins/textTransform.ts +554 -0
  437. package/src/Markdown/remark.test.tsx +1877 -0
  438. package/src/Markdown/remark.ts +1574 -0
  439. package/src/Markdown/url.ts +36 -0
  440. package/src/Markdown/utils.ts +13 -1
  441. package/src/MetadataList/MetadataList.test.tsx +4 -2
  442. package/src/MetadataList/MetadataList.tsx +3 -3
  443. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  444. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  445. package/src/NumberInput/NumberInput.tsx +6 -1
  446. package/src/Outline/Outline.doc.mjs +7 -5
  447. package/src/Outline/Outline.spec.md +1 -1
  448. package/src/Outline/index.ts +1 -0
  449. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  450. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  451. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  452. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  453. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  454. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  455. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  456. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  457. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  458. package/src/ProgressBar/ProgressBar.tsx +59 -72
  459. package/src/RadioList/RadioList.doc.mjs +11 -0
  460. package/src/RadioList/RadioList.test.tsx +32 -0
  461. package/src/RadioList/RadioListItem.tsx +25 -1
  462. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  463. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  464. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  465. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  466. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  467. package/src/Selector/Selector.doc.mjs +2 -2
  468. package/src/Selector/Selector.spec.md +51 -49
  469. package/src/Selector/Selector.test.tsx +61 -55
  470. package/src/Selector/Selector.tsx +34 -15
  471. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  472. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  473. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  474. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  475. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  476. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  477. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  478. package/src/SideNav/SideNav.doc.mjs +1 -1
  479. package/src/SideNav/SideNav.test.tsx +10 -0
  480. package/src/SideNav/SideNav.tsx +14 -2
  481. package/src/Slider/Slider.doc.mjs +17 -3
  482. package/src/Slider/Slider.test.tsx +221 -0
  483. package/src/Slider/Slider.tsx +40 -9
  484. package/src/Spinner/Spinner.test.tsx +4 -0
  485. package/src/Spinner/Spinner.tsx +31 -18
  486. package/src/Switch/Switch.doc.mjs +11 -0
  487. package/src/Switch/Switch.test.tsx +16 -0
  488. package/src/Switch/Switch.tsx +28 -0
  489. package/src/TabList/Tab.tsx +35 -7
  490. package/src/TabList/TabList.doc.mjs +11 -0
  491. package/src/TabList/TabList.spec.md +204 -0
  492. package/src/TabList/TabList.test.tsx +66 -0
  493. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  494. package/src/Table/Table.doc.mjs +1 -1
  495. package/src/Table/Table.spec.md +54 -40
  496. package/src/Table/Table.test.tsx +83 -10
  497. package/src/Table/Table.tsx +30 -22
  498. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  499. package/src/TextArea/TextArea.tsx +6 -1
  500. package/src/TextInput/TextInput.test.tsx +20 -1
  501. package/src/TextInput/TextInput.tsx +6 -1
  502. package/src/TimeInput/TimeInput.tsx +6 -1
  503. package/src/Timer/Timer.doc.mjs +183 -0
  504. package/src/Timer/Timer.spec.md +215 -0
  505. package/src/Timer/Timer.test.tsx +302 -0
  506. package/src/Timer/Timer.tsx +258 -0
  507. package/src/Timer/index.ts +11 -0
  508. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  509. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  510. package/src/ToggleButton/ToggleButton.tsx +26 -33
  511. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  512. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  513. package/src/Token/Token.doc.mjs +2 -2
  514. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  515. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  516. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  517. package/src/TopNav/TopNavMenu.tsx +7 -2
  518. package/src/TreeList/TreeList.doc.mjs +3 -3
  519. package/src/TreeList/TreeList.spec.md +123 -75
  520. package/src/TreeList/TreeList.test.tsx +29 -0
  521. package/src/TreeList/TreeList.tsx +3 -0
  522. package/src/TreeList/TreeListItem.tsx +15 -1
  523. package/src/TreeList/TreeListTypes.ts +11 -1
  524. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  525. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  526. package/src/Typeahead/Typeahead.test.tsx +53 -0
  527. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  528. package/src/__tests__/pressState.ts +93 -0
  529. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  530. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  531. package/src/hooks/useClickableContainer.test.tsx +236 -0
  532. package/src/hooks/useClickableContainer.ts +20 -4
  533. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  534. package/src/hooks/useScrollableArea.test.tsx +59 -1
  535. package/src/hooks/useScrollableArea.ts +34 -10
  536. package/src/index.ts +3 -0
  537. package/src/inputFontFloor.test.ts +106 -0
  538. package/src/tailwind-theme.css +0 -6
  539. package/src/theme/Theme.test.tsx +32 -0
  540. package/src/theme/declarationBoundary.test.ts +126 -0
  541. package/src/theme/declarationBoundary.ts +347 -0
  542. package/src/theme/derivedVarRegistry.test.ts +7 -0
  543. package/src/theme/derivedVarRegistry.ts +1 -0
  544. package/src/theme/generateThemeRules.test.ts +526 -1
  545. package/src/theme/generateThemeRules.ts +228 -50
  546. package/src/theme/tokenValueCompat.test.ts +4 -1
  547. package/src/utils/interactionOverlay.stylex.ts +20 -0
  548. package/src/utils/safeUrl.test.ts +88 -0
  549. package/src/utils/safeUrl.ts +85 -0
@@ -4,89 +4,61 @@
4
4
 
5
5
  /**
6
6
  * @file useLinkComponent.ts
7
- * @input React use, useMemo, createElement, forwardRef, LinkContext, LinkComponentType
8
- * @output Exports useLinkComponent hook
9
- * @position Hook for resolving the link component in Astryx components
7
+ * @input Per-component override, LinkProvider context, and navigation props
8
+ * @output Stable link component with destination checks and custom-router `to` injection
9
+ * @position Shared native and custom link boundary; rejected destinations render
10
+ * as destination-less anchors without invoking a router.
10
11
  *
11
- * Resolution order: per-component `as` prop > LinkProvider context > native `<a>`.
12
- *
13
- * When the resolved component is a custom component (not native `<a>`),
14
- * wraps it to pass `to={href}` alongside `href`. This enables compatibility
15
- * with routers that use `to` (React Router, TanStack Router)
16
- * without requiring an adapter component.
17
- *
18
- * SYNC: When modified, update these files to stay in sync:
19
- * - /packages/core/src/Link/index.ts
20
- * - /packages/core/src/Link/Link.doc.mjs
12
+ * SYNC: Link.doc.mjs and LinkProvider.doc.mjs describe this boundary.
21
13
  */
22
14
 
23
15
  import {use, useMemo, createElement} from 'react';
24
16
  import {LinkContext} from './LinkContext';
17
+ import {isSafeDestination} from '../utils/safeUrl';
25
18
  import type {LinkComponentType} from './types';
26
19
 
27
- /**
28
- * Creates a wrapper component that passes both `href` and `to` props
29
- * to the underlying link component. This enables routers that use `to`
30
- * (React Router, TanStack Router) to work without an adapter.
31
- *
32
- * The wrapper is transparent: it forwards refs and all other props unchanged.
33
- * Native `<a>` elements ignore the unknown `to` prop harmlessly.
34
- */
35
- function createLinkWithTo(
36
- Component: LinkComponentType,
37
- ): LinkComponentType {
38
- function LinkWithTo({
20
+ function createSafeLink(Component: LinkComponentType): LinkComponentType {
21
+ function SafeLink({
39
22
  href,
23
+ to,
40
24
  ref,
41
25
  ...rest
42
26
  }: {
43
- href?: string;
44
- to?: string;
27
+ href?: unknown;
28
+ to?: unknown;
45
29
  ref?: React.Ref<unknown>;
46
30
  }) {
47
- return createElement(Component, {ref, href, to: href, ...rest});
31
+ // An undefined destination can crash a router or resolve to the current
32
+ // route. Reject the whole handoff, not just one prop or a fallback value.
33
+ if (!isSafeDestination(href) || !isSafeDestination(to)) {
34
+ return createElement('a', {ref, ...rest});
35
+ }
36
+ if (Component === 'a') {
37
+ return createElement('a', {ref, ...rest, href});
38
+ }
39
+ return createElement(Component, {ref, ...rest, href, to: to ?? href});
48
40
  }
49
- LinkWithTo.displayName = `LinkWithTo(${
41
+ SafeLink.displayName = `SafeLink(${
50
42
  typeof Component === 'string'
51
43
  ? Component
52
44
  : Component.displayName || Component.name || 'Component'
53
45
  })`;
54
- return LinkWithTo as LinkComponentType;
46
+ return SafeLink as LinkComponentType;
55
47
  }
56
48
 
57
49
  /**
58
- * Resolves the link component to use.
59
- *
60
- * Priority: `as` prop > `LinkProvider` context > native `<a>`.
61
- *
62
- * When the resolved component is a custom component (not the native `<a>`),
63
- * it is wrapped to receive both `href` and `to` props set to the same value.
64
- * This allows `to`-based routers (React Router, TanStack Router) to work
65
- * out of the box without a manual adapter.
66
- *
67
- * @param as - Per-component override. If provided, takes highest priority.
68
- * @returns The resolved link component (with `to` injection for custom components).
50
+ * Resolve `as`, then LinkProvider, then a native anchor. Both destination props
51
+ * are checked before rendering; accepted structured values retain identity.
52
+ * Custom components receive `to={href}` unless an explicit `to` is supplied.
69
53
  *
70
54
  * @example
71
55
  * ```
72
- * function MyComponent({ as }: { as?: LinkComponentType }) {
73
- * const LinkComponent = useLinkComponent(as);
74
- * return <LinkComponent href="/foo">Click me</LinkComponent>;
75
- * }
56
+ * const LinkComponent = useLinkComponent(as);
57
+ * return <LinkComponent href="/docs">Documentation</LinkComponent>;
76
58
  * ```
77
59
  */
78
- export function useLinkComponent(
79
- as?: LinkComponentType,
80
- ): LinkComponentType {
60
+ export function useLinkComponent(as?: LinkComponentType): LinkComponentType {
81
61
  const ctx = use(LinkContext);
82
62
  const resolved = as ?? ctx?.component ?? 'a';
83
-
84
- // Memoize the wrapper to maintain referential stability.
85
- // The wrapper is only created for custom components (not native <a>).
86
- return useMemo(() => {
87
- if (resolved === 'a') {
88
- return 'a';
89
- }
90
- return createLinkWithTo(resolved);
91
- }, [resolved]);
63
+ return useMemo(() => createSafeLink(resolved), [resolved]);
92
64
  }
@@ -41,6 +41,12 @@ export const docs = {
41
41
  description: 'Show dividers between items.',
42
42
  default: 'false',
43
43
  },
44
+ {
45
+ name: 'edgeCompensation',
46
+ type: "'inline'",
47
+ description:
48
+ "Compensate for item content inset on each inline edge by cancelling the smaller of each item's built-in horizontal inset and the container's published inline padding. The margin reads the same variable the items derive their inline padding from, so it tracks density and theme padding overrides automatically without pulling rows outside zero-padding or full-bleed surfaces. Use under a section heading to bring row text toward the heading text. Content aligns when container padding is at least the item inset; smaller padding leaves some inset uncompensated. Omit to leave item positions unchanged.",
49
+ },
44
50
  {
45
51
  name: 'header',
46
52
  type: 'ReactNode',
@@ -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.
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * @file List.test.tsx
5
5
  * @input Uses vitest, @testing-library/react, List, ListItem
6
- * @output Unit tests for List and ListItem components
6
+ * @output Unit tests for List and ListItem, including bounded inline edge compensation
7
7
  * @position Testing; validates List.tsx and ListItem.tsx implementation
8
8
  *
9
9
  * SYNC: When modified, update this header
@@ -255,6 +255,122 @@ describe('List', () => {
255
255
  expect(item).toHaveAttribute('data-density', 'spacious');
256
256
  });
257
257
 
258
+ // ===========================================================================
259
+ // Inline edge compensation
260
+ // ===========================================================================
261
+
262
+ it('does not apply inline edge compensation styles by default', () => {
263
+ const {container} = render(
264
+ <List>
265
+ <ListItem label="Item" />
266
+ </List>,
267
+ );
268
+ const item = container.querySelector('li')!;
269
+ expect(item.className).not.toContain('inlineEdgeCompensation');
270
+ });
271
+
272
+ it('applies the clamped cancelling margin to each item when edgeCompensation is inline', () => {
273
+ // The margin lives on the row element itself — the only place that can
274
+ // read --_item-inset-inline, which Item sets on the same element (custom
275
+ // properties cascade downward, so the <ul> cannot read it).
276
+ const {container} = render(
277
+ <List edgeCompensation="inline">
278
+ <ListItem label="Item 1" />
279
+ <ListItem label="Item 2" />
280
+ </List>,
281
+ );
282
+ const items = container.querySelectorAll('li');
283
+ expect(items).toHaveLength(2);
284
+ for (const item of items) {
285
+ expect(item.className).toContain('inlineEdgeCompensation');
286
+ }
287
+ expect(container.querySelector('ul')!.className).not.toContain(
288
+ 'inlineEdgeCompensation',
289
+ );
290
+ });
291
+
292
+ it('uses the same var-derived cancel for every density', () => {
293
+ // The cancelling margin reads --_item-inset-inline instead of hardcoding
294
+ // per-density values, so all densities (and theme paddingInline
295
+ // overrides on `item`) share one style.
296
+ for (const density of ['compact', 'balanced', 'spacious'] as const) {
297
+ const {container, unmount} = render(
298
+ <List edgeCompensation="inline" density={density}>
299
+ <ListItem label="Item" />
300
+ </List>,
301
+ );
302
+ expect(container.querySelector('li')!.className).toContain(
303
+ 'inlineEdgeCompensation',
304
+ );
305
+ unmount();
306
+ }
307
+ });
308
+
309
+ it('does not pull the header when edgeCompensation is inline', () => {
310
+ // The negative margin lives on the row elements, so the header keeps its
311
+ // position and the row text aligns up to it.
312
+ const {container} = render(
313
+ <List edgeCompensation="inline" header={<span>Items</span>}>
314
+ <ListItem label="Item" />
315
+ </List>,
316
+ );
317
+ expect(container.querySelector('li')!.className).toContain(
318
+ 'inlineEdgeCompensation',
319
+ );
320
+ const ul = container.querySelector('ul')!;
321
+ expect(ul.className).not.toContain('inlineEdgeCompensation');
322
+ expect(ul.parentElement?.className).not.toContain('inlineEdgeCompensation');
323
+ });
324
+
325
+ it('clamps each inline edge against its own container padding var', () => {
326
+ // Regression for asymmetric container padding (16px start / 4px end):
327
+ // clamping BOTH margins against --container-padding-inline-start
328
+ // over-cancels the end edge by the difference, and the selected row
329
+ // paints past the outer border (RTL mirrors the spill). Each edge must
330
+ // clamp against its matching var; logical properties keep RTL correct.
331
+ // jsdom does no real layout, so assert on the injected StyleX CSS
332
+ // (runtimeInjection is on in the test config).
333
+ const {container} = render(
334
+ <List edgeCompensation="inline">
335
+ <ListItem label="Item" />
336
+ </List>,
337
+ );
338
+ expect(container.querySelector('li')!.className).toContain(
339
+ 'inlineEdgeCompensation',
340
+ );
341
+ let out = '';
342
+ for (const sheet of Array.from(document.styleSheets)) {
343
+ try {
344
+ for (const rule of Array.from(sheet.cssRules)) {
345
+ out += rule.cssText + '\n';
346
+ }
347
+ } catch {
348
+ // ignore cross-origin sheets
349
+ }
350
+ }
351
+ out += Array.from(document.querySelectorAll('style'))
352
+ .map(s => s.textContent || '')
353
+ .join('\n');
354
+ const css = out.replace(/\s+/g, '');
355
+ expect(css).toContain(
356
+ 'margin-inline-start:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-start,0px)))',
357
+ );
358
+ expect(css).toContain(
359
+ 'margin-inline-end:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-end,0px)))',
360
+ );
361
+ // The end edge must not clamp against the start var (the over-cancel).
362
+ expect(css).not.toContain(
363
+ 'margin-inline-end:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-start,0px)))',
364
+ );
365
+ });
366
+
367
+ it('does not apply the cancelling margin to a ListItem outside a List', () => {
368
+ const {container} = render(<ListItem label="Standalone" />);
369
+ expect(container.querySelector('li')!.className).not.toContain(
370
+ 'inlineEdgeCompensation',
371
+ );
372
+ });
373
+
258
374
  // ===========================================================================
259
375
  // Dividers
260
376
  // ===========================================================================
package/src/List/List.tsx CHANGED
@@ -5,6 +5,7 @@
5
5
  /**
6
6
  * @file List.tsx
7
7
  * @input Uses React, ReactNode, StyleXStyles, theme tokens, ListContext
8
+ * with optional inline edge compensation
8
9
  * @output Exports List component, ListProps, ListDensity, ListStyle types
9
10
  * @position Core implementation; consumed by index.ts, tested by List.test.tsx
10
11
  *
@@ -58,6 +59,20 @@ export interface ListProps extends BaseProps<
58
59
  */
59
60
  hasDividers?: boolean;
60
61
 
62
+ /**
63
+ * Compensates for each item's built-in inline inset, up to the container
64
+ * padding available on each edge. Use "inline" to bring row content toward
65
+ * sibling content such as a section heading. Content aligns when the
66
+ * container padding is at least the item inset; smaller padding leaves
67
+ * some inset uncompensated.
68
+ * The cancelling margin reads the same variable the items derive their
69
+ * inline padding from, so it tracks density and theme padding overrides
70
+ * automatically. Hover and selection backgrounds still extend past the
71
+ * text by the inset.
72
+ * Omit to leave item positions unchanged.
73
+ */
74
+ edgeCompensation?: 'inline';
75
+
61
76
  /**
62
77
  * Header content rendered above the list.
63
78
  * Semantically associated via aria-labelledby.
@@ -128,6 +143,10 @@ const dynamicStyles = stylex.create({
128
143
  * Renders semantic `<ul>` or `<ol>` elements with configurable density,
129
144
  * dividers, marker styles, and an optional header.
130
145
  *
146
+ * Set `edgeCompensation="inline"` to compensate for the items' inline inset
147
+ * up to the container padding available on each edge, for alignment with
148
+ * sibling content such as a section heading.
149
+ *
131
150
  * @example
132
151
  * ```
133
152
  * <List>
@@ -144,6 +163,7 @@ export function List({
144
163
  children,
145
164
  density = 'balanced',
146
165
  hasDividers = false,
166
+ edgeCompensation,
147
167
  header,
148
168
  listStyle = 'none',
149
169
  start,
@@ -159,8 +179,8 @@ export function List({
159
179
  const Tag = isOrdered ? 'ol' : 'ul';
160
180
 
161
181
  const contextValue = useMemo(
162
- () => ({density, hasDividers, listStyle}),
163
- [density, hasDividers, listStyle],
182
+ () => ({density, hasDividers, listStyle, edgeCompensation}),
183
+ [density, hasDividers, listStyle, edgeCompensation],
164
184
  );
165
185
 
166
186
  const listElement = (
@@ -5,7 +5,8 @@
5
5
  /**
6
6
  * @file ListContext.tsx
7
7
  * @input Uses React createContext
8
- * @output Exports ListContext for sharing density between List and ListItem
8
+ * @output Exports ListContext for sharing density, dividers, marker style,
9
+ * and optional inline edge compensation between List and ListItem
9
10
  * @position Internal context; consumed by List.tsx and ListItem.tsx
10
11
  */
11
12
 
@@ -18,6 +19,7 @@ export interface ListContextValue {
18
19
  density: ListDensity;
19
20
  hasDividers: boolean;
20
21
  listStyle: ListMarkerStyle;
22
+ edgeCompensation?: 'inline';
21
23
  }
22
24
 
23
25
  export const ListContext = createContext<ListContextValue | null>(null);
@@ -67,7 +67,7 @@ export const docs = {
67
67
  {
68
68
  name: 'href',
69
69
  type: 'string',
70
- description: 'Link URL; enables the invisible anchor pattern.',
70
+ description: 'Link URL; enables the invisible anchor pattern. The destination follows the shared navigation rule described on the Link `href` prop.',
71
71
  },
72
72
  {
73
73
  name: 'target',
@@ -174,7 +174,7 @@ export const docsDense = {
174
174
  onClick: 'Click handler; enables invisible button pattern.',
175
175
  interactiveRef:
176
176
  'Ref to a nested control that owns the item\'s keyboard access/action; row delegates surface clicks to it (useClickableContainer), no invisible button/anchor, no second tab stop (WCAG 4.1.2). Mutually exclusive with onClick/href.',
177
- href: 'Link URL; enables invisible anchor pattern.',
177
+ href: 'Link URL; enables invisible anchor pattern. Follows the shared navigation rule (see Link href).',
178
178
  target: 'Link target attribute, only when href provided. target="_blank" auto-adds noopener noreferrer.',
179
179
  rel: 'Link relationship tokens. noopener noreferrer are merged for target="_blank".',
180
180
  isDisabled: 'Disabled state; sets aria-disabled.',
@@ -0,0 +1,41 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file ListItem.source-build.test.mjs
5
+ * @input Uses Babel, the core StyleX build config, and ListItem source
6
+ * @output Verifies the divider reset for the last item survives compilation
7
+ * @position Regression test for the shipped ListItem stylesheet
8
+ */
9
+
10
+ import fs from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import {fileURLToPath} from 'node:url';
13
+ import {transformAsync} from '@babel/core';
14
+ import {describe, expect, it} from 'vitest';
15
+
16
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+ const CORE_ROOT = path.resolve(__dirname, '../..');
18
+ const LIST_ITEM_SOURCE = path.join(__dirname, 'ListItem.tsx');
19
+
20
+ async function compileListItemRules() {
21
+ const source = await fs.readFile(LIST_ITEM_SOURCE, 'utf8');
22
+ const result = await transformAsync(source, {
23
+ babelrc: false,
24
+ configFile: path.join(CORE_ROOT, 'babel.config.json'),
25
+ filename: LIST_ITEM_SOURCE,
26
+ });
27
+ return (result?.metadata?.stylex ?? []).map(([, rule]) => rule.ltr);
28
+ }
29
+
30
+ describe('ListItem divider stylesheet', () => {
31
+ it('emits a :last-child rule that removes the divider after the last item', async () => {
32
+ const rules = await compileListItemRules();
33
+
34
+ // The divider is the item's block-end border. StyleX's default
35
+ // property-specificity mode silently drops a `borderBlockEnd` shorthand,
36
+ // so the last-item reset must be a longhand to reach the shipped CSS.
37
+ expect(rules).toContainEqual(
38
+ expect.stringMatching(/:last-child\{border-bottom-width:0\}$/),
39
+ );
40
+ });
41
+ });