@unpunnyfuns/swatchbook-blocks 0.60.9 → 0.61.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.
package/dist/index.mjs CHANGED
@@ -78,10 +78,6 @@ function hexToComponents(hex) {
78
78
  parseInt(expanded.slice(4, 6), 16) / 255
79
79
  ];
80
80
  }
81
- /**
82
- * Map Terrazzo's canonical CSS Color 4 space identifiers to the shorter
83
- * identifiers colorjs.io registers. Only the ones that differ need an entry.
84
- */
85
81
  const COLORJS_SPACE_ALIASES = {
86
82
  "display-p3": "p3",
87
83
  "a98-rgb": "a98rgb",
@@ -114,16 +110,16 @@ function formatHex(color, alpha) {
114
110
  value: formatRgb(color, alpha).value,
115
111
  outOfGamut: true
116
112
  };
117
- const r = clamp255(coord(srgb, 0));
118
- const g = clamp255(coord(srgb, 1));
119
- const b = clamp255(coord(srgb, 2));
113
+ const r = unitToByte(coord(srgb, 0));
114
+ const g = unitToByte(coord(srgb, 1));
115
+ const b = unitToByte(coord(srgb, 2));
120
116
  const base = `#${toHexByte(r)}${toHexByte(g)}${toHexByte(b)}`;
121
117
  if (alpha >= 1) return {
122
118
  value: base,
123
119
  outOfGamut: false
124
120
  };
125
121
  return {
126
- value: `${base}${toHexByte(clamp255(alpha))}`,
122
+ value: `${base}${toHexByte(unitToByte(alpha))}`,
127
123
  outOfGamut: false
128
124
  };
129
125
  }
@@ -153,7 +149,7 @@ function formatOklch(color, alpha) {
153
149
  outOfGamut: false
154
150
  };
155
151
  }
156
- function clamp255(n) {
152
+ function unitToByte(n) {
157
153
  return Math.max(0, Math.min(255, Math.round(n * 255)));
158
154
  }
159
155
  function clampUnit(n) {
@@ -189,6 +185,13 @@ function stringifyFallback(value, fallback) {
189
185
  }
190
186
  //#endregion
191
187
  //#region src/internal/styles.tsx
188
+ /**
189
+ * Chrome-style primitives shared across every block. Kept as JS exports
190
+ * for the inline-style sites that still compose them into per-block style
191
+ * objects (e.g. TokenNavigator's `typePill` that builds on the shared
192
+ * pill base). The pure direct-reference chrome — surface wrapper, caption,
193
+ * empty-state — lives in `styles.css` and is applied via class names.
194
+ */
192
195
  const TEXT_MUTED = "var(--swatchbook-text-muted, CanvasText)";
193
196
  const SURFACE_RAISED = "var(--swatchbook-surface-raised, Canvas)";
194
197
  const SURFACE_MUTED = "var(--swatchbook-surface-muted, rgba(128,128,128,0.15))";
@@ -196,7 +199,7 @@ const BORDER_FAINT = `1px solid var(--swatchbook-border-default, rgba(128,128,12
196
199
  const BORDER_STRONG = `1px solid var(--swatchbook-border-default, rgba(128,128,128,0.3))`;
197
200
  /**
198
201
  * Inner content for a block's "nothing to render" state. Call sites wrap
199
- * it in their own block wrapper (which already carries `themeAttrs`), so
202
+ * it in their own block wrapper (which already carries `blockWrapperAttrs`), so
200
203
  * the message itself just needs the muted type.
201
204
  */
202
205
  function EmptyState({ children }) {
@@ -243,11 +246,6 @@ function ensureSubscribed$1() {
243
246
  channel.on("updateGlobals", onGlobals);
244
247
  channel.on("setGlobals", onGlobals);
245
248
  }
246
- /**
247
- * Subscribe at module load so the `SET_GLOBALS` emission from preview init
248
- * lands in our snapshot before any block renders. Running `useSyncExternalStore`'s
249
- * `subscribe` lazily on first hook call would miss the event in most cases.
250
- */
251
249
  ensureSubscribed$1();
252
250
  function subscribe$1(cb) {
253
251
  ensureSubscribed$1();
@@ -293,7 +291,7 @@ function useActiveTheme() {
293
291
  * Active axis tuple for the current story/docs render — `Record<axisName,
294
292
  * contextName>`. Derived from the same input as {@link ThemeContext}; split
295
293
  * out so consumers needing per-axis info (toolbar, panel, tuple-aware
296
- * blocks) don't have to reparse the composed permutation ID.
294
+ * blocks) don't have to reparse the composed theme name.
297
295
  */
298
296
  const AxesContext = createContext({});
299
297
  function useActiveAxes() {
@@ -393,25 +391,10 @@ function defaultTuple$1(axes) {
393
391
  for (const axis of axes) out[axis.name] = axis.default;
394
392
  return out;
395
393
  }
396
- /**
397
- * Build a `resolveAt` accessor backed by the token graph. Returns an
398
- * empty resolver when no graph is present (test stubs, partial
399
- * snapshots). Stable identity when memoized on `tokenGraph` — the
400
- * graph is a module-level virtual-module export so its reference stays
401
- * constant for the lifetime of the iframe.
402
- */
403
394
  function makeResolveAt(graph) {
404
395
  if (!graph) return () => ({});
405
396
  return (tuple) => resolveAllAt(graph, tuple);
406
397
  }
407
- /**
408
- * Build the `resolveAt` accessor for a snapshot. Prefers the
409
- * snapshot's own `resolveAt` (the addon's preview decorator
410
- * pre-builds one at module load — see `previewResolveAt` in
411
- * `packages/addon/src/preview.tsx`), otherwise builds one from
412
- * `tokenGraph`. Hand-built snapshots can omit `resolveAt`;
413
- * the graph-backed fallback covers them.
414
- */
415
398
  function snapshotResolveAt(snapshot) {
416
399
  if (snapshot.resolveAt) return snapshot.resolveAt;
417
400
  return makeResolveAt(snapshot.tokenGraph);
@@ -458,12 +441,6 @@ function useProject() {
458
441
  cssVarPrefix: cssVarPrefix ?? "",
459
442
  listing: listing ?? {},
460
443
  varianceByPath: derivedVarianceByPath,
461
- tokenGraph: tokenGraph ?? {
462
- nodes: {},
463
- axes: [],
464
- axisDefaults: {},
465
- axisContexts: {}
466
- },
467
444
  resolveAt
468
445
  };
469
446
  }, [
@@ -482,16 +459,9 @@ function useProject() {
482
459
  return providerData ?? fallback;
483
460
  }
484
461
  function useVirtualModuleFallback(enabled) {
485
- const contextPermutation = useActiveTheme();
462
+ const contextThemeName = useActiveTheme();
486
463
  const contextAxes = useActiveAxes();
487
464
  const channelGlobals = useChannelGlobals();
488
- /**
489
- * Subscribe to the live token snapshot rather than reading the virtual
490
- * module's module-level exports directly. Initial values come from
491
- * `virtual:swatchbook/tokens` at load time; subsequent dev-time edits
492
- * flow through the addon's HMR channel and update this snapshot in
493
- * place so blocks re-render without a full preview reload.
494
- */
495
465
  const tokens = useTokenSnapshot();
496
466
  useEffect(() => {
497
467
  if (!enabled) return;
@@ -504,7 +474,7 @@ function useVirtualModuleFallback(enabled) {
504
474
  channelGlobals.axes,
505
475
  tokens.axes
506
476
  ]);
507
- const activeTheme = contextPermutation || tupleToName(tokens.axes, activeAxes);
477
+ const activeTheme = contextThemeName || tupleToName(tokens.axes, activeAxes);
508
478
  const resolveAt = useMemo(() => makeResolveAt(tokens.tokenGraph), [tokens.tokenGraph]);
509
479
  const fallbackVarianceByPath = useMemo(() => {
510
480
  const graph = tokens.tokenGraph;
@@ -522,7 +492,6 @@ function useVirtualModuleFallback(enabled) {
522
492
  cssVarPrefix: tokens.cssVarPrefix,
523
493
  listing: tokens.listing,
524
494
  varianceByPath: fallbackVarianceByPath,
525
- tokenGraph: tokens.tokenGraph,
526
495
  resolveAt
527
496
  }), [
528
497
  activeTheme,
@@ -532,7 +501,6 @@ function useVirtualModuleFallback(enabled) {
532
501
  tokens.cssVarPrefix,
533
502
  tokens.listing,
534
503
  fallbackVarianceByPath,
535
- tokens.tokenGraph,
536
504
  resolveAt
537
505
  ]);
538
506
  }
@@ -597,14 +565,6 @@ function BorderSample({ path }) {
597
565
  * override block chrome without relying on hashed class names).
598
566
  */
599
567
  const BLOCK_ATTR = "data-swatchbook-block";
600
- /**
601
- * Opt-out class that Storybook's `.sbdocs` stylesheet uses to self-exclude
602
- * on MDX docs pages — every `.sbdocs` house rule is wrapped in
603
- * `:not(.sb-unstyled, .sb-unstyled *)`, so any descendant of a `.sb-unstyled`
604
- * container is left alone. Stamped onto every block wrapper so blocks
605
- * render identically in MDX docs and regular stories without fighting
606
- * cascade specificity.
607
- */
608
568
  const WRAPPER_CLASSES = "sb-unstyled sb-block";
609
569
  /**
610
570
  * Spread helper for the common block wrapper. Returns:
@@ -622,7 +582,7 @@ const WRAPPER_CLASSES = "sb-unstyled sb-block";
622
582
  * MDX docs house styles self-exclude the subtree, plus `sb-block`
623
583
  * which carries the shared chrome from `internal/styles.css`.
624
584
  */
625
- function themeAttrs(prefix, tuple) {
585
+ function blockWrapperAttrs(prefix, tuple) {
626
586
  return {
627
587
  ...perAxisAttrs(prefix, tuple),
628
588
  [BLOCK_ATTR]: "",
@@ -728,11 +688,6 @@ function toMagnitude(v) {
728
688
  }
729
689
  return NaN;
730
690
  }
731
- /**
732
- * Coerce a possibly-null/undefined number to 0 — `coords` returns
733
- * `(number | null)[]` and `noUncheckedIndexedAccess` adds `undefined`
734
- * on top. `typeof` narrows the union for the comparator below.
735
- */
736
691
  function safeNumber(v) {
737
692
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
738
693
  }
@@ -807,14 +762,14 @@ function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
807
762
  ]);
808
763
  const captionText = caption ?? `${rows.length} border${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
809
764
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
810
- ...themeAttrs(cssVarPrefix, activeAxes),
765
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
811
766
  children: /* @__PURE__ */ jsx("div", {
812
767
  className: "sb-block__empty",
813
768
  children: "No border tokens match this filter."
814
769
  })
815
770
  });
816
771
  return /* @__PURE__ */ jsxs("div", {
817
- ...themeAttrs(cssVarPrefix, activeAxes),
772
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
818
773
  children: [/* @__PURE__ */ jsx("div", {
819
774
  className: "sb-block__caption",
820
775
  children: captionText
@@ -861,10 +816,6 @@ function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
861
816
  }
862
817
  //#endregion
863
818
  //#region src/ColorPalette.tsx
864
- /**
865
- * Count segments in the filter before the first glob (`*` / `**`).
866
- * `color.*` → 2; `color.surface.*` → 3; `color` → 1; undefined → 0.
867
- */
868
819
  function fixedPrefixLength(filter) {
869
820
  if (!filter) return 0;
870
821
  const segments = filter.split(".");
@@ -922,14 +873,14 @@ function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "as
922
873
  const totalCount = groups.reduce((acc, [, swatches]) => acc + swatches.length, 0);
923
874
  const captionText = caption ?? `${totalCount} color${totalCount === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
924
875
  if (totalCount === 0) return /* @__PURE__ */ jsx("div", {
925
- ...themeAttrs(cssVarPrefix, activeAxes),
876
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
926
877
  children: /* @__PURE__ */ jsx("div", {
927
878
  className: "sb-block__empty",
928
879
  children: "No color tokens match this filter."
929
880
  })
930
881
  });
931
882
  return /* @__PURE__ */ jsxs("div", {
932
- ...themeAttrs(cssVarPrefix, activeAxes),
883
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
933
884
  children: [/* @__PURE__ */ jsx("div", {
934
885
  className: "sb-block__caption",
935
886
  children: captionText
@@ -1115,14 +1066,14 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
1115
1066
  const matchSuffix = searchable && query.trim() !== "" ? ` · ${visibleGroups.length} matching "${query.trim()}"` : "";
1116
1067
  const captionText = caption ?? `${totalTokens} color${totalTokens === 1 ? "" : "s"} across ${groups.length} group${groups.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""}${matchSuffix} · ${activeTheme}`;
1117
1068
  if (groups.length === 0) return /* @__PURE__ */ jsx("div", {
1118
- ...themeAttrs(cssVarPrefix, activeAxes),
1069
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1119
1070
  children: /* @__PURE__ */ jsx("div", {
1120
1071
  className: "sb-block__empty",
1121
1072
  children: "No color tokens match this filter."
1122
1073
  })
1123
1074
  });
1124
1075
  return /* @__PURE__ */ jsxs("div", {
1125
- ...themeAttrs(cssVarPrefix, activeAxes),
1076
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1126
1077
  children: [searchable && /* @__PURE__ */ jsx("div", {
1127
1078
  className: "sb-color-table__search",
1128
1079
  children: /* @__PURE__ */ jsx("input", {
@@ -1404,13 +1355,6 @@ function ValueCell({ value, label, children }) {
1404
1355
  ]
1405
1356
  });
1406
1357
  }
1407
- /**
1408
- * Pick the value + gamut flag to display in the single Value column based
1409
- * on the active color-format context. We pre-compute hex/hsl/oklch for the
1410
- * expanded sub-table regardless; the extras (`rgb`, `raw`) take a fresh
1411
- * `formatColor` pass. Keeps the hot path fast while staying honest about
1412
- * out-of-gamut warnings per-format.
1413
- */
1414
1358
  function pickActiveFormat(raw, colorFormat, hex, hsl, oklch) {
1415
1359
  switch (colorFormat) {
1416
1360
  case "hex": return {
@@ -1454,30 +1398,11 @@ function buildVariantDefs(variants) {
1454
1398
  displayOrder
1455
1399
  };
1456
1400
  }
1457
- /**
1458
- * Position of a label within a group — the `base` entry always sorts first,
1459
- * then declared labels in the order the caller wrote them in the `variants`
1460
- * prop. Unknown labels (shouldn't happen in practice) fall to the end.
1461
- */
1462
1401
  function orderIndex(label, defs) {
1463
1402
  if (label === BASE_LABEL) return -1;
1464
1403
  const idx = defs.displayOrder.indexOf(label);
1465
1404
  return idx >= 0 ? idx : Number.POSITIVE_INFINITY;
1466
1405
  }
1467
- /**
1468
- * Resolve the variant label + base path for a token, if any. The leaf
1469
- * (last dot-segment) must either equal the suffix outright (dot-segment
1470
- * form: `hi.disabled` matches suffix `disabled`) or end in `-<suffix>`
1471
- * (hyphen-tail form: `hi-d` matches `d`). The leading hyphen is required
1472
- * for the tail form so suffix `0` can't hit `neutral-900` by character.
1473
- *
1474
- * Returned `basePath` is what gets used as the grouping key:
1475
- * - Dot-segment match → parent path (drop the last dot-segment)
1476
- * - Hyphen-tail match → same dot-depth, leaf with `-<suffix>` stripped
1477
- *
1478
- * Entries in `matchOrder` are pre-sorted longest-first, so `h-dark` wins
1479
- * over `dark` for a path ending in `-h-dark`.
1480
- */
1481
1406
  function matchVariant(path, matchOrder) {
1482
1407
  if (matchOrder.length === 0) return void 0;
1483
1408
  const segments = path.split(".");
@@ -1552,7 +1477,7 @@ function Diagnostics({ caption } = {}) {
1552
1477
  const summary = useMemo(() => summarize(diagnostics), [diagnostics]);
1553
1478
  const headingText = caption ?? `Diagnostics · ${summary.text}`;
1554
1479
  return /* @__PURE__ */ jsx("div", {
1555
- ...themeAttrs(cssVarPrefix, activeAxes),
1480
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1556
1481
  "data-testid": "diagnostics",
1557
1482
  children: /* @__PURE__ */ jsxs("details", {
1558
1483
  open: summary.hasErrorsOrWarnings,
@@ -1605,12 +1530,6 @@ const styles$1 = {
1605
1530
  minHeight: 1
1606
1531
  }
1607
1532
  };
1608
- /**
1609
- * Convert a DTCG dimension `$value` (`{ value, unit }`) to pixels for the
1610
- * purpose of deciding whether to cap the rendered bar. Returns `NaN` for
1611
- * units we can't reasonably approximate (ex / ch / %), which the caller
1612
- * treats as "render at cssVar but don't cap".
1613
- */
1614
1533
  function toPixels$1(raw) {
1615
1534
  if (raw == null || typeof raw !== "object") return NaN;
1616
1535
  const v = raw;
@@ -1622,14 +1541,14 @@ function toPixels$1(raw) {
1622
1541
  default: return NaN;
1623
1542
  }
1624
1543
  }
1625
- function DimensionBar({ path, kind = "length" }) {
1544
+ function DimensionBar({ path, visual = "length" }) {
1626
1545
  const project = useProject();
1627
1546
  const { resolved } = project;
1628
1547
  const cssVar = resolveCssVar(path, project);
1629
1548
  const token = resolved[path];
1630
1549
  const pxValue = toPixels$1(token?.$value);
1631
1550
  const cappedValue = Number.isFinite(pxValue) && pxValue > MAX_RENDER_PX$1 ? `${MAX_RENDER_PX$1}px` : cssVar;
1632
- switch (kind) {
1551
+ switch (visual) {
1633
1552
  case "radius": return /* @__PURE__ */ jsx("div", {
1634
1553
  style: {
1635
1554
  ...styles$1.radiusSample,
@@ -1770,7 +1689,7 @@ function toPixels(raw) {
1770
1689
  default: return NaN;
1771
1690
  }
1772
1691
  }
1773
- function DimensionScale({ filter, kind = "length", caption, sortBy = "value", sortDir = "asc" }) {
1692
+ function DimensionScale({ filter, visual = "length", caption, sortBy = "value", sortDir = "asc" }) {
1774
1693
  const project = useProject();
1775
1694
  const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
1776
1695
  const rows = useMemo(() => {
@@ -1799,14 +1718,14 @@ function DimensionScale({ filter, kind = "length", caption, sortBy = "value", so
1799
1718
  ]);
1800
1719
  const captionText = caption ?? `${rows.length} dimension${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
1801
1720
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
1802
- ...themeAttrs(cssVarPrefix, activeAxes),
1721
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1803
1722
  children: /* @__PURE__ */ jsx("div", {
1804
1723
  className: "sb-block__empty",
1805
1724
  children: "No dimension tokens match this filter."
1806
1725
  })
1807
1726
  });
1808
1727
  return /* @__PURE__ */ jsxs("div", {
1809
- ...themeAttrs(cssVarPrefix, activeAxes),
1728
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1810
1729
  children: [/* @__PURE__ */ jsx("div", {
1811
1730
  className: "sb-block__caption",
1812
1731
  children: captionText
@@ -1827,7 +1746,7 @@ function DimensionScale({ filter, kind = "length", caption, sortBy = "value", so
1827
1746
  className: "sb-dimension-scale__visual-cell",
1828
1747
  children: [/* @__PURE__ */ jsx(DimensionBar, {
1829
1748
  path: row.path,
1830
- kind
1749
+ visual
1831
1750
  }), row.capped && /* @__PURE__ */ jsxs("span", {
1832
1751
  className: "sb-dimension-scale__cap",
1833
1752
  children: [
@@ -1846,13 +1765,13 @@ function DimensionScale({ filter, kind = "length", caption, sortBy = "value", so
1846
1765
  });
1847
1766
  }
1848
1767
  //#endregion
1849
- //#region src/FontFamilySample.tsx
1768
+ //#region src/FontFamilyPreview.tsx
1850
1769
  function stackString(raw) {
1851
1770
  if (typeof raw === "string") return raw;
1852
1771
  if (Array.isArray(raw)) return raw.map(String).join(", ");
1853
1772
  return "";
1854
1773
  }
1855
- function FontFamilySample({ filter, sample = "The quick brown fox jumps over the lazy dog.", caption, sortBy = "path", sortDir = "asc" }) {
1774
+ function FontFamilyPreview({ filter, sample = "The quick brown fox jumps over the lazy dog.", caption, sortBy = "path", sortDir = "asc" }) {
1856
1775
  const project = useProject();
1857
1776
  const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
1858
1777
  const rows = useMemo(() => {
@@ -1876,14 +1795,14 @@ function FontFamilySample({ filter, sample = "The quick brown fox jumps over the
1876
1795
  ]);
1877
1796
  const captionText = caption ?? `${rows.length} fontFamily token${rows.length === 1 ? "" : "s"}${filter && filter !== "fontFamily" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
1878
1797
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
1879
- ...themeAttrs(cssVarPrefix, activeAxes),
1798
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1880
1799
  children: /* @__PURE__ */ jsx("div", {
1881
1800
  className: "sb-block__empty",
1882
1801
  children: "No fontFamily tokens match this filter."
1883
1802
  })
1884
1803
  });
1885
1804
  return /* @__PURE__ */ jsxs("div", {
1886
- ...themeAttrs(cssVarPrefix, activeAxes),
1805
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1887
1806
  children: [/* @__PURE__ */ jsx("div", {
1888
1807
  className: "sb-block__caption",
1889
1808
  children: captionText
@@ -1963,14 +1882,14 @@ function FontWeightScale({ filter, sample = "Aa", caption, sortBy = "value", sor
1963
1882
  ]);
1964
1883
  const captionText = caption ?? `${rows.length} fontWeight token${rows.length === 1 ? "" : "s"}${filter && filter !== "fontWeight" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
1965
1884
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
1966
- ...themeAttrs(cssVarPrefix, activeAxes),
1885
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1967
1886
  children: /* @__PURE__ */ jsx("div", {
1968
1887
  className: "sb-block__empty",
1969
1888
  children: "No fontWeight tokens match this filter."
1970
1889
  })
1971
1890
  });
1972
1891
  return /* @__PURE__ */ jsxs("div", {
1973
- ...themeAttrs(cssVarPrefix, activeAxes),
1892
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
1974
1893
  children: [/* @__PURE__ */ jsx("div", {
1975
1894
  className: "sb-block__caption",
1976
1895
  children: captionText
@@ -2047,14 +1966,14 @@ function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" })
2047
1966
  ]);
2048
1967
  const captionText = caption ?? `${rows.length} gradient${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2049
1968
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
2050
- ...themeAttrs(cssVarPrefix, activeAxes),
1969
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2051
1970
  children: /* @__PURE__ */ jsx("div", {
2052
1971
  className: "sb-block__empty",
2053
1972
  children: "No gradient tokens match this filter."
2054
1973
  })
2055
1974
  });
2056
1975
  return /* @__PURE__ */ jsxs("div", {
2057
- ...themeAttrs(cssVarPrefix, activeAxes),
1976
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2058
1977
  children: [/* @__PURE__ */ jsx("div", {
2059
1978
  className: "sb-block__caption",
2060
1979
  children: captionText
@@ -2104,14 +2023,6 @@ function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" })
2104
2023
  }
2105
2024
  //#endregion
2106
2025
  //#region src/internal/prefers-reduced-motion.ts
2107
- /**
2108
- * True when rendering inside Chromatic's snapshot runner. Chromatic's
2109
- * browser ships a recognisable user-agent string; checked here so
2110
- * motion-looping components can fall back to their static state for
2111
- * deterministic snapshots. Per-component detection rather than the
2112
- * global `chromatic.prefersReducedMotion: true` parameter — that
2113
- * parameter is incompatible with Chromatic's verification parser.
2114
- */
2115
2026
  function isChromatic() {
2116
2027
  if (typeof navigator === "undefined") return false;
2117
2028
  return navigator.userAgent.includes("Chromatic");
@@ -2332,14 +2243,14 @@ function MotionPreview({ filter, caption }) {
2332
2243
  ]);
2333
2244
  const captionText = caption ?? `${rows.length} motion token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2334
2245
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
2335
- ...themeAttrs(cssVarPrefix, activeAxes),
2246
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2336
2247
  children: /* @__PURE__ */ jsx("div", {
2337
2248
  className: "sb-block__empty",
2338
2249
  children: "No motion tokens match this filter."
2339
2250
  })
2340
2251
  });
2341
2252
  return /* @__PURE__ */ jsxs("div", {
2342
- ...themeAttrs(cssVarPrefix, activeAxes),
2253
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2343
2254
  children: [
2344
2255
  /* @__PURE__ */ jsx("div", {
2345
2256
  className: "sb-block__caption",
@@ -2442,7 +2353,7 @@ function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg"
2442
2353
  ]);
2443
2354
  const captionText = caption ?? `${rows.length} opacity token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2444
2355
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
2445
- ...themeAttrs(cssVarPrefix, activeAxes),
2356
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2446
2357
  children: /* @__PURE__ */ jsx("div", {
2447
2358
  className: "sb-block__empty",
2448
2359
  children: "No opacity tokens match this filter."
@@ -2450,7 +2361,7 @@ function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg"
2450
2361
  });
2451
2362
  const sampleColorVar = resolveCssVar(sampleColor, project);
2452
2363
  return /* @__PURE__ */ jsxs("div", {
2453
- ...themeAttrs(cssVarPrefix, activeAxes),
2364
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2454
2365
  children: [/* @__PURE__ */ jsx("div", {
2455
2366
  className: "sb-block__caption",
2456
2367
  children: captionText
@@ -2581,14 +2492,14 @@ function ShadowPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2581
2492
  ]);
2582
2493
  const captionText = caption ?? `${rows.length} shadow${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2583
2494
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
2584
- ...themeAttrs(cssVarPrefix, activeAxes),
2495
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2585
2496
  children: /* @__PURE__ */ jsx("div", {
2586
2497
  className: "sb-block__empty",
2587
2498
  children: "No shadow tokens match this filter."
2588
2499
  })
2589
2500
  });
2590
2501
  return /* @__PURE__ */ jsxs("div", {
2591
- ...themeAttrs(cssVarPrefix, activeAxes),
2502
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2592
2503
  children: [/* @__PURE__ */ jsx("div", {
2593
2504
  className: "sb-block__caption",
2594
2505
  children: captionText
@@ -2654,7 +2565,7 @@ function Layer({ layer, index, total, colorFormat }) {
2654
2565
  });
2655
2566
  }
2656
2567
  //#endregion
2657
- //#region src/StrokeStyleSample.tsx
2568
+ //#region src/StrokeStylePreview.tsx
2658
2569
  const STRING_STYLES = new Set([
2659
2570
  "solid",
2660
2571
  "dashed",
@@ -2669,7 +2580,7 @@ function extractCssStyle(value) {
2669
2580
  if (typeof value === "string" && STRING_STYLES.has(value)) return value;
2670
2581
  return null;
2671
2582
  }
2672
- function StrokeStyleSample({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2583
+ function StrokeStylePreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2673
2584
  const project = useProject();
2674
2585
  const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
2675
2586
  const rows = useMemo(() => {
@@ -2694,14 +2605,14 @@ function StrokeStyleSample({ filter, caption, sortBy = "path", sortDir = "asc" }
2694
2605
  ]);
2695
2606
  const captionText = caption ?? `${rows.length} strokeStyle token${rows.length === 1 ? "" : "s"}${filter && filter !== "strokeStyle" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2696
2607
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
2697
- ...themeAttrs(cssVarPrefix, activeAxes),
2608
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2698
2609
  children: /* @__PURE__ */ jsx("div", {
2699
2610
  className: "sb-block__empty",
2700
2611
  children: "No strokeStyle tokens match this filter."
2701
2612
  })
2702
2613
  });
2703
2614
  return /* @__PURE__ */ jsxs("div", {
2704
- ...themeAttrs(cssVarPrefix, activeAxes),
2615
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
2705
2616
  children: [/* @__PURE__ */ jsx("div", {
2706
2617
  className: "sb-block__caption",
2707
2618
  children: captionText
@@ -3252,12 +3163,6 @@ function formatDimensionValue(v) {
3252
3163
  }
3253
3164
  return JSON.stringify(v);
3254
3165
  }
3255
- /**
3256
- * Route sub-value colors through `formatColor` so they honor the active
3257
- * color-format dropdown, just like the standalone `<ColorPalette />` and
3258
- * `<TokenDetail />` top-line do. Returns `null` for a missing field so
3259
- * the key/value row drops out entirely.
3260
- */
3261
3166
  function formatColorSubValue(v, format) {
3262
3167
  if (v == null) return null;
3263
3168
  return formatColor(v, format).value;
@@ -3270,11 +3175,6 @@ function pickArrayAliases(v) {
3270
3175
  if (!Array.isArray(v)) return void 0;
3271
3176
  return v;
3272
3177
  }
3273
- /**
3274
- * Walk the alias chain starting from an immediate sub-value alias target.
3275
- * `aliasTarget` is the path the sub-value directly references; the target
3276
- * token's own `aliasChain` continues the walk to the primitive.
3277
- */
3278
3178
  function subValueChain(aliasTarget, resolved) {
3279
3179
  if (!aliasTarget) return void 0;
3280
3180
  const tail = (resolved?.[aliasTarget])?.aliasChain;
@@ -3366,7 +3266,7 @@ function CompositePreviewContent({ type, cssVar, rawValue }) {
3366
3266
  style: { background: `linear-gradient(to right, ${cssVar})` },
3367
3267
  "aria-hidden": true
3368
3268
  });
3369
- if (type === "strokeStyle") return /* @__PURE__ */ jsx(StrokeStylePreview, { value: rawValue });
3269
+ if (type === "strokeStyle") return /* @__PURE__ */ jsx(StrokeStylePreview$1, { value: rawValue });
3370
3270
  if (type === "color") return /* @__PURE__ */ jsxs("div", {
3371
3271
  className: "sb-token-detail__color-swatch-row",
3372
3272
  "aria-hidden": true,
@@ -3380,7 +3280,7 @@ function CompositePreviewContent({ type, cssVar, rawValue }) {
3380
3280
  });
3381
3281
  return null;
3382
3282
  }
3383
- function StrokeStylePreview({ value }) {
3283
+ function StrokeStylePreview$1({ value }) {
3384
3284
  if (typeof value === "string" && STROKE_STYLE_STRINGS.has(value)) return /* @__PURE__ */ jsx("div", {
3385
3285
  className: "sb-token-detail__stroke-style-line",
3386
3286
  style: { borderTopStyle: value },
@@ -3579,11 +3479,6 @@ function TokenHeader({ path, heading }) {
3579
3479
  }
3580
3480
  //#endregion
3581
3481
  //#region src/token-detail/TokenUsageSnippet.tsx
3582
- /**
3583
- * DTCG `$type`s with a single canonical CSS property. Types whose value is
3584
- * applied across many properties (`dimension`, `number`, `strokeStyle`,
3585
- * `typography`) are intentionally absent — they fall back to a commented hint.
3586
- */
3587
3482
  const CSS_PROPERTY_BY_TYPE = {
3588
3483
  color: "color",
3589
3484
  shadow: "box-shadow",
@@ -3624,10 +3519,10 @@ function TokenUsageSnippet({ path }) {
3624
3519
  function TokenDetail({ path, heading }) {
3625
3520
  const { token, cssVar, activeTheme, activeAxes, cssVarPrefix } = useTokenDetailData(path);
3626
3521
  const colorFormat = useColorFormat();
3627
- const theme = themeAttrs(cssVarPrefix, activeAxes);
3522
+ const wrapperAttrs = blockWrapperAttrs(cssVarPrefix, activeAxes);
3628
3523
  if (!token) return /* @__PURE__ */ jsx("div", {
3629
- ...theme,
3630
- className: cx(theme["className"], "sb-token-detail"),
3524
+ ...wrapperAttrs,
3525
+ className: cx(wrapperAttrs["className"], "sb-token-detail"),
3631
3526
  children: /* @__PURE__ */ jsxs("div", {
3632
3527
  className: "sb-token-detail__missing",
3633
3528
  children: [
@@ -3645,8 +3540,8 @@ function TokenDetail({ path, heading }) {
3645
3540
  const value = formatTokenValue(token.$value, token.$type, colorFormat, listing[path]);
3646
3541
  const outOfGamut = gamut?.outOfGamut ?? false;
3647
3542
  return /* @__PURE__ */ jsxs("div", {
3648
- ...theme,
3649
- className: cx(theme["className"], "sb-token-detail"),
3543
+ ...wrapperAttrs,
3544
+ className: cx(wrapperAttrs["className"], "sb-token-detail"),
3650
3545
  children: [
3651
3546
  /* @__PURE__ */ jsx(TokenHeader, {
3652
3547
  path,
@@ -3689,12 +3584,6 @@ function TokenDetail({ path, heading }) {
3689
3584
  }
3690
3585
  //#endregion
3691
3586
  //#region src/internal/DetailOverlay.tsx
3692
- /**
3693
- * Selector for elements the trap considers focus stops. Mirrors the
3694
- * "tabbable" set most focus-trap libraries use; the `:not(...)` clauses
3695
- * skip the panel wrapper itself (we focus it manually on mount via its
3696
- * own ref) and any explicitly-detabbed descendants.
3697
- */
3698
3587
  const FOCUSABLE_SELECTOR = [
3699
3588
  "a[href]",
3700
3589
  "button:not([disabled])",
@@ -3734,11 +3623,6 @@ function DetailOverlay({ path, onClose, testId = "swatchbook-overlay" }) {
3734
3623
  window.addEventListener("keydown", onKey);
3735
3624
  return () => window.removeEventListener("keydown", onKey);
3736
3625
  }, [onClose]);
3737
- /**
3738
- * Wrap Tab inside the panel: from the last focusable, jump to the first;
3739
- * from the first (or from the panel itself), Shift+Tab jumps to the last.
3740
- * Defers to the browser otherwise.
3741
- */
3742
3626
  const onPanelKeyDown = (e) => {
3743
3627
  if (e.key !== "Tab") return;
3744
3628
  const panel = panelRef.current;
@@ -3787,12 +3671,6 @@ function DetailOverlay({ path, onClose, testId = "swatchbook-overlay" }) {
3787
3671
  })
3788
3672
  });
3789
3673
  }
3790
- /**
3791
- * Walk up from `node` to the direct child of `<body>` that contains it.
3792
- * Returns `null` when the node isn't attached to the document (mid-mount,
3793
- * post-unmount). Used to identify which top-level branch to *not* mark
3794
- * inert when the overlay opens.
3795
- */
3796
3674
  function findBodyChildContaining(node) {
3797
3675
  let cursor = node;
3798
3676
  while (cursor && cursor.parentElement !== document.body) cursor = cursor.parentElement;
@@ -3886,11 +3764,6 @@ function flattenVisible(nodes, expanded, parentPath, out) {
3886
3764
  if (node.kind === "group" && expanded.has(node.path)) flattenVisible(node.children, expanded, node.path, out);
3887
3765
  }
3888
3766
  }
3889
- /**
3890
- * Return a pruned copy of the tree keeping only leaves whose path is in
3891
- * `matches`, plus the groups on the way to them. Every surviving group's
3892
- * path is added to `expandOut` so callers can force those groups open.
3893
- */
3894
3767
  function pruneTreeForMatches(nodes, matches, expandOut) {
3895
3768
  const out = [];
3896
3769
  for (const node of nodes) if (node.kind === "leaf") {
@@ -4092,11 +3965,11 @@ function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true,
4092
3965
  return n;
4093
3966
  }, [visibleTree, searchExpanded]);
4094
3967
  if (tree.length === 0) return /* @__PURE__ */ jsx("div", {
4095
- ...themeAttrs(cssVarPrefix, activeAxes),
3968
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4096
3969
  children: /* @__PURE__ */ jsx(EmptyState, { children: root ? `No tokens under "${root}"${typeFilter ? ` matching ${typeLabel.slice(3)}` : ""}.` : typeFilter ? `No tokens matching ${typeLabel.slice(3)} in the active theme.` : "No tokens in the active theme." })
4097
3970
  });
4098
3971
  return /* @__PURE__ */ jsxs("div", {
4099
- ...themeAttrs(cssVarPrefix, activeAxes),
3972
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4100
3973
  children: [
4101
3974
  searchable && /* @__PURE__ */ jsx("div", {
4102
3975
  className: "sb-token-navigator__search",
@@ -4288,7 +4161,7 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
4288
4161
  className: "sb-token-navigator__preview-dimension",
4289
4162
  children: /* @__PURE__ */ jsx(DimensionBar, {
4290
4163
  path,
4291
- kind: "length"
4164
+ visual: "length"
4292
4165
  })
4293
4166
  })]
4294
4167
  });
@@ -4378,14 +4251,14 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
4378
4251
  const matchSuffix = searchable && query.trim() !== "" ? ` · ${visibleRows.length} matching "${query.trim()}"` : "";
4379
4252
  const captionText = caption ?? `${rows.length} token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""}${type ? ` · $type=${type}` : ""}${matchSuffix} · ${activeTheme}`;
4380
4253
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
4381
- ...themeAttrs(cssVarPrefix, activeAxes),
4254
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4382
4255
  children: /* @__PURE__ */ jsx("div", {
4383
4256
  className: "sb-block__empty",
4384
4257
  children: "No tokens match this filter."
4385
4258
  })
4386
4259
  });
4387
4260
  return /* @__PURE__ */ jsxs("div", {
4388
- ...themeAttrs(cssVarPrefix, activeAxes),
4261
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4389
4262
  children: [
4390
4263
  searchable && /* @__PURE__ */ jsx("div", {
4391
4264
  className: "sb-token-table__search",
@@ -4557,14 +4430,14 @@ function TypographyScale({ filter, sample = "The quick brown fox jumps over the
4557
4430
  ]);
4558
4431
  const captionText = caption ?? `${rows.length} typography token${rows.length === 1 ? "" : "s"}${filter && filter !== "typography" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
4559
4432
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
4560
- ...themeAttrs(cssVarPrefix, activeAxes),
4433
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4561
4434
  children: /* @__PURE__ */ jsx("div", {
4562
4435
  className: "sb-block__empty",
4563
4436
  children: "No typography tokens match this filter."
4564
4437
  })
4565
4438
  });
4566
4439
  return /* @__PURE__ */ jsxs("div", {
4567
- ...themeAttrs(cssVarPrefix, activeAxes),
4440
+ ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4568
4441
  children: [/* @__PURE__ */ jsx("div", {
4569
4442
  className: "sb-block__caption",
4570
4443
  children: captionText
@@ -4587,6 +4460,6 @@ function TypographyScale({ filter, sample = "The quick brown fox jumps over the
4587
4460
  });
4588
4461
  }
4589
4462
  //#endregion
4590
- export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, ColorTable, CompositeBreakdown, CompositePreview, ConsumerOutput, Diagnostics, DimensionBar, DimensionScale, FontFamilySample, FontWeightScale, GradientPalette, MotionPreview, MotionSample, OpacityScale, ShadowPreview, ShadowSample, StrokeStyleSample, SwatchbookContext, SwatchbookProvider, ThemeContext, TokenDetail, TokenHeader, TokenNavigator, TokenTable, TokenUsageSnippet, TypographyScale, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
4463
+ export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, ColorTable, CompositeBreakdown, CompositePreview, ConsumerOutput, Diagnostics, DimensionBar, DimensionScale, FontFamilyPreview, FontWeightScale, GradientPalette, MotionPreview, MotionSample, OpacityScale, ShadowPreview, ShadowSample, StrokeStylePreview, SwatchbookContext, SwatchbookProvider, ThemeContext, TokenDetail, TokenHeader, TokenNavigator, TokenTable, TokenUsageSnippet, TypographyScale, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
4591
4464
 
4592
4465
  //# sourceMappingURL=index.mjs.map