@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.cjs CHANGED
@@ -108,13 +108,13 @@ function formatCount(value) {
108
108
  return String(Math.floor(value));
109
109
  }
110
110
  function sizingStyle(spec, size) {
111
- const s = spec.sizes[size] ?? spec.sizes.medium;
111
+ const s2 = spec.sizes[size] ?? spec.sizes.medium;
112
112
  return {
113
- "--badge-min-height": tokenToCss(s.minHeight),
114
- "--badge-min-width": tokenToCss(s.minWidth),
115
- "--badge-padding-block": tokenToCss(s.paddingBlock),
116
- "--badge-padding-inline": tokenToCss(s.paddingInline),
117
- ...s.labelTypo ? typoStyles(s.labelTypo) : null
113
+ "--badge-min-height": tokenToCss(s2.minHeight),
114
+ "--badge-min-width": tokenToCss(s2.minWidth),
115
+ "--badge-padding-block": tokenToCss(s2.paddingBlock),
116
+ "--badge-padding-inline": tokenToCss(s2.paddingInline),
117
+ ...s2.labelTypo ? typoStyles(s2.labelTypo) : null
118
118
  };
119
119
  }
120
120
  function appearanceStyle(spec) {
@@ -237,9 +237,14 @@ function useFullBleedGuard(ref, name) {
237
237
  }
238
238
  function Banner({
239
239
  appearance = "default",
240
+ outlined = false,
241
+ neutralBody = false,
242
+ title,
240
243
  icon,
241
244
  thumbnail,
242
245
  action,
246
+ trailingAction,
247
+ trailingIcon,
243
248
  children,
244
249
  className,
245
250
  ...rest
@@ -250,13 +255,20 @@ function Banner({
250
255
  "div",
251
256
  {
252
257
  ref,
253
- className: joinClasses("chorus-banner", `chorus-banner--${appearance}`, className),
258
+ className: joinClasses(
259
+ "chorus-banner",
260
+ `chorus-banner--${appearance}`,
261
+ outlined && "chorus-banner--outlined",
262
+ neutralBody && "chorus-banner--neutral-body",
263
+ className
264
+ ),
254
265
  role: "note",
255
266
  ...rest,
256
267
  children: [
257
268
  thumbnail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-banner__thumbnail", "aria-hidden": "true", children: thumbnail }) : null,
258
269
  !thumbnail && icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-banner__icon", "aria-hidden": "true", children: icon }) : null,
259
270
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chorus-banner__content", children: [
271
+ title ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "chorus-banner__title", children: title }) : null,
260
272
  children ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "chorus-banner__body", children }) : null,
261
273
  action ? /* @__PURE__ */ jsxRuntime.jsx(
262
274
  "a",
@@ -267,7 +279,8 @@ function Banner({
267
279
  children: action.label
268
280
  }
269
281
  ) : null
270
- ] })
282
+ ] }),
283
+ trailingAction ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-banner__trailing-action", children: trailingAction }) : trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-banner__trailing-icon", "aria-hidden": "true", children: trailingIcon }) : null
271
284
  ]
272
285
  }
273
286
  );
@@ -364,16 +377,16 @@ var standard_spec_default = {
364
377
  insetColor: "sys.color.focusInset"
365
378
  }}};
366
379
  function sizeStyle(size) {
367
- const s = standard_spec_default.sizes[size] ?? standard_spec_default.sizes[standard_spec_default.props.size.default];
380
+ const s2 = standard_spec_default.sizes[size] ?? standard_spec_default.sizes[standard_spec_default.props.size.default];
368
381
  return {
369
- "--button-standard-padding-block": tokenToCss(s.paddingBlock),
370
- "--button-standard-padding-inline": tokenToCss(s.paddingInline),
371
- "--button-standard-gap": tokenToCss(s.gap),
372
- "--button-standard-min-height": tokenToCss(s.minHeight),
373
- "--button-standard-min-width": tokenToCss(s.minWidth),
374
- "--button-standard-radius": tokenToCss(s.radius),
375
- "--button-standard-icon-size": tokenToCss(s.iconSize),
376
- ...typoStyles(s.labelTypo)
382
+ "--button-standard-padding-block": tokenToCss(s2.paddingBlock),
383
+ "--button-standard-padding-inline": tokenToCss(s2.paddingInline),
384
+ "--button-standard-gap": tokenToCss(s2.gap),
385
+ "--button-standard-min-height": tokenToCss(s2.minHeight),
386
+ "--button-standard-min-width": tokenToCss(s2.minWidth),
387
+ "--button-standard-radius": tokenToCss(s2.radius),
388
+ "--button-standard-icon-size": tokenToCss(s2.iconSize),
389
+ ...typoStyles(s2.labelTypo)
377
390
  };
378
391
  }
379
392
  function appearanceStyle2(appearance) {
@@ -480,8 +493,7 @@ var fab_spec_default = {
480
493
  overlay: {
481
494
  opacity: "sys.state.pressed"
482
495
  }
483
- }
484
- },
496
+ }},
485
497
  focusIndicator: {
486
498
  overlay: {
487
499
  opacity: "sys.state.focus"
@@ -1581,6 +1593,25 @@ var filter_spec_default = {
1581
1593
  opacity: "sys.state.pressed"
1582
1594
  }
1583
1595
  },
1596
+ focused: {
1597
+ overlay: {
1598
+ color: "label",
1599
+ opacity: "sys.state.focus"
1600
+ },
1601
+ focusRing: {
1602
+ composition: "outward",
1603
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1604
+ innerCounterRing: {
1605
+ width: "sys.borderWidth.hairline",
1606
+ color: "sys.color.focusInset"
1607
+ },
1608
+ outerRing: {
1609
+ width: "sys.borderWidth.thin",
1610
+ color: "sys.color.focus"
1611
+ }
1612
+ },
1613
+ 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."
1614
+ },
1584
1615
  disabled: {
1585
1616
  overlay: null,
1586
1617
  containerOpacity: "sys.state.disabled",
@@ -1688,6 +1719,25 @@ var tag_spec_default = {
1688
1719
  opacity: "sys.state.pressed"
1689
1720
  }
1690
1721
  },
1722
+ focused: {
1723
+ overlay: {
1724
+ color: "label",
1725
+ opacity: "sys.state.focus"
1726
+ },
1727
+ focusRing: {
1728
+ composition: "outward",
1729
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1730
+ innerCounterRing: {
1731
+ width: "sys.borderWidth.hairline",
1732
+ color: "sys.color.focusInset"
1733
+ },
1734
+ outerRing: {
1735
+ width: "sys.borderWidth.thin",
1736
+ color: "sys.color.focus"
1737
+ }
1738
+ },
1739
+ 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."
1740
+ },
1691
1741
  disabled: {
1692
1742
  overlay: null,
1693
1743
  containerOpacity: "sys.state.disabled",
@@ -1813,6 +1863,25 @@ var toggle_spec_default = {
1813
1863
  opacity: "sys.state.pressed"
1814
1864
  }
1815
1865
  },
1866
+ focused: {
1867
+ overlay: {
1868
+ color: "label",
1869
+ opacity: "sys.state.focus"
1870
+ },
1871
+ focusRing: {
1872
+ composition: "outward",
1873
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
1874
+ innerCounterRing: {
1875
+ width: "sys.borderWidth.hairline",
1876
+ color: "sys.color.focusInset"
1877
+ },
1878
+ outerRing: {
1879
+ width: "sys.borderWidth.thin",
1880
+ color: "sys.color.focus"
1881
+ }
1882
+ },
1883
+ 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."
1884
+ },
1816
1885
  disabled: {
1817
1886
  overlay: null,
1818
1887
  containerOpacity: "sys.state.disabled",
@@ -1847,7 +1916,7 @@ var FORCEABLE_STATES3 = /* @__PURE__ */ new Set(["hovered", "pressed", "focused"
1847
1916
  var SPECS = {
1848
1917
  filter: filter_spec_default,
1849
1918
  tag: tag_spec_default,
1850
- "toggle": toggle_spec_default
1919
+ toggle: toggle_spec_default
1851
1920
  };
1852
1921
  function pickAppearance(spec, appearance) {
1853
1922
  const appearances = spec.appearances || {};
@@ -1989,16 +2058,56 @@ function ButtonToggle({ active = false, ...rest }) {
1989
2058
  }
1990
2059
  var VARIANTS = {
1991
2060
  fab: ButtonFab,
1992
- "icon": ButtonIcon,
1993
- "text": ButtonText,
1994
- "check": ButtonCheck,
1995
- "toolbar": ButtonToolbar,
1996
- "toggle": ButtonToggle
2061
+ icon: ButtonIcon,
2062
+ text: ButtonText,
2063
+ check: ButtonCheck,
2064
+ toolbar: ButtonToolbar,
2065
+ toggle: ButtonToggle
1997
2066
  };
1998
2067
  var Button = react.forwardRef(function Button2({ variant, ...rest }, ref) {
1999
2068
  const Impl = variant && VARIANTS[variant] || ButtonStandard;
2000
2069
  return /* @__PURE__ */ jsxRuntime.jsx(Impl, { ref, ...rest });
2001
2070
  });
2071
+ var FOCUSABLE_SELECTOR = [
2072
+ "a[href]",
2073
+ "button",
2074
+ "input",
2075
+ "select",
2076
+ "textarea",
2077
+ '[tabindex]:not([tabindex="-1"])'
2078
+ ].join(",");
2079
+ function useFocusTrap(ref, active) {
2080
+ react.useEffect(() => {
2081
+ if (!active) return void 0;
2082
+ const onKey = (e) => {
2083
+ if (e.key !== "Tab") return;
2084
+ const container = ref.current;
2085
+ if (!container) return;
2086
+ const focusable = Array.from(
2087
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
2088
+ ).filter((el) => !el.disabled && el.offsetParent !== null);
2089
+ if (focusable.length === 0) {
2090
+ e.preventDefault();
2091
+ container.focus({ preventScroll: true });
2092
+ return;
2093
+ }
2094
+ const first = focusable[0];
2095
+ const last = focusable[focusable.length - 1];
2096
+ const activeEl = document.activeElement;
2097
+ if (e.shiftKey) {
2098
+ if (activeEl === first || !container.contains(activeEl)) {
2099
+ e.preventDefault();
2100
+ last.focus({ preventScroll: true });
2101
+ }
2102
+ } else if (activeEl === last || !container.contains(activeEl)) {
2103
+ e.preventDefault();
2104
+ first.focus({ preventScroll: true });
2105
+ }
2106
+ };
2107
+ document.addEventListener("keydown", onKey);
2108
+ return () => document.removeEventListener("keydown", onKey);
2109
+ }, [ref, active]);
2110
+ }
2002
2111
  function useBodyScrollLock(locked) {
2003
2112
  react.useEffect(() => {
2004
2113
  if (!locked) return void 0;
@@ -2038,6 +2147,7 @@ function BottomSheet({
2038
2147
  const lastFocusedRef = react.useRef(null);
2039
2148
  const [overflowing, setOverflowing] = react.useState(false);
2040
2149
  useBodyScrollLock(open && !inline);
2150
+ useFocusTrap(cardRef, open && !inline);
2041
2151
  react.useEffect(() => {
2042
2152
  if (!open || inline) return void 0;
2043
2153
  const vv = typeof window !== "undefined" ? window.visualViewport : null;
@@ -2101,6 +2211,7 @@ function BottomSheet({
2101
2211
  className: "chorus-bottom-sheet__card",
2102
2212
  role: "dialog",
2103
2213
  "aria-modal": "true",
2214
+ tabIndex: -1,
2104
2215
  "aria-label": ariaLabel ?? title,
2105
2216
  onClick: (e) => e.stopPropagation(),
2106
2217
  ...rest,
@@ -2155,6 +2266,35 @@ function Bubble({
2155
2266
  }
2156
2267
  );
2157
2268
  }
2269
+ function ButtonGroup({
2270
+ variant = "inline",
2271
+ orientation = "horizontal",
2272
+ label,
2273
+ children,
2274
+ className,
2275
+ "aria-label": ariaLabel,
2276
+ ...rest
2277
+ }) {
2278
+ const docked = variant === "docked";
2279
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2280
+ "div",
2281
+ {
2282
+ className: joinClasses(
2283
+ "chorus-button-group",
2284
+ docked && "chorus-button-group--docked",
2285
+ orientation === "vertical" && "chorus-button-group--vertical",
2286
+ className
2287
+ ),
2288
+ role: "group",
2289
+ "aria-label": ariaLabel,
2290
+ ...rest,
2291
+ children: [
2292
+ label != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chorus-button-group__label", children: label }) : null,
2293
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chorus-button-group__row", children })
2294
+ ]
2295
+ }
2296
+ );
2297
+ }
2158
2298
 
2159
2299
  // ../../schema/components/header/main.spec.json
2160
2300
  var main_spec_default = {
@@ -2315,6 +2455,7 @@ function EntryRow({
2315
2455
  "span",
2316
2456
  {
2317
2457
  className: "chorus-entry-row__trailing",
2458
+ "data-nested-action": "",
2318
2459
  onClick: (e) => e.stopPropagation(),
2319
2460
  onKeyDown: (e) => e.stopPropagation(),
2320
2461
  children: trailing
@@ -2389,6 +2530,26 @@ function List({
2389
2530
  if (isRadio) onChange == null ? void 0 : onChange(item.value);
2390
2531
  (_a = item.onClick) == null ? void 0 : _a.call(item);
2391
2532
  };
2533
+ const rowMain = isEntry ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2534
+ isRadio ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading", children: /* @__PURE__ */ jsxRuntime.jsx(RadioIndicator, { selected }) }) : item.thumbnail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading chorus-list__leading--image", children: /* @__PURE__ */ jsxRuntime.jsx(Thumbnail, { size: 40, ...item.thumbnail }) }) : item.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading chorus-list__leading--icon", "aria-hidden": "true", children: item.icon }) : null,
2535
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "chorus-list__label-col", children: [
2536
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "chorus-list__primary-row", children: [
2537
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__label", children: item.label }),
2538
+ item.count != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__count", children: item.count }) : null
2539
+ ] }),
2540
+ item.supportingText ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__supporting", children: item.supportingText }) : null
2541
+ ] }),
2542
+ item.nav && !item.trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__trailing chorus-list__nav-chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { size: 16 }) }) : item.trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx(
2543
+ "span",
2544
+ {
2545
+ className: "chorus-list__trailing",
2546
+ "data-nested-action": "",
2547
+ onClick: (e) => e.stopPropagation(),
2548
+ onKeyDown: (e) => e.stopPropagation(),
2549
+ children: item.trailingIcon
2550
+ }
2551
+ ) : null
2552
+ ] });
2392
2553
  return /* @__PURE__ */ jsxRuntime.jsx(
2393
2554
  "div",
2394
2555
  {
@@ -2423,25 +2584,26 @@ function List({
2423
2584
  description: item.description,
2424
2585
  trailing: item.trailingIcon
2425
2586
  }
2426
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2427
- isRadio ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading", children: /* @__PURE__ */ jsxRuntime.jsx(RadioIndicator, { selected }) }) : item.thumbnail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading chorus-list__leading--image", children: /* @__PURE__ */ jsxRuntime.jsx(Thumbnail, { size: 40, ...item.thumbnail }) }) : item.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__leading chorus-list__leading--icon", "aria-hidden": "true", children: item.icon }) : null,
2428
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "chorus-list__label-col", children: [
2429
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "chorus-list__primary-row", children: [
2430
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__label", children: item.label }),
2431
- item.count != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__count", children: item.count }) : null
2432
- ] }),
2433
- item.supportingText ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__supporting", children: item.supportingText }) : null
2434
- ] }),
2435
- item.nav && !item.trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__trailing chorus-list__nav-chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { size: 16 }) }) : item.trailingIcon ? /* @__PURE__ */ jsxRuntime.jsx(
2436
- "span",
2437
- {
2438
- className: "chorus-list__trailing",
2439
- onClick: (e) => e.stopPropagation(),
2440
- onKeyDown: (e) => e.stopPropagation(),
2441
- children: item.trailingIcon
2442
- }
2443
- ) : null
2444
- ] })
2587
+ ) : item.banner ? (
2588
+ // Embedded-Banner row the text group stacks over a Banner
2589
+ // that spans the row's full content width, 8px (stack.xs)
2590
+ // below. The Banner is a nested action region: its own
2591
+ // controls never commit the row, and the row's hover/press
2592
+ // overlay is suppressed over it (see styles.css).
2593
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "chorus-list__stack", children: [
2594
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-list__row-main", children: rowMain }),
2595
+ /* @__PURE__ */ jsxRuntime.jsx(
2596
+ "span",
2597
+ {
2598
+ className: "chorus-list__banner",
2599
+ "data-nested-action": "",
2600
+ onClick: (e) => e.stopPropagation(),
2601
+ onKeyDown: (e) => e.stopPropagation(),
2602
+ children: item.banner
2603
+ }
2604
+ )
2605
+ ] })
2606
+ ) : rowMain
2445
2607
  },
2446
2608
  item.value ?? idx
2447
2609
  );
@@ -2701,6 +2863,7 @@ function Dialog({
2701
2863
  const cardRef = react.useRef(null);
2702
2864
  const lastFocusedRef = react.useRef(null);
2703
2865
  useBodyScrollLock(open && !inline);
2866
+ useFocusTrap(cardRef, open && !inline);
2704
2867
  react.useEffect(() => {
2705
2868
  var _a;
2706
2869
  if (!open) return void 0;
@@ -2746,6 +2909,7 @@ function Dialog({
2746
2909
  className: joinClasses("chorus-dialog__card", image && "chorus-dialog__card--with-image"),
2747
2910
  role: "dialog",
2748
2911
  "aria-modal": "true",
2912
+ tabIndex: -1,
2749
2913
  "aria-label": ariaLabel ?? title,
2750
2914
  onClick: (e) => e.stopPropagation(),
2751
2915
  ...rest,
@@ -2779,6 +2943,80 @@ function Divider({
2779
2943
  }
2780
2944
  );
2781
2945
  }
2946
+
2947
+ // ../../schema/components/empty-state/empty-state.spec.json
2948
+ var empty_state_spec_default = {
2949
+ sizing: {
2950
+ illustrationSize: "ref.space.600",
2951
+ illustrationColor: "sys.color.onSurfaceVariant",
2952
+ illustrationGap: "sys.layout.stack.sm",
2953
+ headlineTypo: "sys.typo.heading.sm",
2954
+ headlineColor: "sys.color.onSurface",
2955
+ bodyTypo: "sys.typo.body.sm",
2956
+ bodyColor: "sys.color.onSurfaceVariant",
2957
+ bodyGap: "sys.layout.stack.2xs",
2958
+ actionGap: "sys.layout.stack.md"
2959
+ }};
2960
+ var s = empty_state_spec_default.sizing;
2961
+ var CONTAINER_STYLE = {
2962
+ "--empty-state-illustration-gap": tokenToCss(s.illustrationGap),
2963
+ "--empty-state-body-gap": tokenToCss(s.bodyGap),
2964
+ "--empty-state-action-gap": tokenToCss(s.actionGap)
2965
+ };
2966
+ var ILLUSTRATION_STYLE = {
2967
+ "--empty-state-illustration-size": tokenToCss(s.illustrationSize),
2968
+ "--empty-state-illustration-color": tokenToCss(s.illustrationColor)
2969
+ };
2970
+ var HEADLINE_STYLE = {
2971
+ "--empty-state-headline-color": tokenToCss(s.headlineColor),
2972
+ ...typoStyles(s.headlineTypo)
2973
+ };
2974
+ var BODY_STYLE = {
2975
+ "--empty-state-body-color": tokenToCss(s.bodyColor),
2976
+ ...typoStyles(s.bodyTypo)
2977
+ };
2978
+ function EmptyState({
2979
+ illustration,
2980
+ headline,
2981
+ body,
2982
+ action,
2983
+ className,
2984
+ style,
2985
+ ...rest
2986
+ }) {
2987
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2988
+ "div",
2989
+ {
2990
+ role: "status",
2991
+ className: joinClasses("chorus-empty-state", className),
2992
+ style: { ...CONTAINER_STYLE, ...style },
2993
+ ...rest,
2994
+ children: [
2995
+ illustration ? /* @__PURE__ */ jsxRuntime.jsx(
2996
+ "span",
2997
+ {
2998
+ className: "chorus-empty-state__illustration",
2999
+ "aria-hidden": "true",
3000
+ style: ILLUSTRATION_STYLE,
3001
+ children: illustration
3002
+ }
3003
+ ) : null,
3004
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "chorus-empty-state__headline", style: HEADLINE_STYLE, children: headline }),
3005
+ body ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "chorus-empty-state__body", style: BODY_STYLE, children: body }) : null,
3006
+ action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chorus-empty-state__action", children: /* @__PURE__ */ jsxRuntime.jsx(
3007
+ Button,
3008
+ {
3009
+ appearance: "primary",
3010
+ onClick: action.onClick ?? (action.href ? () => {
3011
+ window.location.assign(action.href);
3012
+ } : void 0),
3013
+ children: action.label
3014
+ }
3015
+ ) }) : null
3016
+ ]
3017
+ }
3018
+ );
3019
+ }
2782
3020
  var TabsContext = react.createContext({
2783
3021
  variant: "underline",
2784
3022
  value: null,
@@ -2901,19 +3139,19 @@ function TabsUnderline({ className, style, children, ...rest }) {
2901
3139
  useScrollOverflow(ref);
2902
3140
  useSlidingIndicator(ref, indicatorRef, value);
2903
3141
  useFullBleedGuard(ref, "Tabs");
2904
- const s = underline_spec_default.sizing;
3142
+ const s2 = underline_spec_default.sizing;
2905
3143
  const composedStyle = {
2906
- "--tabs-container-padding-inline": tokenToCss(s.containerPaddingInline),
2907
- "--tabs-tab-min-height": tokenToCss(s.minHeight),
2908
- "--tabs-tab-padding-block": tokenToCss(s.paddingBlock),
2909
- "--tabs-tab-padding-inline": tokenToCss(s.paddingInline),
2910
- "--tabs-inter-tab-gap": tokenToCss(s.interTabGap),
2911
- "--tabs-slot-gap": tokenToCss(s.slotGap),
2912
- "--tabs-icon-size": tokenToCss(s.iconSize),
2913
- "--tabs-indicator-height": tokenToCss(s.indicatorHeight),
2914
- "--tabs-divider-width": tokenToCss(s.dividerWidth),
2915
- "--tabs-divider-color": tokenToCss(s.dividerColor),
2916
- "--tabs-fade-width": tokenToCss(s.fadeWidth),
3144
+ "--tabs-container-padding-inline": tokenToCss(s2.containerPaddingInline),
3145
+ "--tabs-tab-min-height": tokenToCss(s2.minHeight),
3146
+ "--tabs-tab-padding-block": tokenToCss(s2.paddingBlock),
3147
+ "--tabs-tab-padding-inline": tokenToCss(s2.paddingInline),
3148
+ "--tabs-inter-tab-gap": tokenToCss(s2.interTabGap),
3149
+ "--tabs-slot-gap": tokenToCss(s2.slotGap),
3150
+ "--tabs-icon-size": tokenToCss(s2.iconSize),
3151
+ "--tabs-indicator-height": tokenToCss(s2.indicatorHeight),
3152
+ "--tabs-divider-width": tokenToCss(s2.dividerWidth),
3153
+ "--tabs-divider-color": tokenToCss(s2.dividerColor),
3154
+ "--tabs-fade-width": tokenToCss(s2.fadeWidth),
2917
3155
  "--tabs-label-unselected": tokenToCss(underline_spec_default.selectionStates.unselected.label),
2918
3156
  "--tabs-label-selected": tokenToCss(underline_spec_default.selectionStates.selected.label),
2919
3157
  "--tabs-indicator-color": tokenToCss(underline_spec_default.selectionStates.selected.indicator),
@@ -2925,7 +3163,7 @@ function TabsUnderline({ className, style, children, ...rest }) {
2925
3163
  "--tabs-focus-outer-color": tokenToCss(underline_spec_default.focusIndicator.ring.outerColor),
2926
3164
  "--tabs-focus-inset-width": tokenToCss(underline_spec_default.focusIndicator.ring.insetWidth),
2927
3165
  "--tabs-focus-inset-color": tokenToCss(underline_spec_default.focusIndicator.ring.insetColor),
2928
- ...typoStyles(s.labelTypo),
3166
+ ...typoStyles(s2.labelTypo),
2929
3167
  ...style
2930
3168
  };
2931
3169
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2987,12 +3225,12 @@ function TabsSegmented({ className, style, children, ...rest }) {
2987
3225
  const ref = react.useRef(null);
2988
3226
  useScrollOverflow(ref);
2989
3227
  useFullBleedGuard(ref, "Tabs");
2990
- const s = segmented_spec_default.sizing;
3228
+ const s2 = segmented_spec_default.sizing;
2991
3229
  const composedStyle = {
2992
- "--tabs-container-padding-block": tokenToCss(s.containerPaddingBlock),
2993
- "--tabs-container-padding-inline": tokenToCss(s.containerPaddingInline),
2994
- "--tabs-inter-segment-gap": tokenToCss(s.interSegmentGap),
2995
- "--tabs-fade-width": tokenToCss(s.fadeWidth),
3230
+ "--tabs-container-padding-block": tokenToCss(s2.containerPaddingBlock),
3231
+ "--tabs-container-padding-inline": tokenToCss(s2.containerPaddingInline),
3232
+ "--tabs-inter-segment-gap": tokenToCss(s2.interSegmentGap),
3233
+ "--tabs-fade-width": tokenToCss(s2.fadeWidth),
2996
3234
  ...style
2997
3235
  };
2998
3236
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3189,13 +3427,13 @@ function Metadata({
3189
3427
  const hasMeta = Array.isArray(meta) && meta.length > 0;
3190
3428
  const hasSubtitle = !hasMeta && subtitle != null && subtitle !== "";
3191
3429
  if (variant === "compact") {
3192
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: joinClasses("chorus-metadata", "chorus-metadata--compact", className), ...rest, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chorus-metadata__meta", children: [
3193
- hasMeta ? metaParts(meta) : null,
3194
- timestamp ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3195
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-metadata__dot", "aria-hidden": "true", children: "\xB7" }),
3196
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-metadata__timestamp", children: timestamp })
3197
- ] }) : null
3198
- ] }) });
3430
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: joinClasses("chorus-metadata", "chorus-metadata--compact", className), ...rest, children: [
3431
+ avatar ? /* @__PURE__ */ jsxRuntime.jsx(Thumbnail, { size: 32, ...avatar }) : null,
3432
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chorus-metadata__meta", children: [
3433
+ hasMeta ? metaParts(meta) : null,
3434
+ timestamp ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-metadata__timestamp", children: timestamp }) : null
3435
+ ] })
3436
+ ] });
3199
3437
  }
3200
3438
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: joinClasses("chorus-metadata", className), ...rest, children: [
3201
3439
  /* @__PURE__ */ jsxRuntime.jsx(Thumbnail, { size: 32, ...avatar ?? { alt: typeof name === "string" ? name : "" } }),
@@ -3486,6 +3724,33 @@ function FeedAd({
3486
3724
  ) : null
3487
3725
  ] });
3488
3726
  }
3727
+ function Pagination({
3728
+ count = 0,
3729
+ activeIndex = 0,
3730
+ className,
3731
+ ...rest
3732
+ }) {
3733
+ if (count < 2) return null;
3734
+ const active = Math.max(0, Math.min(count - 1, activeIndex));
3735
+ return /* @__PURE__ */ jsxRuntime.jsx(
3736
+ "span",
3737
+ {
3738
+ className: joinClasses("chorus-pagination", className),
3739
+ "aria-hidden": "true",
3740
+ ...rest,
3741
+ children: Array.from({ length: count }, (_, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3742
+ "span",
3743
+ {
3744
+ className: joinClasses(
3745
+ "chorus-pagination__dot",
3746
+ idx === active && "chorus-pagination__dot--active"
3747
+ )
3748
+ },
3749
+ idx
3750
+ ))
3751
+ }
3752
+ );
3753
+ }
3489
3754
  var MAX_CARDS = 5;
3490
3755
  function PostCarousel({
3491
3756
  items = [],
@@ -3546,16 +3811,7 @@ function PostCarousel({
3546
3811
  ))
3547
3812
  }
3548
3813
  ),
3549
- cards.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chorus-post-carousel__pagination", "aria-hidden": "true", children: cards.map((_, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3550
- "span",
3551
- {
3552
- className: joinClasses(
3553
- "chorus-post-carousel__dot",
3554
- idx === activeIndex && "chorus-post-carousel__dot--active"
3555
- )
3556
- },
3557
- idx
3558
- )) }) : null
3814
+ /* @__PURE__ */ jsxRuntime.jsx(Pagination, { count: cards.length, activeIndex })
3559
3815
  ]
3560
3816
  }
3561
3817
  );
@@ -3706,16 +3962,7 @@ function ProfileCarousel({
3706
3962
  ))
3707
3963
  }
3708
3964
  ),
3709
- cards.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chorus-profile-carousel__pagination", "aria-hidden": "true", children: cards.map((_, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3710
- "span",
3711
- {
3712
- className: joinClasses(
3713
- "chorus-profile-carousel__dot",
3714
- idx === activeIndex && "chorus-profile-carousel__dot--active"
3715
- )
3716
- },
3717
- idx
3718
- )) }) : null
3965
+ /* @__PURE__ */ jsxRuntime.jsx(Pagination, { count: cards.length, activeIndex })
3719
3966
  ]
3720
3967
  }
3721
3968
  );
@@ -4319,15 +4566,23 @@ var input_spec_default = {
4319
4566
  overlay: {
4320
4567
  color: "text",
4321
4568
  opacity: "sys.state.pressed"
4322
- }
4569
+ },
4570
+ 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."
4323
4571
  },
4324
4572
  active: {
4573
+ isFocusState: true,
4325
4574
  overlay: null,
4326
4575
  border: "borderActive",
4327
4576
  strokeWeight: "activeStrokeWeight",
4328
4577
  caret: "visible",
4329
4578
  showsClearWhenValue: true,
4330
- 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)."
4579
+ focusRing: {
4580
+ composition: "outward",
4581
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4582
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4583
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4584
+ },
4585
+ 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)."
4331
4586
  },
4332
4587
  disabled: {
4333
4588
  overlay: null,
@@ -4505,14 +4760,22 @@ var textarea_spec_default = {
4505
4760
  hovered: { overlay: null, border: "borderHover" },
4506
4761
  pressed: {
4507
4762
  border: "borderHover",
4508
- overlay: { color: "text", opacity: "sys.state.pressed" }
4763
+ overlay: { color: "text", opacity: "sys.state.pressed" },
4764
+ 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."
4509
4765
  },
4510
4766
  active: {
4767
+ isFocusState: true,
4511
4768
  overlay: null,
4512
4769
  border: "borderActive",
4513
4770
  strokeWeight: "activeStrokeWeight",
4514
4771
  caret: "visible",
4515
- note: "Stroke steps from `hairline` (1px) to `activeStrokeWeight` (2px) as an inset box-shadow \u2014 same pixel-stable contract as input."
4772
+ focusRing: {
4773
+ composition: "outward",
4774
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4775
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4776
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4777
+ },
4778
+ 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."
4516
4779
  },
4517
4780
  disabled: {
4518
4781
  overlay: null,
@@ -4642,15 +4905,23 @@ var search_spec_default = {
4642
4905
  overlay: {
4643
4906
  color: "text",
4644
4907
  opacity: "sys.state.pressed"
4645
- }
4908
+ },
4909
+ 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."
4646
4910
  },
4647
4911
  active: {
4912
+ isFocusState: true,
4648
4913
  overlay: null,
4649
4914
  border: "borderActive",
4650
4915
  strokeWeight: "activeStrokeWeight",
4651
4916
  caret: "visible",
4652
4917
  showsClearWhenValue: true,
4653
- 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)."
4918
+ focusRing: {
4919
+ composition: "outward",
4920
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
4921
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
4922
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
4923
+ },
4924
+ 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)."
4654
4925
  },
4655
4926
  disabled: {
4656
4927
  overlay: null,
@@ -4837,9 +5108,17 @@ var select_spec_default = {
4837
5108
  }
4838
5109
  },
4839
5110
  active: {
5111
+ isFocusState: true,
4840
5112
  overlay: null,
4841
5113
  border: "borderActive",
4842
- strokeWeight: "activeStrokeWeight"
5114
+ strokeWeight: "activeStrokeWeight",
5115
+ focusRing: {
5116
+ composition: "outward",
5117
+ layer: "::after overlay \u2014 position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
5118
+ innerCounterRing: { width: "sys.borderWidth.hairline", color: "sys.color.focusInset" },
5119
+ outerRing: { width: "sys.borderWidth.thin", color: "sys.color.focus" }
5120
+ },
5121
+ 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)."
4843
5122
  },
4844
5123
  disabled: {
4845
5124
  overlay: null,
@@ -5013,6 +5292,7 @@ function FormFieldBox({
5013
5292
  {
5014
5293
  type: "button",
5015
5294
  className: "chorus-field__clear",
5295
+ "data-nested-action": "",
5016
5296
  "aria-label": "Clear",
5017
5297
  onClick: handleClear,
5018
5298
  children: /* @__PURE__ */ jsxRuntime.jsx(XCircleFillIcon, {})
@@ -5107,7 +5387,7 @@ var FormFieldSelect = Select;
5107
5387
  var VARIANTS3 = {
5108
5388
  input: FormFieldInput,
5109
5389
  textarea: FormFieldTextarea,
5110
- "search": FormFieldSearchBar,
5390
+ search: FormFieldSearchBar,
5111
5391
  select: FormFieldSelect
5112
5392
  };
5113
5393
  function FormField({ variant = "input", ...rest }) {
@@ -5459,6 +5739,58 @@ function SkeletonGroup({
5459
5739
  }
5460
5740
  );
5461
5741
  }
5742
+
5743
+ // ../../schema/components/spinner/spinner.spec.json
5744
+ var spinner_spec_default = {
5745
+ sizes: {
5746
+ medium: {
5747
+ diameter: "sys.icon.lg",
5748
+ labelTypo: "sys.typo.body.sm",
5749
+ gap: "sys.layout.inline.sm"
5750
+ },
5751
+ small: {
5752
+ diameter: "sys.icon.md",
5753
+ labelTypo: "sys.typo.body.sm",
5754
+ gap: "sys.layout.inline.sm"
5755
+ }
5756
+ }};
5757
+ function sizingStyle4(spec, size) {
5758
+ const s2 = spec.sizes[size] ?? spec.sizes.medium;
5759
+ return {
5760
+ "--spinner-diameter": tokenToCss(s2.diameter),
5761
+ "--spinner-gap": tokenToCss(s2.gap)
5762
+ };
5763
+ }
5764
+ function Spinner({
5765
+ size = "medium",
5766
+ label,
5767
+ className,
5768
+ style,
5769
+ "aria-label": ariaLabel,
5770
+ ...rest
5771
+ }) {
5772
+ var _a;
5773
+ const labelTypo = ((_a = spinner_spec_default.sizes[size]) == null ? void 0 : _a.labelTypo) ?? spinner_spec_default.sizes.medium.labelTypo;
5774
+ const a11yLabel = label != null ? void 0 : ariaLabel ?? "Loading";
5775
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5776
+ "span",
5777
+ {
5778
+ role: "status",
5779
+ "aria-label": a11yLabel,
5780
+ className: joinClasses(
5781
+ "chorus-spinner",
5782
+ `chorus-spinner--${size}`,
5783
+ className
5784
+ ),
5785
+ style: { ...sizingStyle4(spinner_spec_default, size), ...style },
5786
+ ...rest,
5787
+ children: [
5788
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-spinner__arc", "aria-hidden": "true" }),
5789
+ label != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chorus-spinner__label", style: typoStyles(labelTypo), children: label }) : null
5790
+ ]
5791
+ }
5792
+ );
5793
+ }
5462
5794
  function StatusTag({
5463
5795
  appearance = "neutral",
5464
5796
  children,
@@ -5855,6 +6187,7 @@ exports.BottomNav = TabBar;
5855
6187
  exports.BottomSheet = BottomSheet;
5856
6188
  exports.Bubble = Bubble;
5857
6189
  exports.Button = Button;
6190
+ exports.ButtonGroup = ButtonGroup;
5858
6191
  exports.Carousel = Carousel;
5859
6192
  exports.ChannelList = SuggestionList;
5860
6193
  exports.ChannelRail = AvatarRail;
@@ -5863,6 +6196,7 @@ exports.Dialog = Dialog;
5863
6196
  exports.DirectoryList = DirectoryList;
5864
6197
  exports.Divider = Divider;
5865
6198
  exports.Drawer = BottomSheet;
6199
+ exports.EmptyState = EmptyState;
5866
6200
  exports.Feed = Feed;
5867
6201
  exports.FeedAd = FeedAd;
5868
6202
  exports.FeedGroup = FeedGroup;
@@ -5877,6 +6211,7 @@ exports.NavCardGroup = NavCardGroup;
5877
6211
  exports.NavList = NavList;
5878
6212
  exports.NavigationBar = NavigationBar;
5879
6213
  exports.PageShell = PageShell;
6214
+ exports.Pagination = Pagination;
5880
6215
  exports.PostCarousel = PostCarousel;
5881
6216
  exports.ProfileCarousel = ProfileCarousel;
5882
6217
  exports.ProfileHeader = ProfileHeader;
@@ -5890,6 +6225,7 @@ exports.SideSheet = SideSheet;
5890
6225
  exports.SideSheetGroup = SideSheetGroup;
5891
6226
  exports.Skeleton = Skeleton;
5892
6227
  exports.SkeletonGroup = SkeletonGroup;
6228
+ exports.Spinner = Spinner;
5893
6229
  exports.StatusTag = StatusTag;
5894
6230
  exports.SubHeader = SubHeader;
5895
6231
  exports.SuggestionList = SuggestionList;