@teamblind-chorus/ui 1.0.1 → 1.2.0

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 (131) hide show
  1. package/agents/AGENTS.md +4 -6
  2. package/agents/DESIGN.md +2 -0
  3. package/agents/LOVABLE.md +167 -373
  4. package/agents/anti-patterns.md +2 -2
  5. package/agents/catalog.md +12 -6
  6. package/agents/components/avatar-rail/avatar-rail.md +2 -0
  7. package/agents/components/avatar-rail/avatar-rail.spec.json +19 -0
  8. package/agents/components/badge/badge.md +2 -0
  9. package/agents/components/badge/role.md +2 -0
  10. package/agents/components/badge/update.md +2 -0
  11. package/agents/components/banner/banner.family.json +3 -1
  12. package/agents/components/banner/banner.md +125 -9
  13. package/agents/components/banner/banner.spec.json +64 -3
  14. package/agents/components/bottom-sheet/bottom-sheet.md +2 -0
  15. package/agents/components/bubble/bubble.md +2 -0
  16. package/agents/components/button/button.family.json +8 -2
  17. package/agents/components/button/button.md +2 -0
  18. package/agents/components/button/check.md +2 -0
  19. package/agents/components/button/check.spec.json +19 -0
  20. package/agents/components/button/fab.md +2 -0
  21. package/agents/components/button/fab.spec.json +19 -0
  22. package/agents/components/button/group.spec.json +65 -0
  23. package/agents/components/button/icon.md +2 -0
  24. package/agents/components/button/icon.spec.json +19 -0
  25. package/agents/components/button/standard.md +45 -19
  26. package/agents/components/button/standard.spec.json +19 -0
  27. package/agents/components/button/text.md +2 -0
  28. package/agents/components/button/text.spec.json +19 -0
  29. package/agents/components/button/toggle.md +2 -0
  30. package/agents/components/button/toggle.spec.json +19 -0
  31. package/agents/components/button/toolbar.md +2 -0
  32. package/agents/components/carousel/carousel.md +2 -0
  33. package/agents/components/carousel/post.md +5 -3
  34. package/agents/components/carousel/post.spec.json +4 -6
  35. package/agents/components/carousel/profile.md +4 -2
  36. package/agents/components/carousel/profile.spec.json +4 -6
  37. package/agents/components/chip/chip.md +2 -0
  38. package/agents/components/chip/filter.md +2 -0
  39. package/agents/components/chip/filter.spec.json +19 -0
  40. package/agents/components/chip/tag.md +2 -0
  41. package/agents/components/chip/tag.spec.json +19 -0
  42. package/agents/components/dialog/dialog.md +2 -0
  43. package/agents/components/directory-list/directory-list.md +2 -0
  44. package/agents/components/divider/divider.md +2 -0
  45. package/agents/components/empty-state/empty-state.family.json +28 -0
  46. package/agents/components/empty-state/empty-state.md +69 -0
  47. package/agents/components/empty-state/empty-state.spec.json +87 -0
  48. package/agents/components/feed/ad.md +2 -0
  49. package/agents/components/feed/feed.md +2 -0
  50. package/agents/components/feed/post.md +2 -0
  51. package/agents/components/form-field/form-field.md +3 -1
  52. package/agents/components/form-field/input.md +2 -0
  53. package/agents/components/form-field/input.spec.json +10 -2
  54. package/agents/components/form-field/search.md +2 -0
  55. package/agents/components/form-field/search.spec.json +10 -2
  56. package/agents/components/form-field/select.md +2 -0
  57. package/agents/components/form-field/select.spec.json +9 -1
  58. package/agents/components/form-field/textarea.md +2 -0
  59. package/agents/components/form-field/textarea.spec.json +10 -2
  60. package/agents/components/header/header.md +2 -0
  61. package/agents/components/header/main.md +2 -0
  62. package/agents/components/header/sub.md +2 -0
  63. package/agents/components/list/accordion.md +2 -0
  64. package/agents/components/list/accordion.spec.json +9 -0
  65. package/agents/components/list/entry.md +2 -0
  66. package/agents/components/list/entry.spec.json +21 -1
  67. package/agents/components/list/list.md +3 -1
  68. package/agents/components/list/radio.md +2 -0
  69. package/agents/components/list/radio.spec.json +19 -0
  70. package/agents/components/list/standard.md +48 -0
  71. package/agents/components/list/standard.spec.json +39 -3
  72. package/agents/components/metadata/compact.md +13 -7
  73. package/agents/components/metadata/compact.spec.json +19 -6
  74. package/agents/components/metadata/metadata.family.json +3 -3
  75. package/agents/components/metadata/metadata.md +4 -2
  76. package/agents/components/metadata/standard.md +24 -0
  77. package/agents/components/nav-card/nav-card.md +2 -0
  78. package/agents/components/nav-card/nav-card.spec.json +9 -0
  79. package/agents/components/nav-list/nav-list.md +2 -0
  80. package/agents/components/navigation-bar/main.md +2 -0
  81. package/agents/components/navigation-bar/navigation-bar.md +2 -0
  82. package/agents/components/navigation-bar/search.md +2 -0
  83. package/agents/components/navigation-bar/sub.md +2 -0
  84. package/agents/components/page-shell/page-shell.family.json +1 -1
  85. package/agents/components/page-shell/page-shell.md +35 -0
  86. package/agents/components/page-shell/page-shell.spec.json +85 -0
  87. package/agents/components/pagination/pagination.family.json +26 -0
  88. package/agents/components/pagination/pagination.md +40 -0
  89. package/agents/components/pagination/pagination.spec.json +54 -0
  90. package/agents/components/profile-header/profile-header.md +2 -0
  91. package/agents/components/progress/progress.md +2 -0
  92. package/agents/components/side-sheet/side-sheet.md +2 -0
  93. package/agents/components/skeleton/skeleton.md +2 -0
  94. package/agents/components/spinner/spinner.family.json +27 -0
  95. package/agents/components/spinner/spinner.md +98 -0
  96. package/agents/components/spinner/spinner.spec.json +82 -0
  97. package/agents/components/status-tag/status-tag.md +2 -0
  98. package/agents/components/suggestion-list/suggestion-list.md +2 -0
  99. package/agents/components/switch/switch.md +2 -0
  100. package/agents/components/switch/switch.spec.json +9 -0
  101. package/agents/components/tab-bar/tab-bar.md +2 -0
  102. package/agents/components/tab-bar/tab-bar.spec.json +16 -0
  103. package/agents/components/tabs/rounded.md +2 -0
  104. package/agents/components/tabs/rounded.spec.json +19 -0
  105. package/agents/components/tabs/segmented.md +2 -0
  106. package/agents/components/tabs/tabs.md +2 -0
  107. package/agents/components/tabs/underline.md +2 -0
  108. package/agents/components/tabs/underline.spec.json +19 -0
  109. package/agents/components/thumbnail/thumbnail.md +2 -0
  110. package/agents/components/toast/toast.md +2 -0
  111. package/agents/components/tooltip/tooltip.md +2 -0
  112. package/agents/compose.md +3 -3
  113. package/agents/manifest.json +9 -6
  114. package/agents/patterns/README.md +2 -0
  115. package/agents/patterns/actions.md +2 -0
  116. package/agents/patterns/browsing.md +2 -0
  117. package/agents/patterns/communications.md +2 -0
  118. package/agents/patterns/layout.md +2 -0
  119. package/agents/patterns/modals.md +2 -0
  120. package/agents/patterns/visual.md +2 -0
  121. package/agents/usage.json +27 -3
  122. package/dist/index.cjs +433 -97
  123. package/dist/index.cjs.map +1 -1
  124. package/dist/index.d.cts +74 -3
  125. package/dist/index.d.ts +74 -3
  126. package/dist/index.js +430 -98
  127. package/dist/index.js.map +1 -1
  128. package/dist/styles.css +365 -41
  129. package/package.json +1 -2
  130. package/agents/reconstruct.md +0 -55
  131. package/agents/scoped-adoption.md +0 -111
package/dist/index.js CHANGED
@@ -106,13 +106,13 @@ function formatCount(value) {
106
106
  return String(Math.floor(value));
107
107
  }
108
108
  function sizingStyle(spec, size) {
109
- const s = spec.sizes[size] ?? spec.sizes.medium;
109
+ const s2 = spec.sizes[size] ?? spec.sizes.medium;
110
110
  return {
111
- "--badge-min-height": tokenToCss(s.minHeight),
112
- "--badge-min-width": tokenToCss(s.minWidth),
113
- "--badge-padding-block": tokenToCss(s.paddingBlock),
114
- "--badge-padding-inline": tokenToCss(s.paddingInline),
115
- ...s.labelTypo ? typoStyles(s.labelTypo) : null
111
+ "--badge-min-height": tokenToCss(s2.minHeight),
112
+ "--badge-min-width": tokenToCss(s2.minWidth),
113
+ "--badge-padding-block": tokenToCss(s2.paddingBlock),
114
+ "--badge-padding-inline": tokenToCss(s2.paddingInline),
115
+ ...s2.labelTypo ? typoStyles(s2.labelTypo) : null
116
116
  };
117
117
  }
118
118
  function appearanceStyle(spec) {
@@ -235,9 +235,14 @@ function useFullBleedGuard(ref, name) {
235
235
  }
236
236
  function Banner({
237
237
  appearance = "default",
238
+ outlined = false,
239
+ neutralBody = false,
240
+ title,
238
241
  icon,
239
242
  thumbnail,
240
243
  action,
244
+ trailingAction,
245
+ trailingIcon,
241
246
  children,
242
247
  className,
243
248
  ...rest
@@ -248,13 +253,20 @@ function Banner({
248
253
  "div",
249
254
  {
250
255
  ref,
251
- className: joinClasses("chorus-banner", `chorus-banner--${appearance}`, className),
256
+ className: joinClasses(
257
+ "chorus-banner",
258
+ `chorus-banner--${appearance}`,
259
+ outlined && "chorus-banner--outlined",
260
+ neutralBody && "chorus-banner--neutral-body",
261
+ className
262
+ ),
252
263
  role: "note",
253
264
  ...rest,
254
265
  children: [
255
266
  thumbnail ? /* @__PURE__ */ jsx("span", { className: "chorus-banner__thumbnail", "aria-hidden": "true", children: thumbnail }) : null,
256
267
  !thumbnail && icon ? /* @__PURE__ */ jsx("span", { className: "chorus-banner__icon", "aria-hidden": "true", children: icon }) : null,
257
268
  /* @__PURE__ */ jsxs("div", { className: "chorus-banner__content", children: [
269
+ title ? /* @__PURE__ */ jsx("p", { className: "chorus-banner__title", children: title }) : null,
258
270
  children ? /* @__PURE__ */ jsx("p", { className: "chorus-banner__body", children }) : null,
259
271
  action ? /* @__PURE__ */ jsx(
260
272
  "a",
@@ -265,7 +277,8 @@ function Banner({
265
277
  children: action.label
266
278
  }
267
279
  ) : null
268
- ] })
280
+ ] }),
281
+ trailingAction ? /* @__PURE__ */ jsx("span", { className: "chorus-banner__trailing-action", children: trailingAction }) : trailingIcon ? /* @__PURE__ */ jsx("span", { className: "chorus-banner__trailing-icon", "aria-hidden": "true", children: trailingIcon }) : null
269
282
  ]
270
283
  }
271
284
  );
@@ -362,16 +375,16 @@ var standard_spec_default = {
362
375
  insetColor: "sys.color.focusInset"
363
376
  }}};
364
377
  function sizeStyle(size) {
365
- const s = standard_spec_default.sizes[size] ?? standard_spec_default.sizes[standard_spec_default.props.size.default];
378
+ const s2 = standard_spec_default.sizes[size] ?? standard_spec_default.sizes[standard_spec_default.props.size.default];
366
379
  return {
367
- "--button-standard-padding-block": tokenToCss(s.paddingBlock),
368
- "--button-standard-padding-inline": tokenToCss(s.paddingInline),
369
- "--button-standard-gap": tokenToCss(s.gap),
370
- "--button-standard-min-height": tokenToCss(s.minHeight),
371
- "--button-standard-min-width": tokenToCss(s.minWidth),
372
- "--button-standard-radius": tokenToCss(s.radius),
373
- "--button-standard-icon-size": tokenToCss(s.iconSize),
374
- ...typoStyles(s.labelTypo)
380
+ "--button-standard-padding-block": tokenToCss(s2.paddingBlock),
381
+ "--button-standard-padding-inline": tokenToCss(s2.paddingInline),
382
+ "--button-standard-gap": tokenToCss(s2.gap),
383
+ "--button-standard-min-height": tokenToCss(s2.minHeight),
384
+ "--button-standard-min-width": tokenToCss(s2.minWidth),
385
+ "--button-standard-radius": tokenToCss(s2.radius),
386
+ "--button-standard-icon-size": tokenToCss(s2.iconSize),
387
+ ...typoStyles(s2.labelTypo)
375
388
  };
376
389
  }
377
390
  function appearanceStyle2(appearance) {
@@ -478,8 +491,7 @@ var fab_spec_default = {
478
491
  overlay: {
479
492
  opacity: "sys.state.pressed"
480
493
  }
481
- }
482
- },
494
+ }},
483
495
  focusIndicator: {
484
496
  overlay: {
485
497
  opacity: "sys.state.focus"
@@ -1579,6 +1591,25 @@ var filter_spec_default = {
1579
1591
  opacity: "sys.state.pressed"
1580
1592
  }
1581
1593
  },
1594
+ focused: {
1595
+ overlay: {
1596
+ color: "label",
1597
+ opacity: "sys.state.focus"
1598
+ },
1599
+ focusRing: {
1600
+ composition: "outward",
1601
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1602
+ innerCounterRing: {
1603
+ width: "sys.borderWidth.hairline",
1604
+ color: "sys.color.focusInset"
1605
+ },
1606
+ outerRing: {
1607
+ width: "sys.borderWidth.thin",
1608
+ color: "sys.color.focus"
1609
+ }
1610
+ },
1611
+ note: "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the chip is in; never via plain mouse click."
1612
+ },
1582
1613
  disabled: {
1583
1614
  overlay: null,
1584
1615
  containerOpacity: "sys.state.disabled",
@@ -1686,6 +1717,25 @@ var tag_spec_default = {
1686
1717
  opacity: "sys.state.pressed"
1687
1718
  }
1688
1719
  },
1720
+ focused: {
1721
+ overlay: {
1722
+ color: "label",
1723
+ opacity: "sys.state.focus"
1724
+ },
1725
+ focusRing: {
1726
+ composition: "outward",
1727
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1728
+ innerCounterRing: {
1729
+ width: "sys.borderWidth.hairline",
1730
+ color: "sys.color.focusInset"
1731
+ },
1732
+ outerRing: {
1733
+ width: "sys.borderWidth.thin",
1734
+ color: "sys.color.focus"
1735
+ }
1736
+ },
1737
+ note: "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the chip is in; never via plain mouse click."
1738
+ },
1689
1739
  disabled: {
1690
1740
  overlay: null,
1691
1741
  containerOpacity: "sys.state.disabled",
@@ -1811,6 +1861,25 @@ var toggle_spec_default = {
1811
1861
  opacity: "sys.state.pressed"
1812
1862
  }
1813
1863
  },
1864
+ focused: {
1865
+ overlay: {
1866
+ color: "label",
1867
+ opacity: "sys.state.focus"
1868
+ },
1869
+ focusRing: {
1870
+ composition: "outward",
1871
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1872
+ innerCounterRing: {
1873
+ width: "sys.borderWidth.hairline",
1874
+ color: "sys.color.focusInset"
1875
+ },
1876
+ outerRing: {
1877
+ width: "sys.borderWidth.thin",
1878
+ color: "sys.color.focus"
1879
+ }
1880
+ },
1881
+ note: "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the button is in; never via plain mouse click."
1882
+ },
1814
1883
  disabled: {
1815
1884
  overlay: null,
1816
1885
  containerOpacity: "sys.state.disabled",
@@ -1845,7 +1914,7 @@ var FORCEABLE_STATES3 = /* @__PURE__ */ new Set(["hovered", "pressed", "focused"
1845
1914
  var SPECS = {
1846
1915
  filter: filter_spec_default,
1847
1916
  tag: tag_spec_default,
1848
- "toggle": toggle_spec_default
1917
+ toggle: toggle_spec_default
1849
1918
  };
1850
1919
  function pickAppearance(spec, appearance) {
1851
1920
  const appearances = spec.appearances || {};
@@ -1987,16 +2056,56 @@ function ButtonToggle({ active = false, ...rest }) {
1987
2056
  }
1988
2057
  var VARIANTS = {
1989
2058
  fab: ButtonFab,
1990
- "icon": ButtonIcon,
1991
- "text": ButtonText,
1992
- "check": ButtonCheck,
1993
- "toolbar": ButtonToolbar,
1994
- "toggle": ButtonToggle
2059
+ icon: ButtonIcon,
2060
+ text: ButtonText,
2061
+ check: ButtonCheck,
2062
+ toolbar: ButtonToolbar,
2063
+ toggle: ButtonToggle
1995
2064
  };
1996
2065
  var Button = forwardRef(function Button2({ variant, ...rest }, ref) {
1997
2066
  const Impl = variant && VARIANTS[variant] || ButtonStandard;
1998
2067
  return /* @__PURE__ */ jsx(Impl, { ref, ...rest });
1999
2068
  });
2069
+ var FOCUSABLE_SELECTOR = [
2070
+ "a[href]",
2071
+ "button",
2072
+ "input",
2073
+ "select",
2074
+ "textarea",
2075
+ '[tabindex]:not([tabindex="-1"])'
2076
+ ].join(",");
2077
+ function useFocusTrap(ref, active) {
2078
+ useEffect(() => {
2079
+ if (!active) return void 0;
2080
+ const onKey = (e) => {
2081
+ if (e.key !== "Tab") return;
2082
+ const container = ref.current;
2083
+ if (!container) return;
2084
+ const focusable = Array.from(
2085
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
2086
+ ).filter((el) => !el.disabled && el.offsetParent !== null);
2087
+ if (focusable.length === 0) {
2088
+ e.preventDefault();
2089
+ container.focus({ preventScroll: true });
2090
+ return;
2091
+ }
2092
+ const first = focusable[0];
2093
+ const last = focusable[focusable.length - 1];
2094
+ const activeEl = document.activeElement;
2095
+ if (e.shiftKey) {
2096
+ if (activeEl === first || !container.contains(activeEl)) {
2097
+ e.preventDefault();
2098
+ last.focus({ preventScroll: true });
2099
+ }
2100
+ } else if (activeEl === last || !container.contains(activeEl)) {
2101
+ e.preventDefault();
2102
+ first.focus({ preventScroll: true });
2103
+ }
2104
+ };
2105
+ document.addEventListener("keydown", onKey);
2106
+ return () => document.removeEventListener("keydown", onKey);
2107
+ }, [ref, active]);
2108
+ }
2000
2109
  function useBodyScrollLock(locked) {
2001
2110
  useEffect(() => {
2002
2111
  if (!locked) return void 0;
@@ -2036,6 +2145,7 @@ function BottomSheet({
2036
2145
  const lastFocusedRef = useRef(null);
2037
2146
  const [overflowing, setOverflowing] = useState(false);
2038
2147
  useBodyScrollLock(open && !inline);
2148
+ useFocusTrap(cardRef, open && !inline);
2039
2149
  useEffect(() => {
2040
2150
  if (!open || inline) return void 0;
2041
2151
  const vv = typeof window !== "undefined" ? window.visualViewport : null;
@@ -2099,6 +2209,7 @@ function BottomSheet({
2099
2209
  className: "chorus-bottom-sheet__card",
2100
2210
  role: "dialog",
2101
2211
  "aria-modal": "true",
2212
+ tabIndex: -1,
2102
2213
  "aria-label": ariaLabel ?? title,
2103
2214
  onClick: (e) => e.stopPropagation(),
2104
2215
  ...rest,
@@ -2153,6 +2264,35 @@ function Bubble({
2153
2264
  }
2154
2265
  );
2155
2266
  }
2267
+ function ButtonGroup({
2268
+ variant = "inline",
2269
+ orientation = "horizontal",
2270
+ label,
2271
+ children,
2272
+ className,
2273
+ "aria-label": ariaLabel,
2274
+ ...rest
2275
+ }) {
2276
+ const docked = variant === "docked";
2277
+ return /* @__PURE__ */ jsxs(
2278
+ "div",
2279
+ {
2280
+ className: joinClasses(
2281
+ "chorus-button-group",
2282
+ docked && "chorus-button-group--docked",
2283
+ orientation === "vertical" && "chorus-button-group--vertical",
2284
+ className
2285
+ ),
2286
+ role: "group",
2287
+ "aria-label": ariaLabel,
2288
+ ...rest,
2289
+ children: [
2290
+ label != null ? /* @__PURE__ */ jsx("div", { className: "chorus-button-group__label", children: label }) : null,
2291
+ /* @__PURE__ */ jsx("div", { className: "chorus-button-group__row", children })
2292
+ ]
2293
+ }
2294
+ );
2295
+ }
2156
2296
 
2157
2297
  // ../../schema/components/header/main.spec.json
2158
2298
  var main_spec_default = {
@@ -2313,6 +2453,7 @@ function EntryRow({
2313
2453
  "span",
2314
2454
  {
2315
2455
  className: "chorus-entry-row__trailing",
2456
+ "data-nested-action": "",
2316
2457
  onClick: (e) => e.stopPropagation(),
2317
2458
  onKeyDown: (e) => e.stopPropagation(),
2318
2459
  children: trailing
@@ -2387,6 +2528,26 @@ function List({
2387
2528
  if (isRadio) onChange == null ? void 0 : onChange(item.value);
2388
2529
  (_a = item.onClick) == null ? void 0 : _a.call(item);
2389
2530
  };
2531
+ const rowMain = isEntry ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
2532
+ isRadio ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading", children: /* @__PURE__ */ jsx(RadioIndicator, { selected }) }) : item.thumbnail ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading chorus-list__leading--image", children: /* @__PURE__ */ jsx(Thumbnail, { size: 40, ...item.thumbnail }) }) : item.icon ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading chorus-list__leading--icon", "aria-hidden": "true", children: item.icon }) : null,
2533
+ /* @__PURE__ */ jsxs("span", { className: "chorus-list__label-col", children: [
2534
+ /* @__PURE__ */ jsxs("span", { className: "chorus-list__primary-row", children: [
2535
+ /* @__PURE__ */ jsx("span", { className: "chorus-list__label", children: item.label }),
2536
+ item.count != null ? /* @__PURE__ */ jsx("span", { className: "chorus-list__count", children: item.count }) : null
2537
+ ] }),
2538
+ item.supportingText ? /* @__PURE__ */ jsx("span", { className: "chorus-list__supporting", children: item.supportingText }) : null
2539
+ ] }),
2540
+ item.nav && !item.trailingIcon ? /* @__PURE__ */ jsx("span", { className: "chorus-list__trailing chorus-list__nav-chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 }) }) : item.trailingIcon ? /* @__PURE__ */ jsx(
2541
+ "span",
2542
+ {
2543
+ className: "chorus-list__trailing",
2544
+ "data-nested-action": "",
2545
+ onClick: (e) => e.stopPropagation(),
2546
+ onKeyDown: (e) => e.stopPropagation(),
2547
+ children: item.trailingIcon
2548
+ }
2549
+ ) : null
2550
+ ] });
2390
2551
  return /* @__PURE__ */ jsx(
2391
2552
  "div",
2392
2553
  {
@@ -2421,25 +2582,26 @@ function List({
2421
2582
  description: item.description,
2422
2583
  trailing: item.trailingIcon
2423
2584
  }
2424
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2425
- isRadio ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading", children: /* @__PURE__ */ jsx(RadioIndicator, { selected }) }) : item.thumbnail ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading chorus-list__leading--image", children: /* @__PURE__ */ jsx(Thumbnail, { size: 40, ...item.thumbnail }) }) : item.icon ? /* @__PURE__ */ jsx("span", { className: "chorus-list__leading chorus-list__leading--icon", "aria-hidden": "true", children: item.icon }) : null,
2426
- /* @__PURE__ */ jsxs("span", { className: "chorus-list__label-col", children: [
2427
- /* @__PURE__ */ jsxs("span", { className: "chorus-list__primary-row", children: [
2428
- /* @__PURE__ */ jsx("span", { className: "chorus-list__label", children: item.label }),
2429
- item.count != null ? /* @__PURE__ */ jsx("span", { className: "chorus-list__count", children: item.count }) : null
2430
- ] }),
2431
- item.supportingText ? /* @__PURE__ */ jsx("span", { className: "chorus-list__supporting", children: item.supportingText }) : null
2432
- ] }),
2433
- item.nav && !item.trailingIcon ? /* @__PURE__ */ jsx("span", { className: "chorus-list__trailing chorus-list__nav-chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 }) }) : item.trailingIcon ? /* @__PURE__ */ jsx(
2434
- "span",
2435
- {
2436
- className: "chorus-list__trailing",
2437
- onClick: (e) => e.stopPropagation(),
2438
- onKeyDown: (e) => e.stopPropagation(),
2439
- children: item.trailingIcon
2440
- }
2441
- ) : null
2442
- ] })
2585
+ ) : item.banner ? (
2586
+ // Embedded-Banner row the text group stacks over a Banner
2587
+ // that spans the row's full content width, 8px (stack.xs)
2588
+ // below. The Banner is a nested action region: its own
2589
+ // controls never commit the row, and the row's hover/press
2590
+ // overlay is suppressed over it (see styles.css).
2591
+ /* @__PURE__ */ jsxs("span", { className: "chorus-list__stack", children: [
2592
+ /* @__PURE__ */ jsx("span", { className: "chorus-list__row-main", children: rowMain }),
2593
+ /* @__PURE__ */ jsx(
2594
+ "span",
2595
+ {
2596
+ className: "chorus-list__banner",
2597
+ "data-nested-action": "",
2598
+ onClick: (e) => e.stopPropagation(),
2599
+ onKeyDown: (e) => e.stopPropagation(),
2600
+ children: item.banner
2601
+ }
2602
+ )
2603
+ ] })
2604
+ ) : rowMain
2443
2605
  },
2444
2606
  item.value ?? idx
2445
2607
  );
@@ -2699,6 +2861,7 @@ function Dialog({
2699
2861
  const cardRef = useRef(null);
2700
2862
  const lastFocusedRef = useRef(null);
2701
2863
  useBodyScrollLock(open && !inline);
2864
+ useFocusTrap(cardRef, open && !inline);
2702
2865
  useEffect(() => {
2703
2866
  var _a;
2704
2867
  if (!open) return void 0;
@@ -2744,6 +2907,7 @@ function Dialog({
2744
2907
  className: joinClasses("chorus-dialog__card", image && "chorus-dialog__card--with-image"),
2745
2908
  role: "dialog",
2746
2909
  "aria-modal": "true",
2910
+ tabIndex: -1,
2747
2911
  "aria-label": ariaLabel ?? title,
2748
2912
  onClick: (e) => e.stopPropagation(),
2749
2913
  ...rest,
@@ -2777,6 +2941,80 @@ function Divider({
2777
2941
  }
2778
2942
  );
2779
2943
  }
2944
+
2945
+ // ../../schema/components/empty-state/empty-state.spec.json
2946
+ var empty_state_spec_default = {
2947
+ sizing: {
2948
+ illustrationSize: "ref.space.600",
2949
+ illustrationColor: "sys.color.onSurfaceVariant",
2950
+ illustrationGap: "sys.layout.stack.sm",
2951
+ headlineTypo: "sys.typo.heading.sm",
2952
+ headlineColor: "sys.color.onSurface",
2953
+ bodyTypo: "sys.typo.body.sm",
2954
+ bodyColor: "sys.color.onSurfaceVariant",
2955
+ bodyGap: "sys.layout.stack.2xs",
2956
+ actionGap: "sys.layout.stack.md"
2957
+ }};
2958
+ var s = empty_state_spec_default.sizing;
2959
+ var CONTAINER_STYLE = {
2960
+ "--empty-state-illustration-gap": tokenToCss(s.illustrationGap),
2961
+ "--empty-state-body-gap": tokenToCss(s.bodyGap),
2962
+ "--empty-state-action-gap": tokenToCss(s.actionGap)
2963
+ };
2964
+ var ILLUSTRATION_STYLE = {
2965
+ "--empty-state-illustration-size": tokenToCss(s.illustrationSize),
2966
+ "--empty-state-illustration-color": tokenToCss(s.illustrationColor)
2967
+ };
2968
+ var HEADLINE_STYLE = {
2969
+ "--empty-state-headline-color": tokenToCss(s.headlineColor),
2970
+ ...typoStyles(s.headlineTypo)
2971
+ };
2972
+ var BODY_STYLE = {
2973
+ "--empty-state-body-color": tokenToCss(s.bodyColor),
2974
+ ...typoStyles(s.bodyTypo)
2975
+ };
2976
+ function EmptyState({
2977
+ illustration,
2978
+ headline,
2979
+ body,
2980
+ action,
2981
+ className,
2982
+ style,
2983
+ ...rest
2984
+ }) {
2985
+ return /* @__PURE__ */ jsxs(
2986
+ "div",
2987
+ {
2988
+ role: "status",
2989
+ className: joinClasses("chorus-empty-state", className),
2990
+ style: { ...CONTAINER_STYLE, ...style },
2991
+ ...rest,
2992
+ children: [
2993
+ illustration ? /* @__PURE__ */ jsx(
2994
+ "span",
2995
+ {
2996
+ className: "chorus-empty-state__illustration",
2997
+ "aria-hidden": "true",
2998
+ style: ILLUSTRATION_STYLE,
2999
+ children: illustration
3000
+ }
3001
+ ) : null,
3002
+ /* @__PURE__ */ jsx("p", { className: "chorus-empty-state__headline", style: HEADLINE_STYLE, children: headline }),
3003
+ body ? /* @__PURE__ */ jsx("p", { className: "chorus-empty-state__body", style: BODY_STYLE, children: body }) : null,
3004
+ action ? /* @__PURE__ */ jsx("div", { className: "chorus-empty-state__action", children: /* @__PURE__ */ jsx(
3005
+ Button,
3006
+ {
3007
+ appearance: "primary",
3008
+ onClick: action.onClick ?? (action.href ? () => {
3009
+ window.location.assign(action.href);
3010
+ } : void 0),
3011
+ children: action.label
3012
+ }
3013
+ ) }) : null
3014
+ ]
3015
+ }
3016
+ );
3017
+ }
2780
3018
  var TabsContext = createContext({
2781
3019
  variant: "underline",
2782
3020
  value: null,
@@ -2899,19 +3137,19 @@ function TabsUnderline({ className, style, children, ...rest }) {
2899
3137
  useScrollOverflow(ref);
2900
3138
  useSlidingIndicator(ref, indicatorRef, value);
2901
3139
  useFullBleedGuard(ref, "Tabs");
2902
- const s = underline_spec_default.sizing;
3140
+ const s2 = underline_spec_default.sizing;
2903
3141
  const composedStyle = {
2904
- "--tabs-container-padding-inline": tokenToCss(s.containerPaddingInline),
2905
- "--tabs-tab-min-height": tokenToCss(s.minHeight),
2906
- "--tabs-tab-padding-block": tokenToCss(s.paddingBlock),
2907
- "--tabs-tab-padding-inline": tokenToCss(s.paddingInline),
2908
- "--tabs-inter-tab-gap": tokenToCss(s.interTabGap),
2909
- "--tabs-slot-gap": tokenToCss(s.slotGap),
2910
- "--tabs-icon-size": tokenToCss(s.iconSize),
2911
- "--tabs-indicator-height": tokenToCss(s.indicatorHeight),
2912
- "--tabs-divider-width": tokenToCss(s.dividerWidth),
2913
- "--tabs-divider-color": tokenToCss(s.dividerColor),
2914
- "--tabs-fade-width": tokenToCss(s.fadeWidth),
3142
+ "--tabs-container-padding-inline": tokenToCss(s2.containerPaddingInline),
3143
+ "--tabs-tab-min-height": tokenToCss(s2.minHeight),
3144
+ "--tabs-tab-padding-block": tokenToCss(s2.paddingBlock),
3145
+ "--tabs-tab-padding-inline": tokenToCss(s2.paddingInline),
3146
+ "--tabs-inter-tab-gap": tokenToCss(s2.interTabGap),
3147
+ "--tabs-slot-gap": tokenToCss(s2.slotGap),
3148
+ "--tabs-icon-size": tokenToCss(s2.iconSize),
3149
+ "--tabs-indicator-height": tokenToCss(s2.indicatorHeight),
3150
+ "--tabs-divider-width": tokenToCss(s2.dividerWidth),
3151
+ "--tabs-divider-color": tokenToCss(s2.dividerColor),
3152
+ "--tabs-fade-width": tokenToCss(s2.fadeWidth),
2915
3153
  "--tabs-label-unselected": tokenToCss(underline_spec_default.selectionStates.unselected.label),
2916
3154
  "--tabs-label-selected": tokenToCss(underline_spec_default.selectionStates.selected.label),
2917
3155
  "--tabs-indicator-color": tokenToCss(underline_spec_default.selectionStates.selected.indicator),
@@ -2923,7 +3161,7 @@ function TabsUnderline({ className, style, children, ...rest }) {
2923
3161
  "--tabs-focus-outer-color": tokenToCss(underline_spec_default.focusIndicator.ring.outerColor),
2924
3162
  "--tabs-focus-inset-width": tokenToCss(underline_spec_default.focusIndicator.ring.insetWidth),
2925
3163
  "--tabs-focus-inset-color": tokenToCss(underline_spec_default.focusIndicator.ring.insetColor),
2926
- ...typoStyles(s.labelTypo),
3164
+ ...typoStyles(s2.labelTypo),
2927
3165
  ...style
2928
3166
  };
2929
3167
  return /* @__PURE__ */ jsxs(
@@ -2985,12 +3223,12 @@ function TabsSegmented({ className, style, children, ...rest }) {
2985
3223
  const ref = useRef(null);
2986
3224
  useScrollOverflow(ref);
2987
3225
  useFullBleedGuard(ref, "Tabs");
2988
- const s = segmented_spec_default.sizing;
3226
+ const s2 = segmented_spec_default.sizing;
2989
3227
  const composedStyle = {
2990
- "--tabs-container-padding-block": tokenToCss(s.containerPaddingBlock),
2991
- "--tabs-container-padding-inline": tokenToCss(s.containerPaddingInline),
2992
- "--tabs-inter-segment-gap": tokenToCss(s.interSegmentGap),
2993
- "--tabs-fade-width": tokenToCss(s.fadeWidth),
3228
+ "--tabs-container-padding-block": tokenToCss(s2.containerPaddingBlock),
3229
+ "--tabs-container-padding-inline": tokenToCss(s2.containerPaddingInline),
3230
+ "--tabs-inter-segment-gap": tokenToCss(s2.interSegmentGap),
3231
+ "--tabs-fade-width": tokenToCss(s2.fadeWidth),
2994
3232
  ...style
2995
3233
  };
2996
3234
  return /* @__PURE__ */ jsx(
@@ -3187,13 +3425,13 @@ function Metadata({
3187
3425
  const hasMeta = Array.isArray(meta) && meta.length > 0;
3188
3426
  const hasSubtitle = !hasMeta && subtitle != null && subtitle !== "";
3189
3427
  if (variant === "compact") {
3190
- return /* @__PURE__ */ jsx("div", { className: joinClasses("chorus-metadata", "chorus-metadata--compact", className), ...rest, children: /* @__PURE__ */ jsxs("div", { className: "chorus-metadata__meta", children: [
3191
- hasMeta ? metaParts(meta) : null,
3192
- timestamp ? /* @__PURE__ */ jsxs(Fragment, { children: [
3193
- /* @__PURE__ */ jsx("span", { className: "chorus-metadata__dot", "aria-hidden": "true", children: "\xB7" }),
3194
- /* @__PURE__ */ jsx("span", { className: "chorus-metadata__timestamp", children: timestamp })
3195
- ] }) : null
3196
- ] }) });
3428
+ return /* @__PURE__ */ jsxs("div", { className: joinClasses("chorus-metadata", "chorus-metadata--compact", className), ...rest, children: [
3429
+ avatar ? /* @__PURE__ */ jsx(Thumbnail, { size: 32, ...avatar }) : null,
3430
+ /* @__PURE__ */ jsxs("div", { className: "chorus-metadata__meta", children: [
3431
+ hasMeta ? metaParts(meta) : null,
3432
+ timestamp ? /* @__PURE__ */ jsx("span", { className: "chorus-metadata__timestamp", children: timestamp }) : null
3433
+ ] })
3434
+ ] });
3197
3435
  }
3198
3436
  return /* @__PURE__ */ jsxs("div", { className: joinClasses("chorus-metadata", className), ...rest, children: [
3199
3437
  /* @__PURE__ */ jsx(Thumbnail, { size: 32, ...avatar ?? { alt: typeof name === "string" ? name : "" } }),
@@ -3484,6 +3722,33 @@ function FeedAd({
3484
3722
  ) : null
3485
3723
  ] });
3486
3724
  }
3725
+ function Pagination({
3726
+ count = 0,
3727
+ activeIndex = 0,
3728
+ className,
3729
+ ...rest
3730
+ }) {
3731
+ if (count < 2) return null;
3732
+ const active = Math.max(0, Math.min(count - 1, activeIndex));
3733
+ return /* @__PURE__ */ jsx(
3734
+ "span",
3735
+ {
3736
+ className: joinClasses("chorus-pagination", className),
3737
+ "aria-hidden": "true",
3738
+ ...rest,
3739
+ children: Array.from({ length: count }, (_, idx) => /* @__PURE__ */ jsx(
3740
+ "span",
3741
+ {
3742
+ className: joinClasses(
3743
+ "chorus-pagination__dot",
3744
+ idx === active && "chorus-pagination__dot--active"
3745
+ )
3746
+ },
3747
+ idx
3748
+ ))
3749
+ }
3750
+ );
3751
+ }
3487
3752
  var MAX_CARDS = 5;
3488
3753
  function PostCarousel({
3489
3754
  items = [],
@@ -3544,16 +3809,7 @@ function PostCarousel({
3544
3809
  ))
3545
3810
  }
3546
3811
  ),
3547
- cards.length > 1 ? /* @__PURE__ */ jsx("div", { className: "chorus-post-carousel__pagination", "aria-hidden": "true", children: cards.map((_, idx) => /* @__PURE__ */ jsx(
3548
- "span",
3549
- {
3550
- className: joinClasses(
3551
- "chorus-post-carousel__dot",
3552
- idx === activeIndex && "chorus-post-carousel__dot--active"
3553
- )
3554
- },
3555
- idx
3556
- )) }) : null
3812
+ /* @__PURE__ */ jsx(Pagination, { count: cards.length, activeIndex })
3557
3813
  ]
3558
3814
  }
3559
3815
  );
@@ -3704,16 +3960,7 @@ function ProfileCarousel({
3704
3960
  ))
3705
3961
  }
3706
3962
  ),
3707
- cards.length > 1 ? /* @__PURE__ */ jsx("div", { className: "chorus-profile-carousel__pagination", "aria-hidden": "true", children: cards.map((_, idx) => /* @__PURE__ */ jsx(
3708
- "span",
3709
- {
3710
- className: joinClasses(
3711
- "chorus-profile-carousel__dot",
3712
- idx === activeIndex && "chorus-profile-carousel__dot--active"
3713
- )
3714
- },
3715
- idx
3716
- )) }) : null
3963
+ /* @__PURE__ */ jsx(Pagination, { count: cards.length, activeIndex })
3717
3964
  ]
3718
3965
  }
3719
3966
  );
@@ -4317,15 +4564,23 @@ var input_spec_default = {
4317
4564
  overlay: {
4318
4565
  color: "text",
4319
4566
  opacity: "sys.state.pressed"
4320
- }
4567
+ },
4568
+ nestedActionScope: "The pressed overlay (and hover stroke) is suppressed while the pointer presses / hovers the trailing clear button \u2014 that '\xD7' is an independent nested action, so the small control owns the state and the large field does not also read as pressed. The visual-state boundary matches the action boundary."
4321
4569
  },
4322
4570
  active: {
4571
+ isFocusState: true,
4323
4572
  overlay: null,
4324
4573
  border: "borderActive",
4325
4574
  strokeWeight: "activeStrokeWeight",
4326
4575
  caret: "visible",
4327
4576
  showsClearWhenValue: true,
4328
- note: "The stroke steps from its rest `hairline` (1px) to `activeStrokeWeight` (2px) and re-tones to `borderActive` \u2014 but it is an inset `box-shadow`, not a `border`, so the box model is untouched: the field's footprint, caret, and text position are pixel-stable as it goes active. Nothing reflows. The clear button is shown only in this state (and only when the value is non-empty)."
4577
+ focusRing: {
4578
+ composition: "outward",
4579
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4580
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4581
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4582
+ },
4583
+ note: "This IS the field's keyboard/input-focus state \u2014 `active` (caret visible, input engaged) and `:focus-visible` coincide for a text field, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. The stroke steps from its rest `hairline` (1px) to `activeStrokeWeight` (2px) and re-tones to `borderActive` \u2014 but it is an inset `box-shadow`, not a `border`, so the box model is untouched: the field's footprint, caret, and text position are pixel-stable as it goes active. Nothing reflows. The clear button is shown only in this state (and only when the value is non-empty)."
4329
4584
  },
4330
4585
  disabled: {
4331
4586
  overlay: null,
@@ -4503,14 +4758,22 @@ var textarea_spec_default = {
4503
4758
  hovered: { overlay: null, border: "borderHover" },
4504
4759
  pressed: {
4505
4760
  border: "borderHover",
4506
- overlay: { color: "text", opacity: "sys.state.pressed" }
4761
+ overlay: { color: "text", opacity: "sys.state.pressed" },
4762
+ nestedActionScope: "The pressed overlay (and hover stroke) is suppressed while the pointer presses / hovers the trailing clear button \u2014 that '\xD7' is an independent nested action, so the small control owns the state and the large field does not also read as pressed. The visual-state boundary matches the action boundary."
4507
4763
  },
4508
4764
  active: {
4765
+ isFocusState: true,
4509
4766
  overlay: null,
4510
4767
  border: "borderActive",
4511
4768
  strokeWeight: "activeStrokeWeight",
4512
4769
  caret: "visible",
4513
- note: "Stroke steps from `hairline` (1px) to `activeStrokeWeight` (2px) as an inset box-shadow \u2014 same pixel-stable contract as input."
4770
+ focusRing: {
4771
+ composition: "outward",
4772
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4773
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4774
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4775
+ },
4776
+ note: "This IS the field's keyboard/input-focus state \u2014 `active` (caret visible, input engaged) and `:focus-visible` coincide for a text field, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. Stroke steps from `hairline` (1px) to `activeStrokeWeight` (2px) as an inset box-shadow \u2014 same pixel-stable contract as input."
4514
4777
  },
4515
4778
  disabled: {
4516
4779
  overlay: null,
@@ -4640,15 +4903,23 @@ var search_spec_default = {
4640
4903
  overlay: {
4641
4904
  color: "text",
4642
4905
  opacity: "sys.state.pressed"
4643
- }
4906
+ },
4907
+ nestedActionScope: "The pressed overlay (and hover stroke) is suppressed while the pointer presses / hovers the trailing clear button \u2014 that '\xD7' is an independent nested action, so the small control owns the state and the large field does not also read as pressed. The visual-state boundary matches the action boundary."
4644
4908
  },
4645
4909
  active: {
4910
+ isFocusState: true,
4646
4911
  overlay: null,
4647
4912
  border: "borderActive",
4648
4913
  strokeWeight: "activeStrokeWeight",
4649
4914
  caret: "visible",
4650
4915
  showsClearWhenValue: true,
4651
- note: "The stroke steps from its rest `hairline` (1px) to `activeStrokeWeight` (2px) and re-tones to `borderActive` \u2014 but it is an inset `box-shadow`, not a `border`, so the box model is untouched: the field's footprint, caret, and text position are pixel-stable as it goes active. Nothing reflows. The clear button is shown only in this state (and only when the value is non-empty)."
4916
+ focusRing: {
4917
+ composition: "outward",
4918
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4919
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4920
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4921
+ },
4922
+ note: "This IS the field's keyboard/input-focus state \u2014 `active` (caret visible, input engaged) and `:focus-visible` coincide for a text field, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. The stroke steps from its rest `hairline` (1px) to `activeStrokeWeight` (2px) and re-tones to `borderActive` \u2014 but it is an inset `box-shadow`, not a `border`, so the box model is untouched: the field's footprint, caret, and text position are pixel-stable as it goes active. Nothing reflows. The clear button is shown only in this state (and only when the value is non-empty)."
4652
4923
  },
4653
4924
  disabled: {
4654
4925
  overlay: null,
@@ -4835,9 +5106,17 @@ var select_spec_default = {
4835
5106
  }
4836
5107
  },
4837
5108
  active: {
5109
+ isFocusState: true,
4838
5110
  overlay: null,
4839
5111
  border: "borderActive",
4840
- strokeWeight: "activeStrokeWeight"
5112
+ strokeWeight: "activeStrokeWeight",
5113
+ focusRing: {
5114
+ composition: "outward",
5115
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
5116
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
5117
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
5118
+ },
5119
+ note: "This IS the trigger's keyboard-focus / open state \u2014 `:focus-visible` and the engaged (open) state coincide for the select trigger, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. The stroke re-tones to `borderActive` at `activeStrokeWeight` (2px) as an inset box-shadow, pixel-stable (no reflow)."
4841
5120
  },
4842
5121
  disabled: {
4843
5122
  overlay: null,
@@ -5011,6 +5290,7 @@ function FormFieldBox({
5011
5290
  {
5012
5291
  type: "button",
5013
5292
  className: "chorus-field__clear",
5293
+ "data-nested-action": "",
5014
5294
  "aria-label": "Clear",
5015
5295
  onClick: handleClear,
5016
5296
  children: /* @__PURE__ */ jsx(XCircleFillIcon, {})
@@ -5105,7 +5385,7 @@ var FormFieldSelect = Select;
5105
5385
  var VARIANTS3 = {
5106
5386
  input: FormFieldInput,
5107
5387
  textarea: FormFieldTextarea,
5108
- "search": FormFieldSearchBar,
5388
+ search: FormFieldSearchBar,
5109
5389
  select: FormFieldSelect
5110
5390
  };
5111
5391
  function FormField({ variant = "input", ...rest }) {
@@ -5457,6 +5737,58 @@ function SkeletonGroup({
5457
5737
  }
5458
5738
  );
5459
5739
  }
5740
+
5741
+ // ../../schema/components/spinner/spinner.spec.json
5742
+ var spinner_spec_default = {
5743
+ sizes: {
5744
+ medium: {
5745
+ diameter: "sys.icon.lg",
5746
+ labelTypo: "sys.typo.body.sm",
5747
+ gap: "sys.layout.inline.sm"
5748
+ },
5749
+ small: {
5750
+ diameter: "sys.icon.md",
5751
+ labelTypo: "sys.typo.body.sm",
5752
+ gap: "sys.layout.inline.sm"
5753
+ }
5754
+ }};
5755
+ function sizingStyle4(spec, size) {
5756
+ const s2 = spec.sizes[size] ?? spec.sizes.medium;
5757
+ return {
5758
+ "--spinner-diameter": tokenToCss(s2.diameter),
5759
+ "--spinner-gap": tokenToCss(s2.gap)
5760
+ };
5761
+ }
5762
+ function Spinner({
5763
+ size = "medium",
5764
+ label,
5765
+ className,
5766
+ style,
5767
+ "aria-label": ariaLabel,
5768
+ ...rest
5769
+ }) {
5770
+ var _a;
5771
+ const labelTypo = ((_a = spinner_spec_default.sizes[size]) == null ? void 0 : _a.labelTypo) ?? spinner_spec_default.sizes.medium.labelTypo;
5772
+ const a11yLabel = label != null ? void 0 : ariaLabel ?? "Loading";
5773
+ return /* @__PURE__ */ jsxs(
5774
+ "span",
5775
+ {
5776
+ role: "status",
5777
+ "aria-label": a11yLabel,
5778
+ className: joinClasses(
5779
+ "chorus-spinner",
5780
+ `chorus-spinner--${size}`,
5781
+ className
5782
+ ),
5783
+ style: { ...sizingStyle4(spinner_spec_default, size), ...style },
5784
+ ...rest,
5785
+ children: [
5786
+ /* @__PURE__ */ jsx("span", { className: "chorus-spinner__arc", "aria-hidden": "true" }),
5787
+ label != null ? /* @__PURE__ */ jsx("span", { className: "chorus-spinner__label", style: typoStyles(labelTypo), children: label }) : null
5788
+ ]
5789
+ }
5790
+ );
5791
+ }
5460
5792
  function StatusTag({
5461
5793
  appearance = "neutral",
5462
5794
  children,
@@ -5842,6 +6174,6 @@ function Tooltip({
5842
6174
  );
5843
6175
  }
5844
6176
 
5845
- export { Accordion, Banner as Alert, NavigationBar as AppBar, Thumbnail as Avatar, AvatarRail, Badge, Banner, TabBar as BottomNav, BottomSheet, Bubble, Button, Carousel, SuggestionList as ChannelList, AvatarRail as ChannelRail, Chip, Dialog, DirectoryList, Divider, BottomSheet as Drawer, Feed, FeedAd, FeedGroup, FormField, FormFieldGroup, Header, Input, List, Metadata, NavCard, NavCardGroup, NavList, NavigationBar, PageShell, PostCarousel, ProfileCarousel, ProfileHeader, Progress, SearchBar2 as SearchBar, Carousel as Section, Select, BottomSheet as Sheet, SideSheet as SideDrawer, SideSheet, SideSheetGroup, Skeleton, SkeletonGroup, StatusTag, SubHeader, SuggestionList, Switch, Tab, TabBar, Tabs, Textarea, Thumbnail, Toast, Tooltip };
6177
+ export { Accordion, Banner as Alert, NavigationBar as AppBar, Thumbnail as Avatar, AvatarRail, Badge, Banner, TabBar as BottomNav, BottomSheet, Bubble, Button, ButtonGroup, Carousel, SuggestionList as ChannelList, AvatarRail as ChannelRail, Chip, Dialog, DirectoryList, Divider, BottomSheet as Drawer, EmptyState, Feed, FeedAd, FeedGroup, FormField, FormFieldGroup, Header, Input, List, Metadata, NavCard, NavCardGroup, NavList, NavigationBar, PageShell, Pagination, PostCarousel, ProfileCarousel, ProfileHeader, Progress, SearchBar2 as SearchBar, Carousel as Section, Select, BottomSheet as Sheet, SideSheet as SideDrawer, SideSheet, SideSheetGroup, Skeleton, SkeletonGroup, Spinner, StatusTag, SubHeader, SuggestionList, Switch, Tab, TabBar, Tabs, Textarea, Thumbnail, Toast, Tooltip };
5846
6178
  //# sourceMappingURL=index.js.map
5847
6179
  //# sourceMappingURL=index.js.map