@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
@@ -9,11 +9,17 @@
9
9
  * Used by both the runtime path (Theme injects <style>) and the build
10
10
  * path (`astryx theme build` pre-compiles to CSS files).
11
11
  *
12
+ * Every consumer-supplied declaration is checked by declarationBoundary.ts
13
+ * before it is emitted, so a value can never end its declaration or rule
14
+ * early. A refused declaration is dropped and reported in the supplied
15
+ * warnings array (build receipts), or through `console.warn` (runtime).
16
+ *
12
17
  * Extracted from defineTheme.ts to reduce cyclomatic complexity and provide
13
18
  * a clear single-responsibility module for CSS generation.
14
19
  *
15
- * @input DefinedTheme (resolved theme object from defineTheme)
16
- * @output CSS rule strings, split by layer (component vs prose)
20
+ * @input DefinedTheme (resolved theme object from defineTheme), optional warnings array
21
+ * @output CSS rule strings, split by layer (component vs prose), and warning text
22
+ * for each dropped declaration
17
23
  * @position packages/core/src/theme/generateThemeRules.ts
18
24
  */
19
25
 
@@ -26,6 +32,10 @@ import {parseStyleKey} from '../utils/parseStyleKey';
26
32
  import {getDerivedVars} from './derivedVarRegistry';
27
33
  import {dataTokenDefaults} from './domainTokens/dataTokens';
28
34
  import {cssVar, classPrefix, dataAttrNamespace} from '../naming';
35
+ import {
36
+ checkDeclarationName,
37
+ checkDeclarationValue,
38
+ } from './declarationBoundary';
29
39
 
30
40
  /**
31
41
  * Theme @scope selectors.
@@ -366,6 +376,117 @@ function expandContainerPadding(
366
376
  return tokens;
367
377
  }
368
378
 
379
+ // =============================================================================
380
+ // Declaration assembly: a declaration always stays one declaration
381
+ // =============================================================================
382
+
383
+ type DiagnosticSink = (message: string) => void;
384
+
385
+ const warnToConsole: DiagnosticSink = message => {
386
+ console.warn(`[astryx theme] ${message}`);
387
+ };
388
+
389
+ /** Build receipts collect warning text; runtime callers warn by default. */
390
+ function diagnosticSink(warnings: string[] | undefined): DiagnosticSink {
391
+ return warnings ? message => warnings.push(message) : warnToConsole;
392
+ }
393
+
394
+ /** Shorten an authored value for a one-line message. */
395
+ function previewValue(value: string): string {
396
+ const oneLine = JSON.stringify(value);
397
+ return oneLine.length > 80 ? `${oneLine.slice(0, 77)}..."` : oneLine;
398
+ }
399
+
400
+ function reportDrop(
401
+ sink: DiagnosticSink,
402
+ property: string,
403
+ value: string,
404
+ location: string,
405
+ reason: string,
406
+ ): void {
407
+ sink(
408
+ `dropped "${property}" in ${location}: ${reason} (value: ${previewValue(value)})`,
409
+ );
410
+ }
411
+
412
+ /**
413
+ * True when `name: value;` stays exactly one declaration; otherwise reports
414
+ * the drop and returns false. Both checks follow CSS syntax, so valid CSS a
415
+ * browser keeps inside one declaration (a `data:` URI, `Gill\ Sans`, a closed
416
+ * comment, a vendor-prefixed property) is never refused; see
417
+ * declarationBoundary.ts for the rules.
418
+ */
419
+ function acceptDeclaration(
420
+ property: string,
421
+ value: string,
422
+ location: string,
423
+ sink: DiagnosticSink,
424
+ ): boolean {
425
+ const reason = checkDeclarationName(property) ?? checkDeclarationValue(value);
426
+ if (reason === null) {
427
+ return true;
428
+ }
429
+ reportDrop(sink, property, value, location, reason);
430
+ return false;
431
+ }
432
+
433
+ /**
434
+ * Assemble `prop: value;` lines, dropping (and reporting) any entry whose
435
+ * property or value could not stay a single declaration. Theme definitions
436
+ * are code, but apps do assemble them from stored input (brand colors,
437
+ * white-labeling), so the generated stylesheet must never extend beyond the
438
+ * declarations it means to emit.
439
+ */
440
+ function joinDeclarations(
441
+ entries: [string, string][],
442
+ location: string,
443
+ sink: DiagnosticSink,
444
+ mapProp: (prop: string) => string = p => p,
445
+ ): string {
446
+ const lines: string[] = [];
447
+ for (const [rawProp, rawValue] of entries) {
448
+ // Legacy unenrolled tokens retain non-string values. Scan the same text
449
+ // interpolation has always emitted, without changing the normalized theme.
450
+ const value = `${rawValue}`;
451
+ const prop = mapProp(rawProp);
452
+ if (acceptDeclaration(prop, value, location, sink)) {
453
+ lines.push(` ${prop}: ${value};`);
454
+ }
455
+ }
456
+ return lines.join('\n');
457
+ }
458
+
459
+ /** The `:scope` token block for portable and theme-local token values. */
460
+ function tokenBlock(
461
+ source: ThemeRuleSource,
462
+ location: string,
463
+ sink: DiagnosticSink,
464
+ ): string | null {
465
+ const declarations = [
466
+ joinDeclarations(Object.entries(source.tokens), `${location}tokens`, sink),
467
+ joinDeclarations(
468
+ Object.entries(source.localTokens ?? {}),
469
+ `${location}localTokens`,
470
+ sink,
471
+ ),
472
+ ]
473
+ .filter(block => block.length > 0)
474
+ .join('\n');
475
+ const hasEntries =
476
+ Object.keys(source.tokens).length > 0 ||
477
+ Object.keys(source.localTokens ?? {}).length > 0;
478
+ return hasEntries ? ` :scope {\n${declarations}\n }` : null;
479
+ }
480
+
481
+ /** `components.button["variant:secondary"]`-style location for a rule. */
482
+ function componentLocation(
483
+ prefix: string,
484
+ component: string,
485
+ key: string,
486
+ ): string {
487
+ return `${prefix}components.${component}[${JSON.stringify(key)}]`;
488
+ }
489
+
369
490
  // =============================================================================
370
491
  // Public API
371
492
  // =============================================================================
@@ -393,9 +514,12 @@ export interface ThemeRuleSource {
393
514
  components?: ComponentStyleMap;
394
515
  }
395
516
 
396
- export function generateThemeRules(theme: ThemeRuleSource): string[] {
517
+ export function generateThemeRules(
518
+ theme: ThemeRuleSource,
519
+ warnings?: string[],
520
+ ): string[] {
397
521
  const parts: string[] = [];
398
- const tokens = theme.tokens;
522
+ const sink = diagnosticSink(warnings);
399
523
 
400
524
  // Bare prose rules reference semantic variables instead of baking the root
401
525
  // value. That lets adaptation token writes take effect through CSS alone
@@ -403,21 +527,25 @@ export function generateThemeRules(theme: ThemeRuleSource): string[] {
403
527
  const val = (key: string): string => `var(${key})`;
404
528
 
405
529
  // 1. Token block — CSS custom properties on :scope
406
- const tokenEntries = [
407
- ...Object.entries(tokens),
408
- ...Object.entries(theme.localTokens ?? {}),
409
- ];
410
- if (tokenEntries.length > 0) {
411
- const declarations = tokenEntries
412
- .map(([prop, value]) => ` ${prop}: ${value};`)
413
- .join('\n');
414
- parts.push(` :scope {\n${declarations}\n }`);
530
+ const tokenRules = tokenBlock(theme, '', sink);
531
+ if (tokenRules !== null) {
532
+ parts.push(tokenRules);
533
+ }
534
+
535
+ // 1b. Base font — apply the theme's declared body font to the scope root.
536
+ // Components styled with `font-family: inherit` (SideNav items, Buttons)
537
+ // otherwise resolve against the browser default serif, since nothing else
538
+ // sets a page font. `font-family` inherits, so one rule on :scope covers
539
+ // the tree. Only emitted when the theme declares a body font, so a bare
540
+ // theme still contributes no scope rules of its own.
541
+ if (theme.tokens['--font-family-body']) {
542
+ parts.push(` :scope {\n font-family: var(--font-family-body);\n }`);
415
543
  }
416
544
 
417
545
  // 2. Component overrides: stable .astryx-* target classes combined with
418
546
  // reflected data-* selectors for visual props and runtime states.
419
547
  if (theme.components) {
420
- generateComponentRules(theme.components, parts);
548
+ generateComponentRules(theme.components, parts, {sink});
421
549
  }
422
550
 
423
551
  // 3. Prose HTML element rules (h1-h6, p, small, code, hr)
@@ -513,6 +641,13 @@ function generateHeadingWeightOverrides(
513
641
  }
514
642
  }
515
643
 
644
+ /**
645
+ * The `fontWeight` a heading rule authors for `styleKey`, or undefined when
646
+ * it has none. A value that cannot stay one declaration is treated as not
647
+ * authored, so the override falls back to the inherited or token weight
648
+ * instead of interpolating it; the drop itself is reported where that same
649
+ * rule's declarations are emitted, so it is not reported twice here.
650
+ */
516
651
  function getAuthoredHeadingWeight(
517
652
  headingRuleMap: Record<string, unknown>,
518
653
  styleKey: string,
@@ -522,13 +657,20 @@ function getAuthoredHeadingWeight(
522
657
  return undefined;
523
658
  }
524
659
  const fontWeight = (rule as Record<string, unknown>).fontWeight;
525
- return typeof fontWeight === 'string' ? fontWeight : undefined;
660
+ return typeof fontWeight === 'string' &&
661
+ checkDeclarationValue(fontWeight) === null
662
+ ? fontWeight
663
+ : undefined;
526
664
  }
527
665
 
528
666
  /** Options shared by root, adaptation, and media-surface component lowering. */
529
667
  interface ComponentRuleOptions {
530
668
  resetInheritedPaddingSpecificity?: boolean;
531
669
  surface?: MediaSurface;
670
+ /** Receives dropped declarations; defaults to `console.warn`. */
671
+ sink?: DiagnosticSink;
672
+ /** Location prefix for diagnostics, e.g. `onDark.` or `adaptations[0].`. */
673
+ location?: string;
532
674
  }
533
675
 
534
676
  /**
@@ -545,6 +687,8 @@ function generateComponentRules(
545
687
  {
546
688
  resetInheritedPaddingSpecificity = false,
547
689
  surface,
690
+ sink = warnToConsole,
691
+ location: locationPrefix = '',
548
692
  }: ComponentRuleOptions = {},
549
693
  ): void {
550
694
  for (const [component, rules] of Object.entries(components)) {
@@ -556,6 +700,7 @@ function generateComponentRules(
556
700
 
557
701
  const suffix = parseStyleKey(key);
558
702
  const baseSelector = componentSelector(component, suffix, surface);
703
+ const location = componentLocation(locationPrefix, component, key);
559
704
 
560
705
  // Separate regular properties from pseudo-class overrides
561
706
  const props: [string, string][] = [];
@@ -564,7 +709,14 @@ function generateComponentRules(
564
709
  for (const [prop, value] of entries) {
565
710
  if (prop.startsWith(':') && typeof value === 'object') {
566
711
  pseudos.push([prop, value]);
567
- } else {
712
+ } else if (
713
+ acceptDeclaration(
714
+ toKebabCase(prop),
715
+ `${value as string}`,
716
+ location,
717
+ sink,
718
+ )
719
+ ) {
568
720
  props.push([prop, value as string]);
569
721
  }
570
722
  }
@@ -644,9 +796,12 @@ function generateComponentRules(
644
796
 
645
797
  // Emit base rule
646
798
  if (finalProps.length > 0) {
647
- const declarations = finalProps
648
- .map(([prop, value]) => ` ${toKebabCase(prop)}: ${value};`)
649
- .join('\n');
799
+ const declarations = joinDeclarations(
800
+ finalProps,
801
+ location,
802
+ sink,
803
+ toKebabCase,
804
+ );
650
805
  parts.push(` ${baseSelector} {\n${declarations}\n }`);
651
806
  }
652
807
 
@@ -654,9 +809,12 @@ function generateComponentRules(
654
809
  for (const [pseudo, pseudoStyles] of pseudos) {
655
810
  const pseudoEntries = Object.entries(pseudoStyles);
656
811
  if (pseudoEntries.length > 0) {
657
- const declarations = pseudoEntries
658
- .map(([prop, value]) => ` ${toKebabCase(prop)}: ${value};`)
659
- .join('\n');
812
+ const declarations = joinDeclarations(
813
+ pseudoEntries,
814
+ `${location}[${JSON.stringify(pseudo)}]`,
815
+ sink,
816
+ toKebabCase,
817
+ );
660
818
  parts.push(
661
819
  ` ${appendPseudoToSelectorList(baseSelector, pseudo)} {\n${declarations}\n }`,
662
820
  );
@@ -822,8 +980,11 @@ function generateSizeOverrides(
822
980
  * Component rules (tokens, stable .astryx-* targets plus data-* selectors) are
823
981
  * that need to beat StyleX — they stay in astryx-theme (above StyleX layers).
824
982
  */
825
- export function generateThemeRulesSplit(theme: DefinedTheme): ThemeRulesSplit {
826
- const allRules = generateThemeRules(theme);
983
+ export function generateThemeRulesSplit(
984
+ theme: DefinedTheme,
985
+ warnings?: string[],
986
+ ): ThemeRulesSplit {
987
+ const allRules = generateThemeRules(theme, warnings);
827
988
 
828
989
  const prose: string[] = [];
829
990
  const component: string[] = [];
@@ -847,22 +1008,29 @@ export function generateThemeRulesSplit(theme: DefinedTheme): ThemeRulesSplit {
847
1008
  * to media contexts — only tokens change. Themes can further customize
848
1009
  * via onDark.components / onLight.components.
849
1010
  */
850
- export function generateOnMediaCSS(theme: DefinedTheme): string {
1011
+ export function generateOnMediaCSS(
1012
+ theme: DefinedTheme,
1013
+ warnings?: string[],
1014
+ ): string {
851
1015
  const parts: string[] = [];
852
1016
  const scopeSelector = themeScopeStart(theme.name);
1017
+ const sink = diagnosticSink(warnings);
853
1018
 
854
1019
  for (const surface of ['dark', 'light'] as const) {
855
1020
  const onMedia = surface === 'dark' ? theme.__onDark : theme.__onLight;
856
1021
  if (!onMedia) {
857
1022
  continue;
858
1023
  }
1024
+ const location = surface === 'dark' ? 'onDark.' : 'onLight.';
859
1025
 
860
1026
  // Token overrides
861
1027
  const tokenEntries = Object.entries(onMedia.tokens);
862
1028
  if (tokenEntries.length > 0) {
863
- const declarations = tokenEntries
864
- .map(([prop, value]) => ` ${prop}: ${value};`)
865
- .join('\n');
1029
+ const declarations = joinDeclarations(
1030
+ tokenEntries,
1031
+ `${location}tokens`,
1032
+ sink,
1033
+ );
866
1034
  parts.push(` ${mediaSelector(surface)} {\n${declarations}\n }`);
867
1035
  }
868
1036
 
@@ -872,6 +1040,8 @@ export function generateOnMediaCSS(theme: DefinedTheme): string {
872
1040
  generateComponentRules(onMedia.components, parts, {
873
1041
  resetInheritedPaddingSpecificity: true,
874
1042
  surface,
1043
+ sink,
1044
+ location,
875
1045
  });
876
1046
  generateColorOverrides(onMedia.components, parts, surface);
877
1047
  generateSizeOverrides(onMedia.components, parts, surface);
@@ -895,22 +1065,22 @@ export function generateOnMediaCSS(theme: DefinedTheme): string {
895
1065
  }
896
1066
 
897
1067
  /** Generate only the declarations one adaptation rule writes. */
898
- function generateAdaptationRuleRules(rule: ThemeRuleSource): string[] {
1068
+ function generateAdaptationRuleRules(
1069
+ rule: ThemeRuleSource,
1070
+ location: string,
1071
+ sink: DiagnosticSink,
1072
+ ): string[] {
899
1073
  const parts: string[] = [];
900
- const tokenEntries = [
901
- ...Object.entries(rule.tokens),
902
- ...Object.entries(rule.localTokens ?? {}),
903
- ];
904
- if (tokenEntries.length > 0) {
905
- const declarations = tokenEntries
906
- .map(([prop, value]) => ` ${prop}: ${value};`)
907
- .join('\n');
908
- parts.push(` :scope {\n${declarations}\n }`);
1074
+ const tokens = tokenBlock(rule, location, sink);
1075
+ if (tokens !== null) {
1076
+ parts.push(tokens);
909
1077
  }
910
1078
 
911
1079
  if (rule.components) {
912
1080
  generateComponentRules(rule.components, parts, {
913
1081
  resetInheritedPaddingSpecificity: true,
1082
+ sink,
1083
+ location,
914
1084
  });
915
1085
  generateColorOverrides(rule.components, parts);
916
1086
  generateSizeOverrides(rule.components, parts);
@@ -948,7 +1118,11 @@ function generateAdaptationMediaBlock(
948
1118
  * rule. Bare prose follows semantic variables, so token writes need no duplicate
949
1119
  * prose selectors here.
950
1120
  */
951
- export function generateAdaptationCSS(theme: DefinedTheme): ThemeCSSOutput {
1121
+ export function generateAdaptationCSS(
1122
+ theme: DefinedTheme,
1123
+ warnings?: string[],
1124
+ ): ThemeCSSOutput {
1125
+ const sink = diagnosticSink(warnings);
952
1126
  // Built themes can reach this compiler without passing through defineTheme.
953
1127
  // Validate retained metadata even when it has no rules (and therefore emits
954
1128
  // no CSS): width points remain observable through AppShell and inheritance.
@@ -982,8 +1156,12 @@ export function generateAdaptationCSS(theme: DefinedTheme): ThemeCSSOutput {
982
1156
  // active whenever the later condition matches, even if the earlier condition
983
1157
  // does not. Pairwise merging cannot repair that: multiple mutually exclusive
984
1158
  // earlier rules can each overlap one broad later rule.
985
- for (const rule of rules) {
986
- const parts = generateAdaptationRuleRules(rule);
1159
+ for (const [index, rule] of rules.entries()) {
1160
+ const parts = generateAdaptationRuleRules(
1161
+ rule,
1162
+ `adaptations[${index}].`,
1163
+ sink,
1164
+ );
987
1165
  if (parts.length === 0) {
988
1166
  continue;
989
1167
  }
@@ -1029,12 +1207,9 @@ export function generateAdaptationCSS(theme: DefinedTheme): ThemeCSSOutput {
1029
1207
  return {prose: '', component: blocks.join('\n\n')};
1030
1208
  }
1031
1209
 
1032
- /** Indent a generated rule one level further, for nesting inside `@media`. */
1210
+ /** Indent the rule start without rewriting newlines inside authored values. */
1033
1211
  function indentRule(rule: string): string {
1034
- return rule
1035
- .split('\n')
1036
- .map(line => (line.length > 0 ? ` ${line}` : line))
1037
- .join('\n');
1212
+ return ` ${rule}`;
1038
1213
  }
1039
1214
 
1040
1215
  /**
@@ -1075,8 +1250,11 @@ export function generateDataTokenDefaultsCSS(): string {
1075
1250
  * The theme-independent `--color-data-*` defaults are not part of this output:
1076
1251
  * see `generateDataTokenDefaultsCSS`.
1077
1252
  */
1078
- export function generateThemeCSS(theme: DefinedTheme): ThemeCSSOutput {
1079
- const {component, prose} = generateThemeRulesSplit(theme);
1253
+ export function generateThemeCSS(
1254
+ theme: DefinedTheme,
1255
+ warnings?: string[],
1256
+ ): ThemeCSSOutput {
1257
+ const {component, prose} = generateThemeRulesSplit(theme, warnings);
1080
1258
  const scopeSelector = themeScopeStart(theme.name);
1081
1259
  const scopeTo = THEME_SCOPE_TO;
1082
1260
 
@@ -1096,14 +1274,14 @@ export function generateThemeCSS(theme: DefinedTheme): ThemeCSSOutput {
1096
1274
 
1097
1275
  // Adaptations follow the root theme in authored order. Media-surface rules
1098
1276
  // follow adaptations so onDark/onLight keep their specified precedence.
1099
- const adaptationCss = generateAdaptationCSS(theme);
1277
+ const adaptationCss = generateAdaptationCSS(theme, warnings);
1100
1278
  if (adaptationCss.component) {
1101
1279
  componentCss = componentCss
1102
1280
  ? `${componentCss}\n\n${adaptationCss.component}`
1103
1281
  : adaptationCss.component;
1104
1282
  }
1105
1283
 
1106
- const onMediaCss = generateOnMediaCSS(theme);
1284
+ const onMediaCss = generateOnMediaCSS(theme, warnings);
1107
1285
  if (onMediaCss) {
1108
1286
  componentCss = componentCss
1109
1287
  ? `${componentCss}\n\n${onMediaCss}`
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  import {describe, expect, it} from 'vitest';
25
- import {defineTheme} from './defineTheme';
25
+ import {defineTheme, generateThemeCSS} from './defineTheme';
26
26
  import {resolveTokenValue} from './resolveThemeValues';
27
27
 
28
28
  /** Exactly what the pre-extraction implementation did with each shape. */
@@ -43,6 +43,9 @@ describe('root token values keep their historical acceptance', () => {
43
43
  });
44
44
 
45
45
  expect(theme.tokens['--color-background-body']).toBe(expected);
46
+ expect(generateThemeCSS(theme).component).toContain(
47
+ `--color-background-body: ${String(expected)};`,
48
+ );
46
49
  });
47
50
 
48
51
  it('still resolves the documented shapes', () => {
@@ -10,6 +10,11 @@
10
10
  * @output Exports reusable background-color and background-image state styles
11
11
  * @position Internal styling utility for interactive core surfaces
12
12
  *
13
+ * Every surface that paints a press composes one of these (or carries its own
14
+ * `:active` rule), so a change to how the system answers a press is made here
15
+ * once. `pressedBackgroundColor` is the press without the hover, for controls
16
+ * whose hover is a colour or nothing.
17
+ *
13
18
  * Keep the enabled guard outside the individual states. StyleX assigns an
14
19
  * extra priority bucket (and generated selector specificity) to media-nested
15
20
  * rules. Repeating `:active` inside the hover-capable branch gives hover and
@@ -71,4 +76,19 @@ export const interactionOverlayStyles = stylex.create({
71
76
  },
72
77
  },
73
78
  },
79
+ /**
80
+ * The pressed arm alone, for a control whose hover answer is its own — a
81
+ * text link changes colour, a disclosure row has none — so a press paints
82
+ * the system's pressed overlay without adding a hover surface the control
83
+ * never had. Same enabled guard as the paired states above.
84
+ */
85
+ pressedBackgroundColor: {
86
+ backgroundColor: {
87
+ default: null,
88
+ [ENABLED]: {
89
+ default: null,
90
+ ':active': colorVars['--color-overlay-pressed'],
91
+ },
92
+ },
93
+ },
74
94
  });
@@ -0,0 +1,88 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+ import {isSafeDestination, isSafeUrl, sanitizeUrl} from './safeUrl';
5
+
6
+ // Expectations come from the navigation contract, not another policy helper.
7
+ const accepted = [
8
+ 'https://example.com/a?b=c#d',
9
+ 'http://example.com',
10
+ '//example.com/protocol-relative',
11
+ '/relative/path',
12
+ 'relative/path',
13
+ '#fragment',
14
+ 'mailto:a@example.com',
15
+ 'tel:+1234567890',
16
+ 'custom:open/document',
17
+ 'data:image/png;base64,iVBORw0KGgo=',
18
+ 'data:text/plain,download',
19
+ ];
20
+ const rejected = [
21
+ 'javascript:alert(1)',
22
+ 'JaVaScRiPt:alert(1)',
23
+ ' javascript:alert(1)',
24
+ 'vbscript:MsgBox(1)',
25
+ 'data:text/html,<script>alert(1)</script>',
26
+ 'DATA:TEXT/HTML;base64,PHNjcmlwdD4=',
27
+ 'java\tscript:alert(1)',
28
+ 'java\nscript:alert(1)',
29
+ '\x00javascript:alert(1)',
30
+ 'java\x7fscript:alert(1)',
31
+ ];
32
+
33
+ describe('navigation strings', () => {
34
+ it.each(accepted)('accepts %s', url => {
35
+ expect(isSafeUrl(url)).toBe(true);
36
+ expect(isSafeDestination(url)).toBe(true);
37
+ expect(sanitizeUrl(url)).toBe(url);
38
+ });
39
+ it.each(rejected)('rejects %s', url => {
40
+ expect(isSafeUrl(url)).toBe(false);
41
+ expect(isSafeDestination(url)).toBe(false);
42
+ expect(sanitizeUrl(url)).toBeNull();
43
+ });
44
+ it('normalizes accepted strings only when requested', () => {
45
+ expect(sanitizeUrl(' /docs\n')).toBe('/docs');
46
+ expect(sanitizeUrl('/do\x00cs')).toBe('/docs');
47
+ });
48
+ });
49
+
50
+ describe('structured destinations', () => {
51
+ it.each([
52
+ {pathname: '/docs', search: '?q=1', hash: '#a'},
53
+ {href: 'https://example.com'},
54
+ {protocol: 'https:', hostname: 'example.com', pathname: '/docs'},
55
+ {protocol: 'custom', pathname: 'document'},
56
+ {protocol: 'mailto:', pathname: 'a@example.com'},
57
+ {protocol: 'data:', pathname: 'image/png;base64,aA=='},
58
+ {search: '?page=2'},
59
+ {hash: '#section'},
60
+ {},
61
+ new URL('https://example.com/docs'),
62
+ undefined,
63
+ null,
64
+ ])('accepts supported destination %j', destination => {
65
+ expect(isSafeDestination(destination)).toBe(true);
66
+ });
67
+ it.each([
68
+ {pathname: 'javascript:alert(1)'},
69
+ {pathname: 'java\x00script:alert(1)'},
70
+ {href: 'vbscript:MsgBox(1)'},
71
+ {pathname: '/ok', href: 'javascript:1'},
72
+ {protocol: 'javascript:', pathname: 'alert(1)'},
73
+ {protocol: 'javascript', pathname: 'alert(1)'},
74
+ {protocol: ' JaVa\tsCrIpT ', pathname: 'alert(1)'},
75
+ {protocol: 'vbscript', pathname: 'MsgBox(1)'},
76
+ {protocol: 'data:', pathname: 'text/html,<b>x</b>'},
77
+ {protocol: 'data', pathname: 'TEXT/HTML;base64,PGI+eDwvYj4='},
78
+ {protocol: 'data:', host: 'text/', pathname: 'html,<b>x</b>'},
79
+ {protocol: 'data:', hostname: 'text/html,<b>x</b>'},
80
+ {href: '/safe', protocol: 'javascript:'},
81
+ new URL('javascript:alert(1)'),
82
+ {pathname: 42},
83
+ {protocol: {toString: (): string => 'javascript:'}},
84
+ 3,
85
+ ])('rejects blocked or unsupported destination %j', destination => {
86
+ expect(isSafeDestination(destination)).toBe(false);
87
+ });
88
+ });
@@ -0,0 +1,85 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file safeUrl.ts
5
+ * @input Navigation strings or supported structured router destinations
6
+ * @output The shared blocked-scheme decision, without rewriting accepted inputs
7
+ * @position Core navigation policy; embedded-resource policy is separate
8
+ */
9
+
10
+ function normalizeUrl(url: string): string {
11
+ // eslint-disable-next-line no-control-regex -- inspect schemes without browser-ignored controls
12
+ return url.replace(/[\x00-\x1f\x7f]/g, '').trim();
13
+ }
14
+
15
+ /** Return a normalized navigation URL, or null for a blocked scheme. */
16
+ export function sanitizeUrl(url: string): string | null {
17
+ const normalized = normalizeUrl(url);
18
+ const lower = normalized.toLowerCase();
19
+ if (
20
+ lower.startsWith('javascript:') ||
21
+ lower.startsWith('vbscript:') ||
22
+ lower.startsWith('data:text/html')
23
+ ) {
24
+ return null;
25
+ }
26
+ return normalized;
27
+ }
28
+
29
+ /** Inspect a navigation string without changing the value used by its sink. */
30
+ export function isSafeUrl(url: string): boolean {
31
+ return sanitizeUrl(url) !== null;
32
+ }
33
+
34
+ /**
35
+ * Inspect React Router paths, Next.js URL objects, and URL instances without
36
+ * serializing or replacing the object handed to the router. Search/query/hash
37
+ * are route-relative data, not schemes. Absent destinations remain absent.
38
+ */
39
+ export function isSafeDestination(destination: unknown): boolean {
40
+ if (destination == null) {
41
+ return true;
42
+ }
43
+ if (typeof destination === 'string') {
44
+ return isSafeUrl(destination);
45
+ }
46
+ if (typeof destination !== 'object') {
47
+ return false;
48
+ }
49
+
50
+ const {pathname, href, protocol, host, hostname} = destination as {
51
+ pathname?: unknown;
52
+ href?: unknown;
53
+ protocol?: unknown;
54
+ host?: unknown;
55
+ hostname?: unknown;
56
+ };
57
+ for (const field of [pathname, href, protocol, host, hostname]) {
58
+ if (field != null && typeof field !== 'string') {
59
+ return false;
60
+ }
61
+ }
62
+ if (typeof pathname === 'string' && !isSafeUrl(pathname)) {
63
+ return false;
64
+ }
65
+ if (typeof href === 'string' && !isSafeUrl(href)) {
66
+ return false;
67
+ }
68
+ if (typeof protocol === 'string' && protocol !== '') {
69
+ // URL-object formatters accept a protocol with or without its colon and
70
+ // can assemble a data media type from the host and pathname fields.
71
+ const normalizedProtocol = normalizeUrl(protocol);
72
+ const prefix = normalizedProtocol.endsWith(':')
73
+ ? normalizedProtocol
74
+ : `${normalizedProtocol}:`;
75
+ if (!isSafeUrl(prefix)) {
76
+ return false;
77
+ }
78
+ const body = (host || hostname || '') as string;
79
+ const path = typeof pathname === 'string' ? pathname : '';
80
+ if (!isSafeUrl(`${prefix}${body}${path}`)) {
81
+ return false;
82
+ }
83
+ }
84
+ return true;
85
+ }