@astryxdesign/core 0.4.5-canary.013e728 → 0.4.5-canary.0dfce02

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 (309) hide show
  1. package/dist/Avatar/Avatar.d.ts +10 -5
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +139 -30
  4. package/dist/Avatar/AvatarStatusDot.d.ts +2 -1
  5. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  6. package/dist/Avatar/AvatarStatusDot.js +30 -4
  7. package/dist/Avatar/AvatarStatusLabelContext.d.ts +39 -0
  8. package/dist/Avatar/AvatarStatusLabelContext.d.ts.map +1 -0
  9. package/dist/Avatar/AvatarStatusLabelContext.js +41 -0
  10. package/dist/AvatarGroup/AvatarGroup.d.ts +3 -1
  11. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  13. package/dist/AvatarGroup/AvatarGroupOverflow.js +14 -8
  14. package/dist/Blockquote/Blockquote.d.ts +4 -3
  15. package/dist/Blockquote/Blockquote.d.ts.map +1 -1
  16. package/dist/Blockquote/Blockquote.js +9 -10
  17. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  18. package/dist/BottomSheet/BottomSheetPanel.js +13 -4
  19. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  20. package/dist/Breadcrumbs/BreadcrumbItem.js +47 -34
  21. package/dist/Button/Button.d.ts.map +1 -1
  22. package/dist/Button/Button.js +2 -2
  23. package/dist/Calendar/styles.d.ts +2 -2
  24. package/dist/Calendar/styles.d.ts.map +1 -1
  25. package/dist/Calendar/styles.js +2 -2
  26. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  27. package/dist/Chat/ChatComposer.js +1 -1
  28. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  29. package/dist/Chat/ChatComposerDrawer.js +2 -2
  30. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  31. package/dist/Chat/ChatToolCalls.js +2 -2
  32. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  33. package/dist/Chat/useTriggerMenu.js +2 -2
  34. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  35. package/dist/CheckboxInput/CheckboxInput.js +2 -2
  36. package/dist/Citation/Citation.d.ts.map +1 -1
  37. package/dist/Citation/Citation.js +2 -2
  38. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  39. package/dist/ClickableCard/ClickableCard.js +2 -2
  40. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  41. package/dist/CodeBlock/CodeBlock.js +1 -1
  42. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  43. package/dist/Collapsible/Collapsible.js +2 -2
  44. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  45. package/dist/CommandPalette/CommandPaletteItem.js +2 -2
  46. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  47. package/dist/ComplexSelector/ComplexSelector.js +4 -4
  48. package/dist/DateInput/DateInput.d.ts +31 -2
  49. package/dist/DateInput/DateInput.d.ts.map +1 -1
  50. package/dist/DateInput/DateInput.js +79 -14
  51. package/dist/DateInput/MonthScroller.d.ts +35 -0
  52. package/dist/DateInput/MonthScroller.d.ts.map +1 -0
  53. package/dist/DateInput/MonthScroller.js +658 -0
  54. package/dist/DateInput/MonthYearWheels.d.ts +24 -0
  55. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
  56. package/dist/DateInput/MonthYearWheels.js +114 -0
  57. package/dist/DateInput/TouchDateField.d.ts +10 -0
  58. package/dist/DateInput/TouchDateField.d.ts.map +1 -0
  59. package/dist/DateInput/TouchDateField.js +810 -0
  60. package/dist/DateInput/Wheel.d.ts +41 -0
  61. package/dist/DateInput/Wheel.d.ts.map +1 -0
  62. package/dist/DateInput/Wheel.js +315 -0
  63. package/dist/DateInput/monthGeometry.d.ts +65 -0
  64. package/dist/DateInput/monthGeometry.d.ts.map +1 -0
  65. package/dist/DateInput/monthGeometry.js +98 -0
  66. package/dist/DateInput/tokens.stylex.d.ts +63 -0
  67. package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
  68. package/dist/DateInput/tokens.stylex.js +74 -0
  69. package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
  70. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
  71. package/dist/DateInput/useOwnScrollGesture.js +202 -0
  72. package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
  73. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
  74. package/dist/DateInput/usePointerDragScroll.js +216 -0
  75. package/dist/DateInput/useScrollSettle.d.ts +26 -0
  76. package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
  77. package/dist/DateInput/useScrollSettle.js +149 -0
  78. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  79. package/dist/DateRangeInput/DateRangeInput.js +8 -8
  80. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  81. package/dist/DateTimeInput/DateTimeInput.js +6 -6
  82. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  83. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +2 -2
  84. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  85. package/dist/DropdownMenu/DropdownMenuItem.js +2 -2
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +2 -2
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -2
  90. package/dist/Field/FieldLabel.d.ts.map +1 -1
  91. package/dist/Field/FieldLabel.js +4 -4
  92. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  93. package/dist/Field/inputStyles.stylex.js +1 -1
  94. package/dist/FileInput/FileInput.d.ts.map +1 -1
  95. package/dist/FileInput/FileInput.js +4 -4
  96. package/dist/InputGroup/InputGroup.js +1 -1
  97. package/dist/Item/Item.d.ts.map +1 -1
  98. package/dist/Item/Item.js +10 -10
  99. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  100. package/dist/Lightbox/Lightbox.js +3 -3
  101. package/dist/Link/Link.d.ts.map +1 -1
  102. package/dist/Link/Link.js +2 -2
  103. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  104. package/dist/MetadataList/MetadataList.js +1 -1
  105. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  106. package/dist/MultiSelector/MultiSelector.js +6 -6
  107. package/dist/NavItem/navItemStyles.stylex.d.ts +2 -2
  108. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  109. package/dist/NavItem/navItemStyles.stylex.js +2 -2
  110. package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
  111. package/dist/NavMenu/NavHeadingMenuItem.js +2 -2
  112. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  113. package/dist/NumberInput/NumberInput.js +6 -6
  114. package/dist/Outline/Outline.d.ts.map +1 -1
  115. package/dist/Outline/Outline.js +1 -1
  116. package/dist/Pagination/Pagination.d.ts.map +1 -1
  117. package/dist/Pagination/Pagination.js +2 -2
  118. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  119. package/dist/RadioList/RadioListItem.js +2 -2
  120. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  121. package/dist/Resizable/ResizeHandle.js +4 -4
  122. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  123. package/dist/SegmentedControl/SegmentedControlItem.js +1 -1
  124. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  125. package/dist/SelectableCard/SelectableCard.js +2 -2
  126. package/dist/Selector/Selector.d.ts.map +1 -1
  127. package/dist/Selector/Selector.js +6 -6
  128. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  129. package/dist/SideNav/SideNavHeading.js +3 -3
  130. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  131. package/dist/SideNav/SideNavItem.js +2 -2
  132. package/dist/Slider/Slider.d.ts.map +1 -1
  133. package/dist/Slider/Slider.js +6 -6
  134. package/dist/Switch/Switch.d.ts.map +1 -1
  135. package/dist/Switch/Switch.js +2 -2
  136. package/dist/TabList/Tab.d.ts.map +1 -1
  137. package/dist/TabList/Tab.js +1 -1
  138. package/dist/TabList/TabMenu.d.ts.map +1 -1
  139. package/dist/TabList/TabMenu.js +2 -2
  140. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  141. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  142. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  143. package/dist/Table/plugins/filtering/useTableFiltering.js +2 -2
  144. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  145. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +2 -2
  146. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  147. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -2
  148. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  149. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  150. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  151. package/dist/Table/plugins/tree/useTableTreeData.js +3 -3
  152. package/dist/TextArea/TextArea.js +1 -1
  153. package/dist/TextInput/TextInput.js +1 -1
  154. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  155. package/dist/Thumbnail/Thumbnail.js +2 -2
  156. package/dist/TimeInput/TimeInput.js +2 -2
  157. package/dist/Toast/Toast.d.ts +4 -3
  158. package/dist/Toast/Toast.d.ts.map +1 -1
  159. package/dist/Toast/Toast.js +10 -7
  160. package/dist/Token/Token.d.ts.map +1 -1
  161. package/dist/Token/Token.js +5 -5
  162. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  163. package/dist/Tokenizer/Tokenizer.js +1 -1
  164. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  165. package/dist/TopNav/TopNavHeading.js +4 -4
  166. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  167. package/dist/TopNav/TopNavItem.js +1 -1
  168. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  169. package/dist/TopNav/TopNavMegaMenu.js +1 -1
  170. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  171. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  172. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  173. package/dist/TopNav/TopNavMenu.js +2 -2
  174. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  175. package/dist/TreeList/TreeListItem.js +6 -6
  176. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  177. package/dist/Typeahead/BaseTypeahead.js +2 -2
  178. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  179. package/dist/Typeahead/Typeahead.js +1 -1
  180. package/dist/astryx.css +39 -2
  181. package/dist/hooks/useAutoMediaMode.d.ts +31 -0
  182. package/dist/hooks/useAutoMediaMode.d.ts.map +1 -0
  183. package/dist/hooks/useAutoMediaMode.js +201 -0
  184. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  185. package/dist/hooks/useInputStatusIcon.js +1 -1
  186. package/dist/theme/MediaTheme.d.ts +25 -5
  187. package/dist/theme/MediaTheme.d.ts.map +1 -1
  188. package/dist/theme/MediaTheme.js +31 -6
  189. package/dist/theme/index.d.ts +1 -1
  190. package/dist/theme/index.d.ts.map +1 -1
  191. package/dist/theme/syntax/index.d.ts +6 -0
  192. package/dist/theme/syntax/index.d.ts.map +1 -1
  193. package/dist/theme/syntax/index.js +7 -2
  194. package/dist/utils/index.d.ts +1 -1
  195. package/dist/utils/index.d.ts.map +1 -1
  196. package/dist/utils/index.js +1 -1
  197. package/dist/utils/plainDate.d.ts +2 -0
  198. package/dist/utils/plainDate.d.ts.map +1 -1
  199. package/dist/utils/plainDate.js +16 -0
  200. package/locales/en.json +24 -0
  201. package/locales/pseudo.json +18 -0
  202. package/package.json +2 -2
  203. package/src/Avatar/Avatar.doc.mjs +10 -6
  204. package/src/Avatar/Avatar.test.tsx +219 -1
  205. package/src/Avatar/Avatar.tsx +179 -37
  206. package/src/Avatar/AvatarStatusDot.tsx +30 -4
  207. package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
  208. package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
  209. package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
  210. package/src/AvatarGroup/AvatarGroup.tsx +3 -1
  211. package/src/AvatarGroup/AvatarGroupOverflow.tsx +30 -7
  212. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  213. package/src/Blockquote/Blockquote.test.tsx +34 -3
  214. package/src/Blockquote/Blockquote.tsx +11 -9
  215. package/src/BottomSheet/BottomSheetPanel.test.tsx +50 -0
  216. package/src/BottomSheet/BottomSheetPanel.tsx +30 -1
  217. package/src/Breadcrumbs/BreadcrumbItem.tsx +35 -22
  218. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
  219. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
  220. package/src/Button/Button.tsx +5 -2
  221. package/src/Calendar/styles.ts +5 -2
  222. package/src/Chat/ChatComposer.tsx +4 -1
  223. package/src/Chat/ChatComposerDrawer.tsx +4 -1
  224. package/src/Chat/ChatToolCalls.tsx +12 -3
  225. package/src/Chat/useTriggerMenu.tsx +4 -1
  226. package/src/CheckboxInput/CheckboxInput.tsx +5 -2
  227. package/src/Citation/Citation.test.tsx +6 -1
  228. package/src/Citation/Citation.tsx +8 -2
  229. package/src/ClickableCard/ClickableCard.tsx +5 -2
  230. package/src/CodeBlock/CodeBlock.tsx +4 -1
  231. package/src/Collapsible/Collapsible.tsx +5 -2
  232. package/src/CommandPalette/CommandPaletteItem.tsx +5 -2
  233. package/src/ComplexSelector/ComplexSelector.tsx +9 -3
  234. package/src/DateInput/DateInput.tsx +83 -13
  235. package/src/DateInput/DateInputTouch.test.tsx +2582 -0
  236. package/src/DateInput/MonthScroller.tsx +878 -0
  237. package/src/DateInput/MonthYearWheels.tsx +147 -0
  238. package/src/DateInput/TouchDateField.tsx +1223 -0
  239. package/src/DateInput/Wheel.tsx +473 -0
  240. package/src/DateInput/monthGeometry.ts +118 -0
  241. package/src/DateInput/tokens.stylex.ts +99 -0
  242. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  243. package/src/DateInput/usePointerDragScroll.ts +220 -0
  244. package/src/DateInput/useScrollSettle.ts +164 -0
  245. package/src/DateRangeInput/DateRangeInput.tsx +15 -6
  246. package/src/DateTimeInput/DateTimeInput.tsx +6 -3
  247. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
  248. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
  249. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
  250. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
  251. package/src/Field/FieldLabel.tsx +9 -3
  252. package/src/Field/inputStyles.stylex.ts +1 -1
  253. package/src/FileInput/FileInput.tsx +10 -4
  254. package/src/InputGroup/InputGroup.tsx +1 -1
  255. package/src/Item/Item.tsx +13 -4
  256. package/src/Lightbox/Lightbox.tsx +9 -2
  257. package/src/Link/Link.tsx +5 -2
  258. package/src/MetadataList/MetadataList.tsx +4 -1
  259. package/src/MultiSelector/MultiSelector.tsx +21 -6
  260. package/src/NavItem/navItemStyles.stylex.ts +5 -2
  261. package/src/NavMenu/NavHeadingMenuItem.tsx +5 -2
  262. package/src/NumberInput/NumberInput.tsx +6 -3
  263. package/src/Outline/Outline.tsx +4 -1
  264. package/src/Pagination/Pagination.tsx +5 -2
  265. package/src/RadioList/RadioListItem.tsx +5 -2
  266. package/src/Resizable/ResizeHandle.tsx +16 -4
  267. package/src/SegmentedControl/SegmentedControlItem.tsx +4 -1
  268. package/src/SelectableCard/SelectableCard.tsx +5 -2
  269. package/src/Selector/Selector.tsx +17 -5
  270. package/src/SideNav/SideNavHeading.tsx +12 -3
  271. package/src/SideNav/SideNavItem.tsx +8 -2
  272. package/src/Slider/Slider.tsx +14 -5
  273. package/src/Switch/Switch.tsx +5 -2
  274. package/src/TabList/Tab.tsx +4 -1
  275. package/src/TabList/TabMenu.tsx +8 -2
  276. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +4 -1
  277. package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
  278. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +8 -2
  279. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +4 -1
  280. package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
  281. package/src/Table/plugins/tree/useTableTreeData.tsx +8 -2
  282. package/src/TextArea/TextArea.tsx +1 -1
  283. package/src/TextInput/TextInput.tsx +1 -1
  284. package/src/Thumbnail/Thumbnail.tsx +8 -2
  285. package/src/TimeInput/TimeInput.tsx +1 -1
  286. package/src/Toast/Toast.tsx +9 -7
  287. package/src/Token/Token.tsx +13 -4
  288. package/src/Tokenizer/Tokenizer.tsx +4 -1
  289. package/src/TopNav/TopNavHeading.tsx +12 -3
  290. package/src/TopNav/TopNavItem.tsx +4 -1
  291. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  292. package/src/TopNav/TopNavMegaMenuItem.tsx +4 -1
  293. package/src/TopNav/TopNavMenu.tsx +8 -2
  294. package/src/TreeList/TreeListItem.tsx +21 -6
  295. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  296. package/src/Typeahead/Typeahead.tsx +4 -1
  297. package/src/hooks/useAutoMediaMode.test.ts +109 -0
  298. package/src/hooks/useAutoMediaMode.ts +233 -0
  299. package/src/hooks/useInputStatusIcon.tsx +4 -1
  300. package/src/reset.css +9 -2
  301. package/src/reset.test.ts +22 -0
  302. package/src/theme/MediaTheme.doc.mjs +23 -4
  303. package/src/theme/MediaTheme.dom.test.tsx +97 -0
  304. package/src/theme/MediaTheme.tsx +42 -7
  305. package/src/theme/index.ts +1 -1
  306. package/src/theme/syntax/index.ts +6 -2
  307. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  308. package/src/utils/index.ts +2 -0
  309. package/src/utils/plainDate.ts +16 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file Avatar.tsx
3
- * @input Uses React, HTMLAttributes, ReactNode, useState; useTooltip
3
+ * @input Uses React, HTMLAttributes, ReactNode, useState, useRef; useTooltip
4
4
  * (Tooltip hook) for the optional name-on-hover tooltip; useTranslator (i18n)
5
5
  * @output Exports Avatar component, AvatarProps, AvatarSize types
6
6
  * @position Core implementation; consumed by index.ts
@@ -8,6 +8,7 @@
8
8
  * SYNC: When modified, update these files to stay in sync:
9
9
  * - /packages/core/src/Avatar/Avatar.doc.mjs (props table, features, implementation notes)
10
10
  * - /packages/core/src/Avatar/index.ts (exports if types change)
11
+ * - /packages/core/src/Avatar/AvatarStatusLabelContext.ts (the status label ref)
11
12
  * - /apps/storybook/stories/Avatar.stories.tsx (storybook stories)
12
13
  * - /packages/cli/assets/templates/blocks/components/Avatar/ (showcase blocks)
13
14
  *
@@ -76,10 +77,14 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
76
77
  * Content displayed in the corner of the avatar.
77
78
  * Typically used for status indicators or badges.
78
79
  *
79
- * When the element carries a string `label` prop (as `AvatarStatusDot`
80
- * does), the label is composed into the avatar's accessible name
81
- * (e.g. "Jane Doe, Online") so assistive tech can reach the status —
82
- * the `role="img"` root prunes descendant semantics (WCAG 4.1.2).
80
+ * `AvatarStatusDot` reports its own `label` to the avatar, which composes
81
+ * it into the avatar's accessible name (e.g. "Jane Doe, Online") so
82
+ * assistive tech can reach the status: the `role="img"` root prunes
83
+ * descendant semantics (WCAG 4.1.2). Reporting goes through context, so it
84
+ * still works when the dot sits inside a wrapper component of your own. A
85
+ * custom status element that is not an `AvatarStatusDot` names itself the
86
+ * same way, by rendering an `AvatarStatusDot` or by carrying a string
87
+ * `label` prop.
83
88
  */
84
89
  status?: ReactNode;
85
90
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/Avatar/Avatar.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAoC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiB5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AA4BrD;;;;;;GAMG;AACH,KAAK,eAAe,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD;;GAEG;AACH,KAAK,iBAAiB,GACpB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAgBpD;AA6HD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CAChD;AA0DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,EACrB,GAAG,EACH,aAAa,EAAE,MAAM,EACrB,WAAW,EACX,IAAI,EACJ,IAAW,EACX,GAAG,EACH,MAAM,EACN,OAAc,EACd,IAAI,EACJ,EAAE,EACF,MAAM,EACN,GAAG,EACH,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,+BA+Pb;yBAlRe,MAAM"}
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/Avatar/Avatar.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAA4C,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAqB5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AA4BrD;;;;;;GAMG;AACH,KAAK,eAAe,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD;;GAEG;AACH,KAAK,iBAAiB,GACpB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAgBpD;AAgID,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CAChD;AAwHD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,EACrB,GAAG,EACH,aAAa,EAAE,MAAM,EACrB,WAAW,EACX,IAAI,EACJ,IAAW,EACX,GAAG,EACH,MAAM,EACN,OAAc,EACd,IAAI,EACJ,EAAE,EACF,MAAM,EACN,GAAG,EACH,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,+BAmUb;yBAtVe,MAAM"}
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Avatar.tsx
7
- * @input Uses React, HTMLAttributes, ReactNode, useState; useTooltip
7
+ * @input Uses React, HTMLAttributes, ReactNode, useState, useRef; useTooltip
8
8
  * (Tooltip hook) for the optional name-on-hover tooltip; useTranslator (i18n)
9
9
  * @output Exports Avatar component, AvatarProps, AvatarSize types
10
10
  * @position Core implementation; consumed by index.ts
@@ -12,16 +12,18 @@
12
12
  * SYNC: When modified, update these files to stay in sync:
13
13
  * - /packages/core/src/Avatar/Avatar.doc.mjs (props table, features, implementation notes)
14
14
  * - /packages/core/src/Avatar/index.ts (exports if types change)
15
+ * - /packages/core/src/Avatar/AvatarStatusLabelContext.ts (the status label ref)
15
16
  * - /apps/storybook/stories/Avatar.stories.tsx (storybook stories)
16
17
  * - /packages/cli/assets/templates/blocks/components/Avatar/ (showcase blocks)
17
18
  *
18
19
  * Last synced props: alt, fallbackSrc, name, size, src, status, href, as, target, rel, onClick
19
20
  */
20
- import { isValidElement, useMemo, useState } from 'react';
21
+ import { isValidElement, useMemo, useRef, useState } from 'react';
21
22
  import * as stylex from '@stylexjs/stylex';
22
23
  import "../theme/tokens.stylex.js";
23
24
  import { colorVars, typographyVars, fontWeightVars, radiusVars } from "../theme/tokens.stylex.js";
24
25
  import { AvatarSizeContext } from "./AvatarSizeContext.js";
26
+ import { AvatarStatusLabelContext } from "./AvatarStatusLabelContext.js";
25
27
  import { useAvatarGroup } from "../AvatarGroup/AvatarGroupContext.js";
26
28
  import { mergeProps, mergeRefs } from "../utils/index.js";
27
29
  import { themeProps } from "../utils/themeProps.js";
@@ -134,7 +136,7 @@ const styles = {
134
136
  kMwMTN: "x1heor9g",
135
137
  kVVagm: "xln7xf2",
136
138
  kybGjl: "x1hl2dhg",
137
- kkrTdU: "x1ypdohk",
139
+ kkrTdU: "x1ypdohk x16khyan",
138
140
  kaIpWk: "xjspbzw",
139
141
  $$css: true
140
142
  }
@@ -217,13 +219,13 @@ function getInitials(name) {
217
219
 
218
220
  /**
219
221
  * Reads the accessible status label off the `status` element, when it
220
- * exposes one. `AvatarStatusDot`'s `label` prop is the canonical source,
221
- * but any custom status element with a string `label` prop participates.
222
+ * exposes one.
222
223
  *
223
- * The avatar root is `role="img"`, which prunes ALL descendant semantics
224
- * from the accessibility tree — a label inside the status subtree is never
225
- * announced on its own. Composing it into the avatar's own accessible name
226
- * is the only way the status reaches assistive tech (WCAG 4.1.2).
224
+ * Only sees a literal `label` prop on the element the consumer passes, so a
225
+ * consumer's own wrapper around the dot hides it — that case comes in through
226
+ * `AvatarStatusLabelContext` instead. Kept because it answers the first
227
+ * render, before any report has landed, and because it tracks a label that
228
+ * changes on an element that does not report at all.
227
229
  */
228
230
  function getStatusLabel(status) {
229
231
  if (! /*#__PURE__*/isValidElement(status)) {
@@ -235,6 +237,60 @@ function getStatusLabel(status) {
235
237
  return typeof label === 'string' && label !== '' ? label : undefined;
236
238
  }
237
239
 
240
+ /**
241
+ * The string when it carries something, `undefined` when it is absent or
242
+ * blank. An empty accessible name is meaningless, so a blank reads as
243
+ * absent, the way Icon treats `label=""`.
244
+ */
245
+ function meaningful(value) {
246
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
247
+ }
248
+
249
+ /**
250
+ * Derives the root's ARIA attributes from the resolved accessible name,
251
+ * following Icon's `getIconA11yProps`.
252
+ *
253
+ * - A name → meaningful image: `role="img"` + `aria-label`.
254
+ * - No name → decorative: `role="presentation"` + `aria-hidden="true"`, rather
255
+ * than announcing a meaningless generic "Avatar" (obs-9).
256
+ *
257
+ * The result is spread BEFORE `{...props}` on every root so an explicit
258
+ * `aria-label` / `role` / `aria-hidden` from the consumer always wins.
259
+ */
260
+ function getAvatarA11yProps(accessibleName) {
261
+ return accessibleName != null && accessibleName !== '' ? {
262
+ role: 'img',
263
+ 'aria-label': accessibleName
264
+ } : {
265
+ role: 'presentation',
266
+ 'aria-hidden': 'true'
267
+ };
268
+ }
269
+
270
+ /**
271
+ * The avatar's accessible name: its own name, the status label, or the two
272
+ * composed ("Jane Doe, Online"). Used both by render and by the commit-phase
273
+ * update, so the two can never compose differently.
274
+ */
275
+ function composeAccessibleName(t, nameLabel, statusLabel) {
276
+ if (nameLabel && statusLabel) {
277
+ return t('@astryx.avatar.nameWithStatus', {
278
+ name: nameLabel,
279
+ status: statusLabel
280
+ });
281
+ }
282
+ return nameLabel || statusLabel;
283
+ }
284
+
285
+ /** Sets an attribute, or removes it when the value is absent. */
286
+ function setAttributeOrRemove(element, name, value) {
287
+ if (value == null) {
288
+ element.removeAttribute(name);
289
+ } else {
290
+ element.setAttribute(name, value);
291
+ }
292
+ }
293
+
238
294
  /**
239
295
  * Default person icon SVG for when no image or name is provided
240
296
  */
@@ -299,8 +355,8 @@ export function Avatar({
299
355
  // A whitespace-only string carries no identity. Without this it produces no
300
356
  // initials (getInitials trims to nothing) and no default icon (a space is
301
357
  // truthy), leaving an empty plate behind a blank accessible name.
302
- const meaningfulName = name?.trim() ? name : undefined;
303
- const meaningfulAlt = alt?.trim() ? alt : undefined;
358
+ const meaningfulName = meaningful(name);
359
+ const meaningfulAlt = meaningful(alt);
304
360
  const showInitials = !showImage && !showFallbackImage && meaningfulName;
305
361
  const showIcon = !showImage && !showFallbackImage && !meaningfulName;
306
362
 
@@ -314,12 +370,17 @@ export function Avatar({
314
370
  // generic "Avatar" (obs-9).
315
371
  const t = useTranslator();
316
372
  const nameLabel = meaningfulAlt || meaningfulName;
317
- const statusLabel = getStatusLabel(status);
318
- const accessibleName = nameLabel && statusLabel ? t('@astryx.avatar.nameWithStatus', {
319
- name: nameLabel,
320
- status: statusLabel
321
- }) : nameLabel || statusLabel;
322
- const isDecorative = !accessibleName;
373
+ // This render can only see a label introspected off a directly-passed
374
+ // element. A status inside a consumer's own wrapper reports through the ref
375
+ // below, in the commit phase, and lands on the root element from there.
376
+ const accessibleName = composeAccessibleName(t, nameLabel, getStatusLabel(status));
377
+ const a11yProps = getAvatarA11yProps(accessibleName);
378
+ // An `<a>`/`<button>` root carries its own role and must not be hidden, so
379
+ // only the name transfers. Spread before `{...props}` for the same
380
+ // consumer-wins precedence as the static root.
381
+ const a11yLabelProps = accessibleName ? {
382
+ 'aria-label': accessibleName
383
+ } : undefined;
323
384
  const avatarGroup = useAvatarGroup();
324
385
  const resolvedSize = avatarGroup?.size ?? size;
325
386
  const numericSize = useMemo(() => resolveSize(resolvedSize), [resolvedSize]);
@@ -351,8 +412,8 @@ export function Avatar({
351
412
  isEnabled: showTooltip
352
413
  });
353
414
  // The tooltip ref attaches to whichever root element renders (static or
354
- // interactive), so the tooltip works for link/button avatars too.
355
- const rootRef = mergeRefs(ref, showTooltip ? tooltipHook.ref : undefined);
415
+ // interactive), so the tooltip works for link/button avatars too. The root
416
+ // ref itself is assembled below, once the element swap is resolved.
356
417
  const describedByProp = showTooltip && isCustomTooltip ? {
357
418
  'aria-describedby': [props['aria-describedby'], tooltipHook.describedBy].filter(Boolean).join(' ') || undefined
358
419
  } : null;
@@ -365,10 +426,57 @@ export function Avatar({
365
426
  const isInteractive = renderAsLink || renderAsButton;
366
427
  const LinkComponent = useLinkComponent(as);
367
428
 
368
- // An interactive control with no accessible name is an unacceptable control
369
- // name. `useDevWarning` is the shared guardrail: it warns once per mount
370
- // rather than on every render, and compiles out of production builds.
371
- useDevWarning('Avatar', 'an interactive avatar (with `href` or `onClick`) needs a meaningful ' + 'accessible name. Pass `alt` or `name`.', isInteractive && !accessibleName);
429
+ // An interactive control needs an identity of its own. A status label is not
430
+ // one: it composes into the accessible name, so `<Avatar href status={<dot
431
+ // label="Online" />} />` resolves a name that reads as legitimate and says
432
+ // nothing about who the link points at. Only `alt`/`name` count, or a
433
+ // consumer's own `aria-label`/`aria-labelledby` escape hatch, which wins
434
+ // over the derived props below.
435
+ const consumerName = meaningful(props['aria-label']) ?? meaningful(props['aria-labelledby']);
436
+ useDevWarning('Avatar', 'an interactive avatar (with `href` or `onClick`) needs a meaningful ' + 'accessible name. Pass `alt` or `name`.', isInteractive && !nameLabel && consumerName == null);
437
+
438
+ // A status inside a consumer's own wrapper is invisible to render: the dot
439
+ // writes its label into this ref from its own callback ref, in the commit
440
+ // phase. A ref write cannot re-render, so the composed name is written
441
+ // straight onto the root element instead — still before paint, and with no
442
+ // second render.
443
+ const statusLabelRef = useRef({
444
+ label: undefined,
445
+ update: null
446
+ });
447
+ // A fresh callback ref every render, so React reattaches it on every commit
448
+ // and the name is recomposed after React has written this render's props.
449
+ // `update` covers the other direction: a label that changes while the avatar
450
+ // itself does not re-render.
451
+ const nameRef = element => {
452
+ if (element == null) {
453
+ return;
454
+ }
455
+ const target = statusLabelRef.current;
456
+ target.update = () => {
457
+ const composed = composeAccessibleName(t, nameLabel, meaningful(target.label) ?? getStatusLabel(status));
458
+ // A consumer's own ARIA wins here exactly as it does in render, where
459
+ // the derived props spread before `{...props}`.
460
+ if (props['aria-label'] == null) {
461
+ setAttributeOrRemove(element, 'aria-label', composed);
462
+ }
463
+ // An `<a>`/`<button>` root carries its own role and must not be hidden,
464
+ // so only the name transfers there.
465
+ if (!isInteractive) {
466
+ if (props.role == null) {
467
+ element.setAttribute('role', composed ? 'img' : 'presentation');
468
+ }
469
+ if (props['aria-hidden'] == null) {
470
+ setAttributeOrRemove(element, 'aria-hidden', composed ? undefined : 'true');
471
+ }
472
+ }
473
+ };
474
+ target.update();
475
+ return () => {
476
+ target.update = null;
477
+ };
478
+ };
479
+ const rootRef = mergeRefs(ref, showTooltip ? tooltipHook.ref : undefined, nameRef);
372
480
 
373
481
  // The inner visuals are identical across the static and interactive variants.
374
482
  const visualContent = /*#__PURE__*/_jsxs(_Fragment, {
@@ -430,13 +538,13 @@ export function Avatar({
430
538
  // roving focus (which selects on `[data-avatar-item]`, not a tag/role) picks
431
539
  // it up while ignoring nested buttons in a custom status/badge slot.
432
540
  rootElement = /*#__PURE__*/_jsx(LinkComponent, {
541
+ ...a11yLabelProps,
433
542
  ...interactivePassthrough,
434
543
  ...describedByProp,
435
544
  ref: rootRef,
436
545
  href: href,
437
546
  target: target,
438
547
  rel: rel,
439
- "aria-label": accessibleName,
440
548
  "data-avatar-item": "",
441
549
  "data-testid": testId,
442
550
  onClick: onClick,
@@ -445,11 +553,11 @@ export function Avatar({
445
553
  });
446
554
  } else if (renderAsButton) {
447
555
  rootElement = /*#__PURE__*/_jsx("button", {
556
+ ...a11yLabelProps,
448
557
  ...interactivePassthrough,
449
558
  ...describedByProp,
450
559
  ref: rootRef,
451
560
  type: "button",
452
- "aria-label": accessibleName,
453
561
  "data-avatar-item": "",
454
562
  "data-testid": testId,
455
563
  onClick: onClick,
@@ -458,11 +566,9 @@ export function Avatar({
458
566
  });
459
567
  } else {
460
568
  rootElement = /*#__PURE__*/_jsx("div", {
569
+ ...a11yProps,
461
570
  ...props,
462
- ref: rootRef,
463
- role: isDecorative ? 'presentation' : 'img',
464
- "aria-label": isDecorative ? undefined : accessibleName,
465
- "aria-hidden": isDecorative || undefined
571
+ ref: rootRef
466
572
  // The root is a div[role="img"], not natively focusable. When a name
467
573
  // tooltip is active, add a tab stop so keyboard users can reveal it
468
574
  // (WCAG 1.4.13 / 2.1.1) — matching Timestamp/Button. Suppressed inside
@@ -477,7 +583,10 @@ export function Avatar({
477
583
  }
478
584
  const avatarElement = /*#__PURE__*/_jsx(AvatarSizeContext, {
479
585
  value: numericSize,
480
- children: rootElement
586
+ children: /*#__PURE__*/_jsx(AvatarStatusLabelContext, {
587
+ value: statusLabelRef,
588
+ children: rootElement
589
+ })
481
590
  });
482
591
 
483
592
  // Always return the same structure so the avatar keeps its position in the
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @file AvatarStatusDot.tsx
3
- * @input Uses React, StyleX, theme tokens, and AvatarSizeContext
3
+ * @input Uses React, StyleX, theme tokens, AvatarSizeContext, and
4
+ * AvatarStatusLabelContext (label reporting)
4
5
  * @output Exports AvatarStatusDot component and AvatarStatusDotProps type
5
6
  * @position Sub-component of Avatar; renders a size-aware status indicator
6
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarStatusDot.d.ts","sourceRoot":"","sources":["../../src/Avatar/AvatarStatusDot.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,EAAM,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AACjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAM5C,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,SAAS,CAAC;AA4CvD;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,yBAAyB,CAAC;AAErE,MAAM,WAAW,oBAAqB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACrE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AA2JD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,OAAmB,EACnB,KAAK,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,oBAAoB,qBAyCtB;yBAlDe,eAAe"}
1
+ {"version":3,"file":"AvatarStatusDot.d.ts","sourceRoot":"","sources":["../../src/Avatar/AvatarStatusDot.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAA4B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,SAAS,CAAC;AA4CvD;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,yBAAyB,CAAC;AAErE,MAAM,WAAW,oBAAqB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACrE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AA2JD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,OAAmB,EACnB,KAAK,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,oBAAoB,qBAiEtB;yBA1Ee,eAAe"}
@@ -4,7 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file AvatarStatusDot.tsx
7
- * @input Uses React, StyleX, theme tokens, and AvatarSizeContext
7
+ * @input Uses React, StyleX, theme tokens, AvatarSizeContext, and
8
+ * AvatarStatusLabelContext (label reporting)
8
9
  * @output Exports AvatarStatusDot component and AvatarStatusDotProps type
9
10
  * @position Sub-component of Avatar; renders a size-aware status indicator
10
11
  *
@@ -14,12 +15,13 @@
14
15
  * - /apps/storybook/stories/Avatar.stories.tsx (storybook stories)
15
16
  * - /packages/cli/assets/templates/blocks/components/Avatar/ (showcase blocks)
16
17
  */
17
- import React, { use } from 'react';
18
+ import React, { use, useCallback, useMemo } from 'react';
18
19
  import * as stylex from '@stylexjs/stylex';
19
20
  import "../theme/tokens.stylex.js";
20
21
  import { colorVars, radiusVars } from "../theme/tokens.stylex.js";
21
22
  import { AvatarSizeContext } from "./AvatarSizeContext.js";
22
- import { isRenderable, mergeProps } from "../utils/index.js";
23
+ import { AvatarStatusLabelContext } from "./AvatarStatusLabelContext.js";
24
+ import { isRenderable, mergeProps, mergeRefs } from "../utils/index.js";
23
25
  import { themeProps } from "../utils/themeProps.js";
24
26
 
25
27
  /**
@@ -264,6 +266,30 @@ export function AvatarStatusDot({
264
266
  ...props
265
267
  }) {
266
268
  const avatarSize = use(AvatarSizeContext);
269
+ const statusLabelRef = use(AvatarStatusLabelContext);
270
+ // Report the label through the avatar's ref from a callback ref rather than
271
+ // an Effect: the ref runs in the commit phase, so the avatar's accessible
272
+ // name is composed before paint, and a ref write costs no render.
273
+ /* eslint-disable react-compiler/react-compiler -- the avatar shares its own
274
+ ref through context so the dot can write into it; a context read looks
275
+ immutable to the compiler */
276
+ const reportRef = useCallback(() => {
277
+ const target = statusLabelRef?.current;
278
+ if (target == null) {
279
+ return;
280
+ }
281
+ target.label = label;
282
+ target.update?.();
283
+ return () => {
284
+ target.label = undefined;
285
+ target.update?.();
286
+ };
287
+ }, [statusLabelRef, label]);
288
+ /* eslint-enable react-compiler/react-compiler */
289
+ // Memoized so React only detaches and reattaches when the reported label
290
+ // actually changes; an inline merge would withdraw and re-report on every
291
+ // render of the avatar.
292
+ const rootRef = useMemo(() => mergeRefs(ref, reportRef), [ref, reportRef]);
267
293
  const {
268
294
  dotSize,
269
295
  borderWidth,
@@ -276,7 +302,7 @@ export function AvatarStatusDot({
276
302
  const glyphShape = showsIcon ? undefined : glyphShapeMap[variant];
277
303
  return /*#__PURE__*/_jsxs("div", {
278
304
  ...props,
279
- ref: ref,
305
+ ref: rootRef,
280
306
  ...(label ? {
281
307
  role: 'img',
282
308
  'aria-label': label
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @file AvatarStatusLabelContext.ts
3
+ * @input Uses React createContext, RefObject
4
+ * @output Exports AvatarStatusLabelContext, AvatarStatusLabelTarget
5
+ * @position Internal context; provided by Avatar, consumed by AvatarStatusDot
6
+ *
7
+ * SYNC: When modified, update:
8
+ * - /packages/core/src/Avatar/Avatar.doc.mjs
9
+ */
10
+ import { type RefObject } from 'react';
11
+ /**
12
+ * The box a status element writes its label into.
13
+ *
14
+ * `update` is the avatar's own recompose, set while its root element is
15
+ * attached and null otherwise. Writing `label` alone is enough on mount (the
16
+ * avatar reads the box when its root attaches, which happens after its
17
+ * children's refs); calling `update` covers a label that changes later
18
+ * without the avatar itself re-rendering.
19
+ */
20
+ export interface AvatarStatusLabelTarget {
21
+ label: string | undefined;
22
+ update: (() => void) | null;
23
+ }
24
+ /**
25
+ * Lets a status element hand its accessible label to the enclosing Avatar,
26
+ * which composes it into its own accessible name ("Jane Doe, Online").
27
+ *
28
+ * The avatar root is `role="img"`, which prunes all descendant semantics, so
29
+ * composing the label in is the only way the status reaches assistive tech
30
+ * (WCAG 4.1.2). Reading `label` off the passed element only works when the
31
+ * consumer passes `AvatarStatusDot` directly; this route works through a
32
+ * consumer's own wrapper, at any depth.
33
+ *
34
+ * The value is a ref, written from the dot's own callback ref in the commit
35
+ * phase, so reporting a label costs no render and no state. `null` outside an
36
+ * Avatar, where a standalone dot names itself and has nobody to report to.
37
+ */
38
+ export declare const AvatarStatusLabelContext: import("react").Context<RefObject<AvatarStatusLabelTarget> | null>;
39
+ //# sourceMappingURL=AvatarStatusLabelContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarStatusLabelContext.d.ts","sourceRoot":"","sources":["../../src/Avatar/AvatarStatusLabelContext.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAAgB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,oEAC2B,CAAC"}
@@ -0,0 +1,41 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file AvatarStatusLabelContext.ts
7
+ * @input Uses React createContext, RefObject
8
+ * @output Exports AvatarStatusLabelContext, AvatarStatusLabelTarget
9
+ * @position Internal context; provided by Avatar, consumed by AvatarStatusDot
10
+ *
11
+ * SYNC: When modified, update:
12
+ * - /packages/core/src/Avatar/Avatar.doc.mjs
13
+ */
14
+ import { createContext } from 'react';
15
+
16
+ /**
17
+ * The box a status element writes its label into.
18
+ *
19
+ * `update` is the avatar's own recompose, set while its root element is
20
+ * attached and null otherwise. Writing `label` alone is enough on mount (the
21
+ * avatar reads the box when its root attaches, which happens after its
22
+ * children's refs); calling `update` covers a label that changes later
23
+ * without the avatar itself re-rendering.
24
+ */
25
+
26
+ /**
27
+ * Lets a status element hand its accessible label to the enclosing Avatar,
28
+ * which composes it into its own accessible name ("Jane Doe, Online").
29
+ *
30
+ * The avatar root is `role="img"`, which prunes all descendant semantics, so
31
+ * composing the label in is the only way the status reaches assistive tech
32
+ * (WCAG 4.1.2). Reading `label` off the passed element only works when the
33
+ * consumer passes `AvatarStatusDot` directly; this route works through a
34
+ * consumer's own wrapper, at any depth.
35
+ *
36
+ * The value is a ref, written from the dot's own callback ref in the commit
37
+ * phase, so reporting a label costs no render and no state. `null` outside an
38
+ * Avatar, where a standalone dot names itself and has nobody to report to.
39
+ */
40
+ export const AvatarStatusLabelContext = /*#__PURE__*/createContext(null);
41
+ AvatarStatusLabelContext.displayName = 'AvatarStatusLabelContext';
@@ -32,7 +32,9 @@ export interface AvatarGroupProps extends BaseProps<HTMLDivElement> {
32
32
  */
33
33
  children: ReactNode;
34
34
  /**
35
- * Size applied to all avatars via context.
35
+ * Size applied to all avatars via context. This wins over each child
36
+ * Avatar's own `size` prop, including when it is left at the default, so
37
+ * set the size here rather than on the children.
36
38
  * @default 'md'
37
39
  */
38
40
  size?: AvatarSize;
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroup.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,WAAW,CAAC;AAavD,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjE,yCAAyC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,kBAAkB,EAChC,kBAAkB,EAAE,wBAAwB,EAC5C,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,gBAAgB,GAAG,SAAS,CAmE9B;yBAhFe,WAAW"}
1
+ {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroup.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,WAAW,CAAC;AAavD,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjE,yCAAyC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,kBAAkB,EAChC,kBAAkB,EAAE,wBAAwB,EAC5C,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,gBAAgB,GAAG,SAAS,CAmE9B;yBAhFe,WAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroupOverflow.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroupOverflow.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAW5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,SAAS,CAAC,WAAW,CAAC,EACtB,SAAS,CACV;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAoED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,KAAK,EACL,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,wBAAwB,GAAG,SAAS,CA2DtC;yBApEe,mBAAmB"}
1
+ {"version":3,"file":"AvatarGroupOverflow.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroupOverflow.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAa5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,SAAS,CAAC,WAAW,CAAC,EACtB,SAAS,CACV;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAqFD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,KAAK,EACL,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,wBAAwB,GAAG,SAAS,CA+DtC;yBAxEe,mBAAmB"}
@@ -15,8 +15,9 @@
15
15
  import React from 'react';
16
16
  import * as stylex from '@stylexjs/stylex';
17
17
  import "../theme/tokens.stylex.js";
18
- import { colorVars, typographyVars, fontWeightVars, radiusVars, spacingVars } from "../theme/tokens.stylex.js";
18
+ import { colorVars, typographyVars, typeScaleVars, fontWeightVars, radiusVars, spacingVars } from "../theme/tokens.stylex.js";
19
19
  import { mergeProps } from "../utils/index.js";
20
+ import { resolveSize } from "../Avatar/index.js";
20
21
  import { useAvatarGroup } from "./AvatarGroupContext.js";
21
22
  import { themeProps } from "../utils/themeProps.js";
22
23
  import { focusOutlineProps } from "../utils/focusOutline.stylex.js";
@@ -27,7 +28,7 @@ const OVERFLOW_FONT_RATIO = 0.35;
27
28
  const styles = {
28
29
  base: {
29
30
  kVAEAm: "x1n2onr6",
30
- k1xSpc: "x78zum5",
31
+ k1xSpc: "x3nfvp2",
31
32
  kGNEyG: "x6s0dn4",
32
33
  kjj79g: "xl56j7k",
33
34
  kaIpWk: "xjspbzw",
@@ -45,13 +46,13 @@ const styles = {
45
46
  $$css: true
46
47
  },
47
48
  button: {
48
- kkrTdU: "x1ypdohk",
49
+ kkrTdU: "x1ypdohk x16khyan",
49
50
  k8WAf4: "xt970qd",
50
51
  kKwaWg: "x14bno8m x1otsd3y x1nocapi",
51
52
  $$css: true
52
53
  },
53
54
  overlap: {
54
- keTefX: "x13hpdyo",
55
+ keTefX: "x1sik18w",
55
56
  $$css: true
56
57
  }
57
58
  };
@@ -74,7 +75,7 @@ const dynamicStyles = {
74
75
  "--x-height": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(s + BORDER_WIDTH * 2)
75
76
  }],
76
77
  fontSize: s => [_temp2, {
77
- "--x-fontSize": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(s * OVERFLOW_FONT_RATIO)
78
+ "--x-fontSize": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(`max(${typeScaleVars['--text-supporting-size']}, ${s * OVERFLOW_FONT_RATIO}px)`)
78
79
  }],
79
80
  overlap: offset => [_temp3, {
80
81
  "--x---_avatar-group-overlap": `${offset}px` != null ? `${offset}px` : undefined
@@ -108,12 +109,17 @@ export function AvatarGroupOverflow({
108
109
  const t = useTranslator();
109
110
  const group = useAvatarGroup();
110
111
  const size = group?.size ?? 'md';
111
- const numericSize = group?.numericSize ?? 36;
112
+ const numericSize = group?.numericSize ?? resolveSize('md');
112
113
  const overlap = group?.overlap ?? 0;
114
+
115
+ // count is a plain number, and the documented shape for it is
116
+ // `total - visibleCount`, which goes negative whenever the list is shorter
117
+ // than the slice. Clamping keeps that from rendering "+-3".
118
+ const safeCount = Math.max(0, count);
113
119
  const label = t('@astryx.avatarGroup.overflow', {
114
- count
120
+ count: safeCount
115
121
  });
116
- const content = children ?? `+${count}`;
122
+ const content = children ?? `+${safeCount}`;
117
123
  if (onClick) {
118
124
  return /*#__PURE__*/_jsx("button", {
119
125
  ref: ref,
@@ -18,15 +18,16 @@ export interface BlockquoteProps extends BaseProps<HTMLQuoteElement> {
18
18
  /** Content of the blockquote */
19
19
  children: ReactNode;
20
20
  /**
21
- * Optional attribution for the quote. Rendered in a <footer> with <cite>.
21
+ * Optional attribution for the quote. Rendered in a `<cite>` element after
22
+ * the quoted content.
22
23
  */
23
24
  cite?: ReactNode;
24
25
  }
25
26
  /**
26
27
  * Blockquote component for displaying quoted content.
27
28
  *
28
- * Renders a semantic `<blockquote>` with an accent-colored left border
29
- * and secondary text color, matching the Astryx visual language.
29
+ * Renders a semantic `<blockquote>` with an inline-start rule and secondary
30
+ * text color, matching the Astryx visual language.
30
31
  *
31
32
  * @example
32
33
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"Blockquote.d.ts","sourceRoot":"","sources":["../../src/Blockquote/Blockquote.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAM5C,MAAM,WAAW,eAAgB,SAAQ,SAAS,CAAC,gBAAgB,CAAC;IAClE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,gCAAgC;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAuBD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAmBjB;yBA3Be,UAAU"}
1
+ {"version":3,"file":"Blockquote.d.ts","sourceRoot":"","sources":["../../src/Blockquote/Blockquote.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAM5C,MAAM,WAAW,eAAgB,SAAQ,SAAS,CAAC,gBAAgB,CAAC;IAClE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,gCAAgC;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAwBD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAmBjB;yBA3Be,UAAU"}