@astryxdesign/core 0.7.1 → 0.7.2-canary.020e1fe

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 (377) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +40 -1
  3. package/dist/AspectRatio/AspectRatio.d.ts +6 -8
  4. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  5. package/dist/AspectRatio/AspectRatio.js +44 -3
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +5 -2
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +38 -14
  10. package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
  11. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  12. package/dist/CheckboxInput/CheckboxInput.js +3 -1
  13. package/dist/CheckboxList/CheckboxListItem.d.ts +9 -1
  14. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  15. package/dist/CheckboxList/CheckboxListItem.js +4 -2
  16. package/dist/CommandPalette/CommandPaletteInput.d.ts +1 -1
  17. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  18. package/dist/CommandPalette/CommandPaletteInput.js +37 -5
  19. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  20. package/dist/ContextMenu/ContextMenu.js +5 -2
  21. package/dist/Dialog/Dialog.d.ts.map +1 -1
  22. package/dist/Dialog/Dialog.js +20 -8
  23. package/dist/Drawer/Drawer.d.ts +191 -0
  24. package/dist/Drawer/Drawer.d.ts.map +1 -0
  25. package/dist/Drawer/Drawer.js +382 -0
  26. package/dist/Drawer/DrawerHeader.d.ts +85 -0
  27. package/dist/Drawer/DrawerHeader.d.ts.map +1 -0
  28. package/dist/Drawer/DrawerHeader.js +134 -0
  29. package/dist/Drawer/index.d.ts +11 -0
  30. package/dist/Drawer/index.d.ts.map +1 -0
  31. package/dist/Drawer/index.js +11 -0
  32. package/dist/Drawer/useDrawerDialogPresence.d.ts +42 -0
  33. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -0
  34. package/dist/Drawer/useDrawerDialogPresence.js +236 -0
  35. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -1
  36. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  37. package/dist/DropdownMenu/DropdownMenu.js +83 -11
  38. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -0
  39. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  40. package/dist/DropdownMenu/DropdownMenuContext.js +21 -0
  41. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +5 -3
  42. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -1
  43. package/dist/DropdownMenu/DropdownMenuDivider.js +7 -3
  44. package/dist/DropdownMenu/DropdownMenuGroup.d.ts +6 -4
  45. package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
  46. package/dist/DropdownMenu/DropdownMenuGroup.js +4 -3
  47. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  48. package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -8
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +24 -0
  50. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -0
  51. package/dist/DropdownMenu/lazyMenuBottomSheet.js +46 -0
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +4 -10
  54. package/dist/Field/Field.d.ts +1 -1
  55. package/dist/Field/Field.d.ts.map +1 -1
  56. package/dist/Field/Field.js +8 -4
  57. package/dist/Grid/GridSpan.d.ts.map +1 -1
  58. package/dist/Grid/GridSpan.js +1 -0
  59. package/dist/Heading/Heading.d.ts.map +1 -1
  60. package/dist/Heading/Heading.js +4 -2
  61. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  62. package/dist/HoverCard/useHoverCard.js +10 -2
  63. package/dist/Item/Item.d.ts +6 -2
  64. package/dist/Item/Item.d.ts.map +1 -1
  65. package/dist/Item/Item.js +19 -4
  66. package/dist/Layer/layerViewportInset.stylex.d.ts +6 -2
  67. package/dist/Layer/layerViewportInset.stylex.d.ts.map +1 -1
  68. package/dist/Layer/layerViewportInset.stylex.js +6 -2
  69. package/dist/Layer/useLayer.d.ts.map +1 -1
  70. package/dist/Layer/useLayer.js +13 -7
  71. package/dist/Layer/useLayerDismissal.d.ts +1 -1
  72. package/dist/Layer/useLayerDismissal.js +1 -1
  73. package/dist/Layout/container.stylex.d.ts +5 -5
  74. package/dist/Layout/container.stylex.js +4 -4
  75. package/dist/List/List.d.ts +46 -19
  76. package/dist/List/List.d.ts.map +1 -1
  77. package/dist/List/List.js +28 -10
  78. package/dist/List/ListContext.d.ts +8 -3
  79. package/dist/List/ListContext.d.ts.map +1 -1
  80. package/dist/List/ListContext.js +7 -2
  81. package/dist/List/ListItem.d.ts +13 -5
  82. package/dist/List/ListItem.d.ts.map +1 -1
  83. package/dist/List/ListItem.js +21 -8
  84. package/dist/List/index.d.ts +1 -1
  85. package/dist/List/index.d.ts.map +1 -1
  86. package/dist/Markdown/Markdown.d.ts.map +1 -1
  87. package/dist/Markdown/Markdown.js +8 -1
  88. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -1
  89. package/dist/Markdown/plugins/frontmatter.js +7 -3
  90. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  91. package/dist/MetadataList/MetadataList.js +5 -1
  92. package/dist/MetadataList/MetadataListContext.d.ts +1 -1
  93. package/dist/MetadataList/MetadataListContext.d.ts.map +1 -1
  94. package/dist/MobileNav/MobileNav.d.ts +9 -0
  95. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  96. package/dist/MobileNav/MobileNav.js +30 -3
  97. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  98. package/dist/MultiSelector/MultiSelector.js +22 -8
  99. package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +100 -2
  100. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  101. package/dist/MultiSelector/hooks.js +3 -1
  102. package/dist/Pagination/Pagination.d.ts +8 -5
  103. package/dist/Pagination/Pagination.d.ts.map +1 -1
  104. package/dist/Pagination/Pagination.js +21 -8
  105. package/dist/Popover/Popover.js +2 -2
  106. package/dist/PowerSearch/PowerSearch.js +1 -1
  107. package/dist/RadioList/RadioList.d.ts +1 -1
  108. package/dist/RadioList/RadioList.d.ts.map +1 -1
  109. package/dist/RadioList/RadioList.js +6 -2
  110. package/dist/RadioList/RadioListItem.d.ts +9 -1
  111. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  112. package/dist/RadioList/RadioListItem.js +2 -0
  113. package/dist/Selector/hooks.d.ts.map +1 -1
  114. package/dist/Selector/hooks.js +6 -2
  115. package/dist/SideNav/SideNavItem.d.ts +8 -1
  116. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  117. package/dist/SideNav/SideNavItem.js +71 -9
  118. package/dist/Switch/Switch.d.ts +1 -1
  119. package/dist/Switch/Switch.d.ts.map +1 -1
  120. package/dist/Switch/Switch.js +13 -1
  121. package/dist/TabList/TabMenu.d.ts +2 -2
  122. package/dist/TabList/TabMenu.d.ts.map +1 -1
  123. package/dist/TabList/TabMenu.js +6 -3
  124. package/dist/Text/Text.d.ts.map +1 -1
  125. package/dist/Text/Text.js +4 -2
  126. package/dist/Text/useTruncation.d.ts +7 -5
  127. package/dist/Text/useTruncation.d.ts.map +1 -1
  128. package/dist/Text/useTruncation.js +16 -5
  129. package/dist/ToggleButton/ToggleButton.d.ts +3 -2
  130. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  131. package/dist/ToggleButton/ToggleButton.js +12 -1
  132. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  133. package/dist/Tooltip/useTooltip.js +11 -0
  134. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  135. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  136. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  137. package/dist/TopNav/TopNavMenu.js +3 -3
  138. package/dist/TreeList/TreeList.d.ts.map +1 -1
  139. package/dist/TreeList/TreeList.js +1 -0
  140. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  141. package/dist/TreeList/TreeListBranches.js +2 -2
  142. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  143. package/dist/TreeList/TreeListItem.js +57 -9
  144. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  145. package/dist/Typeahead/BaseTypeahead.js +26 -0
  146. package/dist/astryx.css +47 -13
  147. package/dist/hooks/useEntryAnimation.d.ts +2 -1
  148. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  149. package/dist/hooks/useEntryAnimation.js +14 -12
  150. package/dist/hooks/useIsHydrating.d.ts +6 -0
  151. package/dist/hooks/useIsHydrating.d.ts.map +1 -0
  152. package/dist/hooks/useIsHydrating.js +30 -0
  153. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  154. package/dist/hooks/useMenuHover.js +18 -13
  155. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  156. package/dist/i18n/generated-locales/en.generated.js +112 -1
  157. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  158. package/dist/i18n/generated-locales/pseudo.generated.js +112 -1
  159. package/dist/index.d.ts +1 -0
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +1 -0
  162. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  163. package/dist/theme/derivedVarRegistry.js +0 -3
  164. package/dist/utils/focusOutline.stylex.d.ts +4 -0
  165. package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
  166. package/dist/utils/focusOutline.stylex.js +4 -0
  167. package/dist/utils/plainDate.d.ts.map +1 -1
  168. package/dist/utils/plainDate.js +15 -9
  169. package/dist/utils/sharedMutationObserver.d.ts +21 -0
  170. package/dist/utils/sharedMutationObserver.d.ts.map +1 -0
  171. package/dist/utils/sharedMutationObserver.js +69 -0
  172. package/locales/en.json +444 -0
  173. package/locales/pseudo.json +333 -0
  174. package/package.json +9 -4
  175. package/scripts/agent-doc-state.mjs +38 -0
  176. package/scripts/postinstall.mjs +1 -1
  177. package/src/AppShell/AppShell.tsx +45 -1
  178. package/src/AspectRatio/AspectRatio.test.tsx +44 -43
  179. package/src/AspectRatio/AspectRatio.tsx +38 -18
  180. package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +157 -0
  181. package/src/Avatar/Avatar.doc.mjs +5 -3
  182. package/src/AvatarGroup/AvatarGroup.doc.mjs +3 -2
  183. package/src/Breadcrumbs/BreadcrumbItem.tsx +4 -1
  184. package/src/Breadcrumbs/Breadcrumbs.test.tsx +30 -1
  185. package/src/Button/Button.doc.mjs +2 -2
  186. package/src/Button/Button.test.tsx +155 -9
  187. package/src/Button/Button.tsx +39 -16
  188. package/src/Button/__tests__/Button.a11y.known-failures.ts +1 -62
  189. package/src/Button/__tests__/Button.a11y.states.ts +0 -10
  190. package/src/Card/Card.doc.mjs +1 -1
  191. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +3 -1
  192. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  193. package/src/CheckboxInput/CheckboxInput.tsx +3 -1
  194. package/src/CheckboxList/CheckboxList.test.tsx +22 -0
  195. package/src/CheckboxList/CheckboxListItem.doc.mjs +16 -0
  196. package/src/CheckboxList/CheckboxListItem.tsx +10 -0
  197. package/src/Code/Code.doc.mjs +1 -0
  198. package/src/CommandPalette/CommandPalette.doc.mjs +2 -0
  199. package/src/CommandPalette/CommandPaletteInput.test.tsx +53 -4
  200. package/src/CommandPalette/CommandPaletteInput.tsx +33 -3
  201. package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +335 -0
  202. package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +292 -0
  203. package/src/ContextMenu/ContextMenu.doc.mjs +2 -3
  204. package/src/ContextMenu/ContextMenu.tsx +4 -1
  205. package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +438 -0
  206. package/src/Dialog/Dialog.test.tsx +46 -0
  207. package/src/Dialog/Dialog.tsx +20 -7
  208. package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -14
  209. package/src/Drawer/Drawer.doc.mjs +473 -0
  210. package/src/Drawer/Drawer.spec.md +357 -0
  211. package/src/Drawer/Drawer.test.tsx +1300 -0
  212. package/src/Drawer/Drawer.tsx +613 -0
  213. package/src/Drawer/DrawerDirection.test.tsx +127 -0
  214. package/src/Drawer/DrawerHeader.doc.mjs +293 -0
  215. package/src/Drawer/DrawerHeader.test.tsx +102 -0
  216. package/src/Drawer/DrawerHeader.tsx +235 -0
  217. package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +401 -0
  218. package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +297 -0
  219. package/src/Drawer/index.ts +13 -0
  220. package/src/Drawer/useDrawerDialogPresence.ts +284 -0
  221. package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +57 -0
  222. package/src/DropdownMenu/DropdownMenu.test.tsx +79 -18
  223. package/src/DropdownMenu/DropdownMenu.tsx +83 -12
  224. package/src/DropdownMenu/DropdownMenuContext.tsx +24 -0
  225. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +2 -2
  226. package/src/DropdownMenu/DropdownMenuDivider.spec.md +192 -0
  227. package/src/DropdownMenu/DropdownMenuDivider.tsx +9 -5
  228. package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +169 -10
  229. package/src/DropdownMenu/DropdownMenuGroup.tsx +6 -4
  230. package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +126 -0
  231. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +185 -0
  232. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +19 -5
  233. package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +280 -0
  234. package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +334 -0
  235. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +145 -0
  236. package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +26 -0
  237. package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +65 -0
  238. package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +138 -0
  239. package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +136 -0
  240. package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +43 -0
  241. package/src/DropdownMenu/lazyMenuBottomSheet.ts +71 -0
  242. package/src/EmptyState/EmptyState.doc.mjs +3 -2
  243. package/src/EmptyState/EmptyState.spec.md +189 -0
  244. package/src/EmptyState/EmptyState.test.tsx +74 -3
  245. package/src/EmptyState/EmptyState.tsx +4 -8
  246. package/src/Field/Field.doc.mjs +14 -8
  247. package/src/Field/Field.spec.md +217 -0
  248. package/src/Field/Field.test.tsx +60 -2
  249. package/src/Field/Field.tsx +17 -8
  250. package/src/Field/FieldLabel.doc.mjs +133 -26
  251. package/src/Field/FieldLabel.spec.md +172 -0
  252. package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +296 -0
  253. package/src/Grid/GridSpan.tsx +3 -0
  254. package/src/Heading/Heading.test.tsx +57 -0
  255. package/src/Heading/Heading.tsx +6 -2
  256. package/src/HoverCard/HoverCard.test.tsx +58 -0
  257. package/src/HoverCard/useHoverCard.tsx +10 -2
  258. package/src/Icon/Icon.doc.mjs +36 -0
  259. package/src/IconButton/IconButton.doc.mjs +2 -2
  260. package/src/IconButton/IconButton.test.tsx +3 -1
  261. package/src/Item/Item.doc.mjs +12 -8
  262. package/src/Item/Item.spec.md +226 -0
  263. package/src/Item/Item.test.tsx +61 -0
  264. package/src/Item/Item.tsx +40 -4
  265. package/src/Layer/layerViewportInset.source-build.test.mjs +90 -0
  266. package/src/Layer/layerViewportInset.stylex.ts +6 -2
  267. package/src/Layer/layerViewportInset.test.ts +1 -0
  268. package/src/Layer/useLayer.tsx +24 -13
  269. package/src/Layer/useLayerDismissal.ts +1 -1
  270. package/src/Layout/LayoutHeader.doc.mjs +5 -0
  271. package/src/Layout/container.stylex.ts +6 -6
  272. package/src/List/List.doc.mjs +14 -1
  273. package/src/List/List.spec.md +129 -77
  274. package/src/List/List.test.tsx +163 -94
  275. package/src/List/List.tsx +74 -18
  276. package/src/List/ListContext.tsx +9 -3
  277. package/src/List/ListItem.doc.mjs +24 -5
  278. package/src/List/ListItem.tsx +56 -27
  279. package/src/List/index.ts +1 -1
  280. package/src/Markdown/Markdown.tsx +14 -1
  281. package/src/Markdown/plugins/frontmatter.test.tsx +25 -0
  282. package/src/Markdown/plugins/frontmatter.ts +7 -3
  283. package/src/MetadataList/MetadataList.doc.mjs +3 -3
  284. package/src/MetadataList/MetadataList.test.tsx +38 -0
  285. package/src/MetadataList/MetadataList.tsx +8 -1
  286. package/src/MetadataList/MetadataListContext.tsx +1 -1
  287. package/src/MobileNav/MobileNav.tsx +30 -3
  288. package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +173 -15
  289. package/src/MoreMenu/MoreMenu.test.tsx +5 -1
  290. package/src/MultiSelector/MultiSelector.test.tsx +159 -0
  291. package/src/MultiSelector/MultiSelector.tsx +40 -7
  292. package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +112 -2
  293. package/src/MultiSelector/hooks.ts +3 -1
  294. package/src/MultiSelector/useMultiCombobox.doc.mjs +267 -0
  295. package/src/Pagination/Pagination.doc.mjs +7 -7
  296. package/src/Pagination/Pagination.spec.md +9 -9
  297. package/src/Pagination/Pagination.test.tsx +242 -0
  298. package/src/Pagination/Pagination.tsx +27 -11
  299. package/src/Popover/Popover.test.tsx +2 -2
  300. package/src/Popover/Popover.tsx +4 -4
  301. package/src/PowerSearch/PowerSearch.test.tsx +85 -0
  302. package/src/PowerSearch/PowerSearch.tsx +2 -2
  303. package/src/ProgressBar/ProgressBar.doc.mjs +1 -0
  304. package/src/RadioList/RadioList.fr10.test.tsx +45 -2
  305. package/src/RadioList/RadioList.test.tsx +22 -0
  306. package/src/RadioList/RadioList.tsx +4 -1
  307. package/src/RadioList/RadioListItem.doc.mjs +16 -0
  308. package/src/RadioList/RadioListItem.tsx +10 -1
  309. package/src/Resizable/useResizable.doc.mjs +2 -0
  310. package/src/ScrollableArea/ScrollableArea.doc.mjs +1 -1
  311. package/src/Selector/hooks.ts +6 -2
  312. package/src/Selector/useCombobox.doc.mjs +237 -0
  313. package/src/SideNav/SideNav.spec.md +227 -0
  314. package/src/SideNav/SideNav.test.tsx +179 -0
  315. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  316. package/src/SideNav/SideNavItem.tsx +96 -17
  317. package/src/Slider/Slider.spec.md +39 -18
  318. package/src/Spinner/Spinner.doc.mjs +1 -1
  319. package/src/Switch/Switch.test.tsx +49 -0
  320. package/src/Switch/Switch.tsx +11 -0
  321. package/src/TabList/TabList.spec.md +57 -44
  322. package/src/TabList/TabList.test.tsx +67 -0
  323. package/src/TabList/TabMenu.doc.mjs +4 -3
  324. package/src/TabList/TabMenu.tsx +7 -4
  325. package/src/Text/Text.test.tsx +57 -0
  326. package/src/Text/Text.tsx +6 -2
  327. package/src/Text/useTruncation.test.tsx +69 -0
  328. package/src/Text/useTruncation.ts +16 -5
  329. package/src/TimeInput/TimeInput.fr10.test.tsx +15 -0
  330. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  331. package/src/ToggleButton/ToggleButton.test.tsx +95 -0
  332. package/src/ToggleButton/ToggleButton.tsx +12 -1
  333. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +4 -0
  334. package/src/Tokenizer/Tokenizer.test.tsx +54 -1
  335. package/src/Toolbar/Toolbar.test.tsx +0 -8
  336. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +163 -0
  337. package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +63 -0
  338. package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +142 -0
  339. package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +126 -0
  340. package/src/Tooltip/Tooltip.test.tsx +31 -0
  341. package/src/Tooltip/useTooltip.test.tsx +13 -0
  342. package/src/Tooltip/useTooltip.tsx +11 -0
  343. package/src/TopNav/TopNavMegaMenu.test.tsx +22 -0
  344. package/src/TopNav/TopNavMegaMenu.tsx +7 -3
  345. package/src/TopNav/TopNavMenu.test.tsx +19 -0
  346. package/src/TopNav/TopNavMenu.tsx +11 -3
  347. package/src/TreeList/TreeList.doc.mjs +1 -0
  348. package/src/TreeList/TreeList.spec.md +29 -17
  349. package/src/TreeList/TreeList.test.tsx +327 -6
  350. package/src/TreeList/TreeList.tsx +5 -1
  351. package/src/TreeList/TreeListBranches.tsx +6 -0
  352. package/src/TreeList/TreeListItem.tsx +88 -13
  353. package/src/Typeahead/BaseTypeahead.tsx +26 -0
  354. package/src/Typeahead/Typeahead.test.tsx +260 -2
  355. package/src/docPropLiterals.test.ts +36 -10
  356. package/src/hooks/useEntryAnimation.doc.mjs +4 -4
  357. package/src/hooks/useEntryAnimation.ssr.test.tsx +143 -0
  358. package/src/hooks/useEntryAnimation.ts +15 -16
  359. package/src/hooks/useIsHydrating.test.tsx +70 -0
  360. package/src/hooks/useIsHydrating.ts +31 -0
  361. package/src/hooks/useMenuHover.ts +18 -13
  362. package/src/i18n/InternationalizationProvider.doc.mjs +8 -0
  363. package/src/i18n/InternationalizationProvider.spec.md +252 -0
  364. package/src/i18n/__tests__/InternationalizationProvider.test.tsx +128 -0
  365. package/src/i18n/generated-locales/en.generated.ts +121 -0
  366. package/src/i18n/generated-locales/pseudo.generated.ts +121 -0
  367. package/src/i18n/useTranslator.test.tsx +37 -0
  368. package/src/index.ts +1 -0
  369. package/src/postinstall.test.mjs +46 -0
  370. package/src/reset.css +0 -29
  371. package/src/theme/derivedVarRegistry.test.ts +7 -3
  372. package/src/theme/derivedVarRegistry.ts +0 -3
  373. package/src/utils/focusOutline.stylex.ts +4 -0
  374. package/src/utils/plainDate.test.ts +37 -1
  375. package/src/utils/plainDate.ts +15 -5
  376. package/src/utils/sharedMutationObserver.test.ts +53 -0
  377. package/src/utils/sharedMutationObserver.ts +78 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @file Field.test.tsx
5
- * @input Uses vitest, @testing-library/react, Field component
5
+ * @input Uses Vitest, Testing Library, StyleX, Field, FormLayoutContext
6
6
  * @output Unit tests for Field component behavior
7
7
  * @position Testing; validates Field.tsx implementation
8
8
  *
@@ -10,11 +10,18 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, afterEach} from 'vitest';
13
- import {render, screen, waitFor} from '@testing-library/react';
13
+ import {render, screen, waitFor, fireEvent} from '@testing-library/react';
14
+ import * as stylex from '@stylexjs/stylex';
14
15
  import {Field} from './Field';
15
16
  import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
16
17
  import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
17
18
 
19
+ const testStyles = stylex.create({
20
+ root: {
21
+ minWidth: '123px',
22
+ },
23
+ });
24
+
18
25
  function politeRegion(): HTMLElement | null {
19
26
  return document.querySelector('[data-astryx-live-region="polite"]');
20
27
  }
@@ -572,6 +579,57 @@ describe('Field', () => {
572
579
  </FormLayoutContext>
573
580
  );
574
581
 
582
+ it('composes root props without letting callers replace layout semantics', () => {
583
+ const onClick = vi.fn();
584
+ const expectedXstyleClass = stylex.props(testStyles.root).className;
585
+ if (expectedXstyleClass == null) {
586
+ throw new Error('Expected the test xstyle to produce a class name.');
587
+ }
588
+
589
+ render(
590
+ <Field
591
+ label="Name"
592
+ inputID="name-input"
593
+ data-testid="field"
594
+ data-layout="consumer"
595
+ data-owner="consumer"
596
+ className="consumer-class"
597
+ style={{width: '321px'}}
598
+ xstyle={testStyles.root}
599
+ onClick={onClick}>
600
+ <input id="name-input" />
601
+ </Field>,
602
+ {wrapper: horizontalLabelsWrapper},
603
+ );
604
+
605
+ const field = screen.getByTestId('field');
606
+ expect(field).toHaveClass('astryx-field', 'consumer-class');
607
+ expect(field).toHaveClass(expectedXstyleClass);
608
+ expect(field).toHaveAttribute('data-layout', 'horizontal-labels');
609
+ expect(field).toHaveAttribute('data-owner', 'consumer');
610
+ expect(field).toHaveStyle({width: '321px'});
611
+ fireEvent.click(field);
612
+ expect(onClick).toHaveBeenCalledOnce();
613
+ });
614
+
615
+ it('visually hides the description in horizontal-label mode when the label is hidden', () => {
616
+ render(
617
+ <Field
618
+ label="Search"
619
+ inputID="search-input"
620
+ isLabelHidden
621
+ description="Search for items"
622
+ descriptionID="search-desc">
623
+ <input id="search-input" aria-describedby="search-desc" />
624
+ </Field>,
625
+ {wrapper: horizontalLabelsWrapper},
626
+ );
627
+
628
+ const description = screen.getByText('Search for items');
629
+ expect(description.className).toContain('visuallyHidden');
630
+ expect(description).toHaveAttribute('id', 'search-desc');
631
+ });
632
+
575
633
  it('applies display:contents when in horizontal-labels context', () => {
576
634
  const {container} = render(
577
635
  <Field label="Name" inputID="name-input">
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Field.tsx
7
- * @input Uses React, HTMLAttributes, ReactNode, FieldLabel, IconType, useFieldStatusAnnouncement
7
+ * @input Uses React, StyleX, BaseProps, FieldLabel/FieldStatus, FormLayoutContext, Text, VisuallyHidden, theme/status utilities
8
8
  * @output Exports Field component, FieldProps
9
9
  * @position Core implementation; consumed by index.ts, tested by Field.test.tsx
10
10
  *
@@ -30,6 +30,7 @@ import {mergeProps} from '../utils';
30
30
  import {useDevWarning} from '../hooks/useDevWarning';
31
31
  import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
32
32
  import {Text} from '../Text';
33
+ import {VisuallyHidden} from '../VisuallyHidden';
33
34
  import {themeProps} from '../utils/themeProps';
34
35
 
35
36
  const styles = stylex.create({
@@ -298,20 +299,28 @@ export function Field({
298
299
  return (
299
300
  <div
300
301
  ref={ref}
302
+ {...props}
301
303
  {...mergeProps(
302
304
  themeProps('field', {layout: 'horizontal-labels'}),
303
305
  stylex.props(styles.horizontalLabels, xstyle),
304
306
  className,
305
307
  style,
306
- )}
307
- {...props}>
308
+ )}>
308
309
  <div {...stylex.props(styles.horizontalLabelAlign)}>{labelNode}</div>
309
310
  <div {...stylex.props(styles.inputStatusWrapper)}>
310
- {description && (
311
- <Text type="supporting" display="block" id={resolvedDescriptionID}>
312
- {description}
313
- </Text>
314
- )}
311
+ {description &&
312
+ (isLabelHidden ? (
313
+ <VisuallyHidden id={resolvedDescriptionID}>
314
+ {description}
315
+ </VisuallyHidden>
316
+ ) : (
317
+ <Text
318
+ type="supporting"
319
+ display="block"
320
+ id={resolvedDescriptionID}>
321
+ {description}
322
+ </Text>
323
+ ))}
315
324
  {children}
316
325
  {statusNode}
317
326
  </div>
@@ -7,12 +7,13 @@ export const docs = {
7
7
  subComponentOf: 'Field',
8
8
  displayName: 'Field Label',
9
9
  isHiddenFromOverview: true,
10
- description: 'Standalone label component with optional/required indicators and tooltip support.',
10
+ description:
11
+ 'Standalone label group for one control or a caller-labelled group, with optional supporting text, optionality indicators, icon, and tooltip.',
11
12
  props: [
12
13
  {
13
14
  name: 'label',
14
15
  type: 'string',
15
- description: 'Label text.',
16
+ description: 'Label text, always rendered for accessibility.',
16
17
  required: true,
17
18
  },
18
19
  {
@@ -21,39 +22,92 @@ export const docs = {
21
22
  description: 'ID of the input this label is for.',
22
23
  required: true,
23
24
  },
25
+ {
26
+ name: 'labelID',
27
+ type: 'string',
28
+ description:
29
+ 'ID applied to the label element itself. Use it with `aria-labelledby` when `isGroupLabel` is true.',
30
+ },
31
+ {
32
+ name: 'isGroupLabel',
33
+ type: 'boolean',
34
+ description:
35
+ 'Render a span that can name a group instead of a native label for one control.',
36
+ default: 'false',
37
+ },
24
38
  {
25
39
  name: 'isLabelHidden',
26
40
  type: 'boolean',
27
- description: 'Visually hide the label.',
41
+ description:
42
+ 'Visually hide the label and description while keeping both available to assistive technology.',
28
43
  default: 'false',
29
44
  },
30
45
  {
31
46
  name: 'isDisabled',
32
47
  type: 'boolean',
33
- description: 'Whether the associated input is disabled.',
48
+ description:
49
+ 'Use disabled label presentation for the associated control.',
34
50
  default: 'false',
35
51
  },
36
52
  {
37
53
  name: 'isOptional',
38
54
  type: 'boolean',
39
- description: 'Show "Optional" indicator.',
55
+ description:
56
+ "Show the localized optional indicator unless it matches FormLayout's default optionality.",
40
57
  default: 'false',
41
58
  },
42
59
  {
43
60
  name: 'isRequired',
44
61
  type: 'boolean',
45
- description: 'Show "Required" indicator.',
62
+ description:
63
+ "Show the localized required indicator unless it matches FormLayout's default optionality.",
46
64
  default: 'false',
47
65
  },
48
66
  {
49
67
  name: 'labelIcon',
50
- type: 'IconType',
51
- description: 'Icon before the label text. See `astryx docs icons` for valid semantic names.',
68
+ type: 'ReactNode | IconType',
69
+ description:
70
+ 'Icon or rendered icon content before the label text. See `astryx docs icons` for semantic names.',
52
71
  },
53
72
  {
54
73
  name: 'labelTooltip',
55
74
  type: 'string',
56
- description: 'Tooltip text for info icon at end of label.',
75
+ description: 'Tooltip text for the info icon after the label.',
76
+ },
77
+ {
78
+ name: 'description',
79
+ type: 'ReactNode',
80
+ description:
81
+ "Supporting content displayed below the label and kept separate from the control's accessible name.",
82
+ },
83
+ {
84
+ name: 'descriptionID',
85
+ type: 'string',
86
+ description:
87
+ 'ID applied to the supporting description for caller-owned `aria-describedby` wiring.',
88
+ },
89
+ {
90
+ name: 'ref',
91
+ type: 'React.Ref<HTMLLabelElement>',
92
+ description:
93
+ 'Ref forwarded to the label element. Group mode currently renders a span.',
94
+ },
95
+ {
96
+ name: 'xstyle',
97
+ type: 'StyleXStyles',
98
+ description:
99
+ 'StyleX styles applied to the label element after its base and state styles.',
100
+ },
101
+ {
102
+ name: 'className',
103
+ type: 'string',
104
+ description: 'CSS class name appended to the label element.',
105
+ },
106
+ {
107
+ name: 'style',
108
+ type: 'React.CSSProperties',
109
+ description:
110
+ 'Inline styles applied to the label element after StyleX inline styles.',
57
111
  },
58
112
  ],
59
113
  };
@@ -62,12 +116,13 @@ export const docsZh = {
62
116
  name: 'FieldLabel',
63
117
  isHiddenFromOverview: true,
64
118
  displayName: 'Field Label',
65
- description: '独立的标签组件,支持可选/必填指示器和工具提示。',
119
+ description:
120
+ '独立标签组,可标记单个控件或调用方命名的控件组,并支持说明文本、可选/必填指示器、图标和工具提示。',
66
121
  props: [
67
122
  {
68
123
  name: 'label',
69
124
  type: 'string',
70
- description: '标签文本。',
125
+ description: '标签文本,始终为辅助技术保留。',
71
126
  required: true,
72
127
  },
73
128
  {
@@ -76,39 +131,82 @@ export const docsZh = {
76
131
  description: '此标签关联的输入框 ID。',
77
132
  required: true,
78
133
  },
134
+ {
135
+ name: 'labelID',
136
+ type: 'string',
137
+ description:
138
+ '应用到标签元素本身的 ID。isGroupLabel 为 true 时用于 aria-labelledby。',
139
+ },
140
+ {
141
+ name: 'isGroupLabel',
142
+ type: 'boolean',
143
+ description:
144
+ '渲染用于命名控件组的 span,而不是关联单个控件的原生 label。',
145
+ default: 'false',
146
+ },
79
147
  {
80
148
  name: 'isLabelHidden',
81
149
  type: 'boolean',
82
- description: '视觉隐藏标签。',
150
+ description: '视觉隐藏标签和说明,同时为辅助技术保留。',
83
151
  default: 'false',
84
152
  },
85
153
  {
86
154
  name: 'isDisabled',
87
155
  type: 'boolean',
88
- description: '关联的输入框是否禁用。',
156
+ description: '使用关联控件的禁用标签样式。',
89
157
  default: 'false',
90
158
  },
91
159
  {
92
160
  name: 'isOptional',
93
161
  type: 'boolean',
94
- description: '显示"Optional"指示器。',
162
+ description: '当其不匹配 FormLayout 默认值时显示本地化的可选指示器。',
95
163
  default: 'false',
96
164
  },
97
165
  {
98
166
  name: 'isRequired',
99
167
  type: 'boolean',
100
- description: '显示"Required"指示器。',
168
+ description: '当其不匹配 FormLayout 默认值时显示本地化的必填指示器。',
101
169
  default: 'false',
102
170
  },
103
171
  {
104
172
  name: 'labelIcon',
105
- type: 'IconType',
106
- description: '标签文本前的图标。',
173
+ type: 'ReactNode | IconType',
174
+ description: '标签文本前的图标或已渲染图标内容。',
107
175
  },
108
176
  {
109
177
  name: 'labelTooltip',
110
178
  type: 'string',
111
- description: '标签末尾信息图标的工具提示文本。',
179
+ description: '标签后信息图标的工具提示文本。',
180
+ },
181
+ {
182
+ name: 'description',
183
+ type: 'ReactNode',
184
+ description: '标签下方的说明内容,与控件的无障碍名称保持分离。',
185
+ },
186
+ {
187
+ name: 'descriptionID',
188
+ type: 'string',
189
+ description: '应用到说明内容的 ID,供调用方连接 aria-describedby。',
190
+ },
191
+ {
192
+ name: 'ref',
193
+ type: 'React.Ref<HTMLLabelElement>',
194
+ description: '转发到标签元素的 ref。控件组模式当前渲染 span。',
195
+ },
196
+ {
197
+ name: 'xstyle',
198
+ type: 'StyleXStyles',
199
+ description: '在基础和状态样式之后应用到标签元素的 StyleX 样式。',
200
+ },
201
+ {
202
+ name: 'className',
203
+ type: 'string',
204
+ description: '追加到标签元素的 CSS 类名。',
205
+ },
206
+ {
207
+ name: 'style',
208
+ type: 'React.CSSProperties',
209
+ description: '在 StyleX 内联样式之后应用到标签元素的内联样式。',
112
210
  },
113
211
  ],
114
212
  };
@@ -117,15 +215,24 @@ export const docsDense = {
117
215
  name: 'FieldLabel',
118
216
  isHiddenFromOverview: true,
119
217
  displayName: 'Field Label',
120
- description: 'Standalone label w/ optional/required indicators + tooltip support.',
218
+ description:
219
+ 'label group for one control or a caller-labelled group; supporting text, optionality, icon, tooltip',
121
220
  propDescriptions: {
122
221
  label: 'Label text.',
123
- inputID: 'ID of input this label is for.',
124
- isLabelHidden: 'Visually hide label.',
125
- isDisabled: 'Associated input disabled.',
126
- isOptional: 'Show "Optional" indicator.',
127
- isRequired: 'Show "Required" indicator.',
128
- labelIcon: 'Icon before label text.',
129
- labelTooltip: 'Tooltip text for info icon at end of label.',
222
+ inputID: 'ID of the associated control.',
223
+ labelID: 'ID of the label element for caller-owned aria-labelledby.',
224
+ isGroupLabel: 'Render a group-label span instead of a native label.',
225
+ isLabelHidden: 'Visually hide label and description.',
226
+ isDisabled: 'Use disabled label presentation.',
227
+ isOptional: 'Show localized optional exception indicator.',
228
+ isRequired: 'Show localized required exception indicator.',
229
+ labelIcon: 'Icon type or rendered icon content before label text.',
230
+ labelTooltip: 'Tooltip text for the info icon after the label.',
231
+ description: 'Supporting content outside the accessible name.',
232
+ descriptionID: 'ID for caller-owned aria-describedby wiring.',
233
+ ref: 'Ref forwarded to the label element; group mode renders a span.',
234
+ xstyle: 'StyleX styles for the label element.',
235
+ className: 'CSS class name for the label element.',
236
+ style: 'Inline styles for the label element.',
130
237
  },
131
238
  };
@@ -0,0 +1,172 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 7
4
+ kind: component
5
+ id: component:FieldLabel
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [public-api, behavior, layout, theming, accessibility]
13
+ verified_by:
14
+ [
15
+ packages/core/src/Field/FieldLabel.test.tsx,
16
+ packages/core/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts,
17
+ packages/core/src/theme/themingTargets.test.ts,
18
+ packages/core/src/docPropReferences.test.ts,
19
+ packages/core/src/docPropLiterals.test.ts,
20
+ ]
21
+ modules: []
22
+ families: []
23
+ design_specs: []
24
+ architecture:
25
+ [
26
+ architecture:public-component-api,
27
+ architecture:component-theming-surface,
28
+ architecture:component-test-sufficiency,
29
+ ]
30
+ contributing: []
31
+ system_specs: [spec:AST-020/FR1]
32
+ ---
33
+
34
+ # FieldLabel component contract
35
+
36
+ ## Contract at a glance
37
+
38
+ | Area | Contract |
39
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | Public contract | `FieldLabel` renders a visible or visually hidden label group for one control or a caller-labelled group, with optional description, optionality indicator, icon, tooltip, label-element BaseProps, and label-element ref. |
41
+ | Behavior | Single-control mode uses a native label; group mode uses a labelled span. A description adds a wrapper div, while caller props and ref stay on the inner label element. Description clicks forward only to one associated control. |
42
+ | End-user impact | People can identify a control or group, hear supporting text separately from its name, and activate eligible controls from the complete visible label area. The current tooltip remains unavailable to keyboard and screen-reader users. |
43
+ | Builder impact | Builders provide stable control and description IDs, select group mode when labelling a composite, and wire `aria-labelledby` or `aria-describedby` on the controlled surface. |
44
+ | Compatibility/readiness | This draft records released behavior and changes no API. Two gaps remain: group mode renders a span while public types promise label props/ref, and the anonymous tooltip icon exposes its content only to a pointer. |
45
+ | Review checks | Reject a lost native/group association, nested description in the accessible name, forwarded clicks from group or interactive description content, dropped label-element props/ref, hidden tooltip content, or undocumented public prop. |
46
+ | Governing rules | Established public interface; HTML label semantics; WCAG 2.2 SC 2.1.1; `architecture:public-component-api/INV1,INV5–INV9,INV12`; `architecture:component-theming-surface/INV3–INV7`; `spec:AST-020/FR1`. |
47
+
48
+ This table is a review projection; the body below is authoritative.
49
+
50
+ ## Intent
51
+
52
+ `FieldLabel` supplies the shared label, description, optionality, icon, and tooltip presentation used by Field and labeled controls such as CheckboxInput and Switch. It owns the label group only; the associated control or group owns value and interaction behavior.
53
+
54
+ ## Compatibility and migration
55
+
56
+ - Released default preserved: yes.
57
+ - Compatibility class: observational only; this draft changes no public prop, default, or behavior.
58
+ - Controlled/uncontrolled behavior: not applicable.
59
+ - Migration decision: none; OQ1 requires a separate public-type decision. The tooltip accessibility defect remains open.
60
+
61
+ ## Ownership boundary
62
+
63
+ **Owns**
64
+
65
+ - Native single-control and non-label group-label element selection.
66
+ - Visible and visually hidden label and description presentation.
67
+ - Optional/required exception indicators from FormLayout optionality context.
68
+ - Safe description-click forwarding for one associated control.
69
+ - The `field-label` theme target and composition of caller props, styles, and ref on the label element.
70
+ - Whether the optional tooltip trigger is reachable and named for every input modality and assistive-technology path.
71
+
72
+ **Does not own / non-goals**
73
+
74
+ - The associated control or group's value, disabled semantics, focus model, keyboard behavior, and ARIA wiring beyond the label's own IDs.
75
+ - The caller's layout around the label group.
76
+ - Tooltip surface behavior and icon rendering, which delegate to `component:Tooltip` and `component:Icon`; FieldLabel still owns whether its chosen trigger exposes that content.
77
+
78
+ ## Public concepts
79
+
80
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
81
+ | ---------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------- | -------------- | ---------------------- | ------------------------- | ------------------------------------------------------------------------- |
82
+ | label mode | single control; group | Chooses a native `<label htmlFor>` or a `<span>` referenced through `aria-labelledby`. | All uses. | single control | `component:FieldLabel` | released | Boolean-constrained; see OQ1 for root/ref typing. |
83
+ | label visibility | visible; visually hidden | Keeps label and description accessible while removing their layout space when hidden. | Both label modes. | visible | `component:FieldLabel` | released | Boolean-constrained. |
84
+ | optionality marker | none; optional; required | Shows the localized exception to FormLayout's form-level optionality default. | Visible and hidden labels. | none | `component:FieldLabel` | released | When both booleans are true, optional wins visibly. |
85
+ | leading icon | absent; icon type; rendered node | Adds visual content before the label text. | Both label modes. | absent | `component:FieldLabel` | released | Rendered through the shared icon slot. |
86
+ | explanatory tooltip | absent; localized string | Adds the shared info-icon anchor and tooltip surface after the label. | Both label modes. | absent | `component:FieldLabel` | released; conformance gap | The current anonymous, nonfocusable icon leaves the content pointer-only. |
87
+ | supporting description | absent; rendered node with optional explicit ID | Keeps supporting content separate from the accessible name and available for description. | Both label modes. | absent | `component:FieldLabel` | released | Missing ID leaves association to the caller. |
88
+ | description activation | forwarding; non-forwarding | For one control, noninteractive description clicks mirror native-label activation. | Single mode only. | forwarding | `component:FieldLabel` | released | Group mode and interactive descendants do not forward. |
89
+
90
+ ## Behavioral and layout contract
91
+
92
+ Draft requirements identify their basis so observed code is not mistaken for an intentional decision.
93
+
94
+ | ID | Candidate invariant | Basis | Draft review state |
95
+ | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------- |
96
+ | FR1 | Single-control mode renders `<label htmlFor={inputID}>`; group mode renders a non-label element with `labelID` for caller-owned `aria-labelledby`. | Established public interface; HTML and ARIA semantics | settled observation |
97
+ | FR2 | Visible and hidden labels retain the label text; hidden label and description boxes remain in the accessibility tree and leave no layout slot. | Established public interface | settled observation |
98
+ | FR3 | Descriptions remain siblings of the label so they do not enter the control's accessible name. Noninteractive description clicks forward to one eligible control; group labels and interactive descendants do not forward activation. | Established public interface; native label behavior | settled observation |
99
+ | FR4 | FormLayout optionality suppresses the matching visible marker; without a form default, the requested marker is localized and shown. When both flags are true, optional wins. | Established public interface and current tests | settled observation |
100
+ | FR5 | Label icons, tooltip anchors, required/optional text, and description content preserve source order. The label element uses a horizontal flex row; the optional label/description wrapper uses block flow. All component-owned spacing is logical or direction-neutral. | Established public interface and source observation | settled observation |
101
+ | FR6 | Accepted data, ARIA, DOM, class, style, event, and `xstyle` inputs compose on the label element without replacing the component-owned target or association. When a description renders, the outer wrapper is layout-only and caller props stay on the inner label element. | `architecture:public-component-api/INV5–INV7` | settled observation for single mode |
102
+ | FR7 | The public ref reaches the label element. Group mode currently renders a span while the public BaseProps/ref type is fixed to `HTMLLabelElement`. | `architecture:public-component-api/INV8,INV9`; current runtime/types | human decision (OQ1) |
103
+ | FR8 | The optional tooltip currently anchors to an anonymous, nonfocusable info icon, so its explanatory content is unavailable by keyboard and is not announced with the associated field. | WCAG 2.2 SC 2.1.1; `spec:AST-020/FR1`; current runtime | objective conformance gap |
104
+
105
+ ### Allowed variation
106
+
107
+ - **AV1 — Parent layout.** Field, CheckboxInput, Switch, and direct consumers may arrange the label group differently without changing its local semantics.
108
+ - **AV2 — Label content.** Icon, tooltip, optionality text, and description content may be absent independently.
109
+ - **AV3 — Control kind.** Description activation follows the associated native or custom control's supported click/focus behavior.
110
+
111
+ ### Representative states
112
+
113
+ | State | Required invariant | Allowed variation |
114
+ | --------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
115
+ | single label | Native label association reaches the one control. | Visible or visually hidden. |
116
+ | group label | Label element is not a literal label and is referenceable by `labelID`. | Caller owns group role and `aria-labelledby`. |
117
+ | description | Supporting content remains outside the label and may expose `descriptionID`. | Text or structured content. |
118
+ | optional or required | One localized exception marker follows label text. | Suppressed when it matches FormLayout's default. |
119
+ | disabled presentation | Label uses disabled text treatment and no pointer cursor. | Associated control owns actual disabled state. |
120
+ | icon and tooltip | Shared Icon and Tooltip render their owned paint; the current tooltip anchor remains nonfocusable and unnamed. | Either slot may be absent. |
121
+
122
+ ### Transformation and precedence order
123
+
124
+ - **ORD1 — Optionality.** Optional wins when both local booleans are true; otherwise the FormLayout default suppresses its matching marker.
125
+ - **ORD2 — Label-element composition.** Component target and base styles apply before caller `xstyle`, class, and inline style composition on the label element; association and label-element choice remain component-owned. An optional description adds a layout wrapper without retargeting those inputs.
126
+
127
+ ### Performance and resources
128
+
129
+ - **PR1 — No retained resource.** FieldLabel owns no global listener, observer, timer, or external store. Description activation reuses the shared input-container hook with local refs.
130
+
131
+ ## Accessibility contract
132
+
133
+ - **AR1 — Label association.** Single controls use native label association; grouped controls use a caller-wired labelled span and never a misleading literal label.
134
+ - **AR2 — Description separation.** Description content stays outside the label so it is not duplicated in the accessible name and exposes an ID for caller-owned `aria-describedby`.
135
+ - **AR3 — Hidden labels.** Visually hidden label and description content remain programmatically available.
136
+ - **AR4 — Activation boundary.** Description activation never hijacks interactive descendants and never forwards from a group label to one control.
137
+ - **AR5 — Tooltip reachability.** The current anonymous, nonfocusable tooltip anchor does not expose its explanatory content to keyboard or screen-reader users; this remains an objective conformance gap.
138
+
139
+ ## Design relationships
140
+
141
+ No current repository design record governs component-local visual tuning. The implementation uses shared semantic type, color, spacing, and disabled tokens; rendered evidence records the resulting states without turning them into policy.
142
+
143
+ ## Family and system relationships
144
+
145
+ - `architecture:public-component-api` owns exports, label-element prop composition, ref accuracy, and released API compatibility.
146
+ - `architecture:component-theming-surface` owns the anatomy-to-target disposition and target composition.
147
+ - `architecture:component-test-sufficiency` owns bounded, mutation-sensitive evidence for each critical established public seam.
148
+
149
+ ## Verification map
150
+
151
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
152
+ | -------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
153
+ | FR1–FR3, AR1–AR4 | `FieldLabel.test.tsx` plus exact-head axe and Chromium sensor receipts | single, group, description, hidden, interactive description child | Wrong label element, lost association, nested description, missing hidden text, or forwarded group/interactive click fails. | `audit:FieldLabel/accessibility` |
154
+ | FR4–FR5, ORD1 | focused DOM tests, i18n catalog checks, and exact-head light/dark/custom-theme frames | optional, required, both, form default, icon, tooltip, disabled | Wrong localized marker, order, visibility, or tokenized state is exposed. | `audit:FieldLabel/behavior` |
155
+ | FR6, ORD2 | BaseProps lint, focused composition tests, theming-target tests, and browser target inventory | class, style, xstyle, data, event, target; with and without description wrapper | Consumer input is dropped, retargeted to the wrapper, or the `field-label` target disappears. | `audit:FieldLabel/api` |
156
+ | FR7 | Public type review against the runtime single-label and group-span branches | single label; group span | Public ref/prop types claim a different label element than runtime. | `audit:FieldLabel/api` |
157
+ | FR8, AR5 | focused source/DOM review plus exact-head sensor receipt for the tooltip anchor | tooltip absent; tooltip present | The known anonymous, nonfocusable anchor disappears from the audit record before an accessible path replaces it. | `audit:FieldLabel/accessibility` |
158
+ | Documentation | docs typecheck, CLI readback, prop-literal/reference tests, and knowledge validation | every public prop and default | Docs omit or misstate a public input or default. | `audit:FieldLabel/docs` |
159
+ | RTL applicability | source/story inspection and verified-N/A registry | all component-owned states | A directional glyph, physical inline property, inline-side placement, order, scroll/drag, inline-side overlay placement, or horizontal arrow key makes N/A stale. | `audit:FieldLabel/i18n-rtl` |
160
+ | Responsive rendering | exact-head 320px Chromium sensor receipts | visible, hidden, description, group, optionality, icon, tooltip | The story overflows horizontally or a visible/hidden label loses its relationship. | `audit:FieldLabel/responsive` |
161
+
162
+ ## Decision log
163
+
164
+ No component-local decision is asserted. This draft records shipped behavior, retains the objective tooltip-accessibility gap, and leaves the polymorphic label-element/ref contract in OQ1.
165
+
166
+ ## Open questions
167
+
168
+ - **OQ1 — Should `isGroupLabel` expose a discriminated span-root BaseProps/ref contract, or should FieldLabel use one root element whose public type is stable in both modes?** (`human-api`)
169
+
170
+ ## Content boundary
171
+
172
+ This draft records verified shipped behavior, the objective tooltip-accessibility gap, and the released label-element type/runtime mismatch. It does not choose a ref migration, add a theme target, duplicate consumer prop tables, or store audit scores and run evidence.