@unpunnyfuns/swatchbook-blocks 0.66.3 → 0.68.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/README.md CHANGED
@@ -4,7 +4,7 @@ React MDX doc blocks for [swatchbook](https://github.com/unpunnyfuns/swatchbook)
4
4
 
5
5
  Render your DTCG tokens in `.mdx` stories: swatch grids, type-specific previews, per-token inspectors. The blocks react to the toolbar's axis flips without any wiring in your story code.
6
6
 
7
- Most consumers pick this up transitively via [`@unpunnyfuns/swatchbook-addon`](../addon); `import { TokenTable } from '@unpunnyfuns/swatchbook-addon'` works out of the box. Install this package directly when you want blocks *without* the Storybook addon, such as unit tests or a standalone React app wrapping tokens in a custom surface.
7
+ Most consumers pick this up transitively via [`@unpunnyfuns/swatchbook-addon`](../addon); `import { TokenTable } from '@unpunnyfuns/swatchbook-addon'` works out of the box. Install this package directly when you want blocks _without_ the Storybook addon, such as unit tests or a standalone React app wrapping tokens in a custom surface.
8
8
 
9
9
  ## Install
10
10
 
@@ -32,7 +32,7 @@ import snapshot from './tokens-snapshot.json';
32
32
 
33
33
  <SwatchbookProvider value={snapshot}>
34
34
  <TokenTable filter="color.**" />
35
- </SwatchbookProvider>
35
+ </SwatchbookProvider>;
36
36
  ```
37
37
 
38
38
  Block catalogue, props, and composition patterns live in the [blocks reference](https://unpunnyfuns.github.io/swatchbook/reference/blocks) and the [authoring guide](https://unpunnyfuns.github.io/swatchbook/guides/authoring-doc-stories).
package/dist/index.mjs CHANGED
@@ -2,41 +2,16 @@ import './style.css';
2
2
  import { COLOR_FORMATS } from "@unpunnyfuns/swatchbook-core/color-formats";
3
3
  import { formatColor, parseColor } from "@unpunnyfuns/swatchbook-core/format-color";
4
4
  import { createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
5
  import { getVariance, listPaths, resolveAllWithProvenanceAt } from "@unpunnyfuns/swatchbook-core/graph";
7
6
  import { makeCssVar } from "@unpunnyfuns/swatchbook-core/css-var";
8
7
  import { SWATCHBOOK_STYLE_ELEMENT_ID, ensureStyleElement } from "@unpunnyfuns/swatchbook-core/style-element";
9
8
  import { tupleToName } from "@unpunnyfuns/swatchbook-core/themes";
10
9
  import { addons } from "storybook/preview-api";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
11
  import { dataAttr } from "@unpunnyfuns/swatchbook-core/data-attr";
12
12
  import { matchPath } from "@unpunnyfuns/swatchbook-core/match-path";
13
13
  import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
14
14
  import cx from "clsx";
15
- //#region src/internal/styles.tsx
16
- /**
17
- * Chrome-style primitives shared across every block. Kept as JS exports
18
- * for the inline-style sites that still compose them into per-block style
19
- * objects (e.g. TokenNavigator's `typePill` that builds on the shared
20
- * pill base). The pure direct-reference chrome — surface wrapper, caption,
21
- * empty-state — lives in `styles.css` and is applied via class names.
22
- */
23
- const TEXT_MUTED = "var(--swatchbook-text-muted, CanvasText)";
24
- const SURFACE_RAISED = "var(--swatchbook-surface-raised, Canvas)";
25
- const SURFACE_MUTED = "var(--swatchbook-surface-muted, rgba(128,128,128,0.15))";
26
- const BORDER_FAINT = `1px solid var(--swatchbook-border-default, rgba(128,128,128,0.15))`;
27
- const BORDER_STRONG = `1px solid var(--swatchbook-border-default, rgba(128,128,128,0.3))`;
28
- /**
29
- * Inner content for a block's "nothing to render" state. Call sites wrap
30
- * it in their own block wrapper (which already carries `blockWrapperAttrs`), so
31
- * the message itself just needs the muted type.
32
- */
33
- function EmptyState({ children }) {
34
- return /* @__PURE__ */ jsx("div", {
35
- className: "sb-block__empty",
36
- children
37
- });
38
- }
39
- //#endregion
40
15
  //#region src/internal/channel-globals.ts
41
16
  const AXES_GLOBAL_KEY = "swatchbookAxes";
42
17
  const COLOR_FORMAT_GLOBAL_KEY = "swatchbookColorFormat";
@@ -75,7 +50,7 @@ function ensureSubscribed$1() {
75
50
  channel.on("setGlobals", onGlobals);
76
51
  }
77
52
  ensureSubscribed$1();
78
- function subscribe$1(cb) {
53
+ function subscribe$2(cb) {
79
54
  ensureSubscribed$1();
80
55
  listeners$1.add(cb);
81
56
  return () => {
@@ -89,7 +64,7 @@ function getServerSnapshot$1() {
89
64
  return snapshot$1;
90
65
  }
91
66
  function useChannelGlobals() {
92
- return useSyncExternalStore(subscribe$1, getSnapshot$1, getServerSnapshot$1);
67
+ return useSyncExternalStore(subscribe$2, getSnapshot$1, getServerSnapshot$1);
93
68
  }
94
69
  //#endregion
95
70
  //#region src/contexts.ts
@@ -212,7 +187,7 @@ function ensureSubscribed() {
212
187
  });
213
188
  }
214
189
  ensureSubscribed();
215
- function subscribe(cb) {
190
+ function subscribe$1(cb) {
216
191
  ensureSubscribed();
217
192
  listeners.add(cb);
218
193
  return () => {
@@ -226,7 +201,7 @@ function getServerSnapshot() {
226
201
  return snapshot;
227
202
  }
228
203
  function useTokenSnapshot() {
229
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
204
+ return useSyncExternalStore(subscribe$1, getSnapshot, getServerSnapshot);
230
205
  }
231
206
  //#endregion
232
207
  //#region src/internal/use-project.ts
@@ -388,19 +363,10 @@ function resolveColorValue(path, raw, colorFormat, project) {
388
363
  }
389
364
  //#endregion
390
365
  //#region src/border-preview/BorderSample.tsx
391
- const sampleStyle$1 = {
392
- width: 120,
393
- height: 56,
394
- background: SURFACE_RAISED,
395
- borderRadius: 6
396
- };
397
366
  function BorderSample({ path }) {
398
- const cssVar = resolveCssVar(path, useProject());
399
367
  return /* @__PURE__ */ jsx("div", {
400
- style: {
401
- ...sampleStyle$1,
402
- border: cssVar
403
- },
368
+ className: "sb-border-sample",
369
+ style: { border: resolveCssVar(path, useProject()) },
404
370
  "aria-hidden": true
405
371
  });
406
372
  }
@@ -484,11 +450,11 @@ const NUMERIC_TYPES = new Set([
484
450
  "lineHeight"
485
451
  ]);
486
452
  const STRING_TYPES = new Set(["fontFamily", "strokeStyle"]);
487
- function computeSortKey(token) {
453
+ function computeSortKey(token, rootFontSizePx) {
488
454
  const type = token.$type;
489
455
  if (!type) return { kind: "none" };
490
456
  if (NUMERIC_TYPES.has(type)) {
491
- const value = toMagnitude(token.$value);
457
+ const value = toMagnitude(token.$value, rootFontSizePx);
492
458
  return {
493
459
  kind: "numeric",
494
460
  value,
@@ -511,8 +477,9 @@ function sortTokens(entries, options = {}) {
511
477
  const sign = dir === "desc" ? -1 : 1;
512
478
  if (by === "none") return dir === "desc" ? [...entries].toReversed() : [...entries];
513
479
  if (by === "path") return [...entries].toSorted(([a], [b]) => sign * a.localeCompare(b, void 0, { numeric: true }));
480
+ const rootFontSizePx = options.rootFontSizePx ?? 16;
514
481
  const keys = /* @__PURE__ */ new Map();
515
- for (const [, token] of entries) keys.set(token, computeSortKey(token));
482
+ for (const [, token] of entries) keys.set(token, computeSortKey(token, rootFontSizePx));
516
483
  return [...entries].toSorted(([aPath, aTok], [bPath, bTok]) => {
517
484
  const cmp = compareValue(aTok, bTok, keys);
518
485
  if (cmp !== 0) return sign * cmp;
@@ -544,7 +511,7 @@ function compareValue(a, b, keys) {
544
511
  if (ak.kind === "string" && bk.kind === "string") return ak.value.localeCompare(bk.value, void 0, { numeric: true });
545
512
  return 0;
546
513
  }
547
- function toMagnitude(v) {
514
+ function toMagnitude(v, rootFontSizePx) {
548
515
  if (typeof v === "number") return v;
549
516
  if (v && typeof v === "object") {
550
517
  const d = v;
@@ -554,8 +521,7 @@ function toMagnitude(v) {
554
521
  case "px":
555
522
  case "ms": return d.value;
556
523
  case "s": return d.value * 1e3;
557
- case "rem":
558
- case "em": return d.value * 16;
524
+ case "rem": return d.value * rootFontSizePx;
559
525
  default: return d.value;
560
526
  }
561
527
  }
@@ -1057,12 +1023,9 @@ function CopyButton$1({ value, label, variant = "icon", className }) {
1057
1023
  timerRef.current = setTimeout(() => setCopied(false), 1500);
1058
1024
  }, [value]);
1059
1025
  const ariaLabel = label ?? `Copy ${value}`;
1060
- const classes = ["sb-copy-button", `sb-copy-button--${variant}`];
1061
- if (copied) classes.push("sb-copy-button--copied");
1062
- if (className) classes.push(className);
1063
1026
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
1064
1027
  type: "button",
1065
- className: classes.join(" "),
1028
+ className: cx("sb-copy-button", `sb-copy-button--${variant}`, copied && "sb-copy-button--copied", className),
1066
1029
  onClick: handleClick,
1067
1030
  "aria-label": ariaLabel,
1068
1031
  title: ariaLabel,
@@ -1702,73 +1665,95 @@ function Diagnostics({ caption } = {}) {
1702
1665
  //#region src/dimension-scale/dimension-px.ts
1703
1666
  /**
1704
1667
  * Convert a DTCG dimension `$value` (`{ value, unit }`) to pixels for the
1705
- * purpose of deciding whether to cap the rendered size. Returns `NaN` for
1706
- * units we can't reasonably approximate (ex / ch / %), which the caller
1707
- * treats as "render at cssVar but don't cap".
1668
+ * purpose of deciding whether to cap the rendered size. `rootFontSizePx`
1669
+ * scales `rem` against the rendering context's actual root font-size
1670
+ * (default 16 for the no-DOM / SSR path); the bar paints at the same
1671
+ * context's `var()`, so passing the measured root keeps the cap decision
1672
+ * aligned with what's drawn. Returns `NaN` for anything other than `px` /
1673
+ * `rem` — `ex` / `ch` / `%`, and the non-DTCG `em` (the `dimension` type
1674
+ * permits only `px | rem`) — which the caller treats as "render at cssVar
1675
+ * but don't cap".
1708
1676
  */
1709
- function toPixels(raw) {
1677
+ function toPixels(raw, rootFontSizePx = 16) {
1710
1678
  if (raw == null || typeof raw !== "object") return NaN;
1711
1679
  const v = raw;
1712
1680
  if (typeof v.value !== "number" || typeof v.unit !== "string") return NaN;
1713
1681
  switch (v.unit) {
1714
1682
  case "px": return v.value;
1715
- case "rem":
1716
- case "em": return v.value * 16;
1683
+ case "rem": return v.value * rootFontSizePx;
1717
1684
  default: return NaN;
1718
1685
  }
1719
1686
  }
1720
1687
  //#endregion
1688
+ //#region src/internal/use-root-font-size.ts
1689
+ function readRootFontSize() {
1690
+ if (typeof document === "undefined") return 16;
1691
+ const fontSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
1692
+ return Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 16;
1693
+ }
1694
+ function subscribe(onChange) {
1695
+ if (typeof window === "undefined") return () => {};
1696
+ window.addEventListener("resize", onChange);
1697
+ return () => window.removeEventListener("resize", onChange);
1698
+ }
1699
+ /**
1700
+ * Root font-size (px) of the context a block renders in, tracked across
1701
+ * viewport changes. `rem` dimension values resolve their `var()` against this
1702
+ * root, so the cap math (`toPixels`) and value sort (`sortTokens`) must scale
1703
+ * `rem` by it rather than a literal 16: a Storybook Docs page, or a responsive
1704
+ * desktop/tablet/mobile breakpoint, can apply a different root to the same
1705
+ * token. Falls back to 16 with no DOM.
1706
+ */
1707
+ function useRootFontSize() {
1708
+ return useSyncExternalStore(subscribe, readRootFontSize, () => 16);
1709
+ }
1710
+ //#endregion
1721
1711
  //#region src/dimension-scale/DimensionBar.tsx
1722
- const styles$1 = {
1723
- bar: {
1724
- height: 14,
1725
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1726
- borderRadius: 2,
1727
- minWidth: 1
1728
- },
1729
- radiusSample: {
1730
- width: 56,
1731
- height: 56,
1732
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1733
- border: BORDER_STRONG
1734
- },
1735
- sizeSample: {
1736
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1737
- border: BORDER_STRONG,
1738
- minWidth: 1,
1739
- minHeight: 1
1740
- }
1741
- };
1712
+ function withCap(visual) {
1713
+ return /* @__PURE__ */ jsxs("span", {
1714
+ className: "sb-dimension-bar sb-dimension-bar--capped",
1715
+ title: `capped at 480px`,
1716
+ children: [visual, /* @__PURE__ */ jsx("span", {
1717
+ className: "sb-dimension-bar__cap",
1718
+ "aria-hidden": true,
1719
+ children: "…"
1720
+ })]
1721
+ });
1722
+ }
1742
1723
  function DimensionBar({ path, visual = "length" }) {
1743
1724
  const project = useProject();
1744
1725
  const { resolved } = project;
1726
+ const rootFontSize = useRootFontSize();
1745
1727
  const cssVar = resolveCssVar(path, project);
1746
1728
  const token = resolved[path];
1747
- const pxValue = toPixels(token?.$value);
1748
- const cappedValue = Number.isFinite(pxValue) && pxValue > 480 ? `480px` : cssVar;
1729
+ const pxValue = toPixels(token?.$value, rootFontSize);
1730
+ const capped = Number.isFinite(pxValue) && pxValue > 480;
1731
+ const cappedValue = capped ? `480px` : cssVar;
1749
1732
  switch (visual) {
1750
1733
  case "radius": return /* @__PURE__ */ jsx("div", {
1751
- style: {
1752
- ...styles$1.radiusSample,
1753
- borderRadius: cssVar
1754
- },
1755
- "aria-hidden": true
1756
- });
1757
- case "size": return /* @__PURE__ */ jsx("div", {
1758
- style: {
1759
- ...styles$1.sizeSample,
1760
- width: cappedValue,
1761
- height: cappedValue
1762
- },
1763
- "aria-hidden": true
1764
- });
1765
- default: return /* @__PURE__ */ jsx("div", {
1766
- style: {
1767
- ...styles$1.bar,
1768
- width: cappedValue
1769
- },
1734
+ className: "sb-dimension-bar__radius-sample",
1735
+ style: { borderRadius: cssVar },
1770
1736
  "aria-hidden": true
1771
1737
  });
1738
+ case "size": {
1739
+ const sample = /* @__PURE__ */ jsx("div", {
1740
+ className: "sb-dimension-bar__size-sample",
1741
+ style: {
1742
+ width: cappedValue,
1743
+ height: cappedValue
1744
+ },
1745
+ "aria-hidden": true
1746
+ });
1747
+ return capped ? withCap(sample) : sample;
1748
+ }
1749
+ default: {
1750
+ const bar = /* @__PURE__ */ jsx("div", {
1751
+ className: "sb-dimension-bar__bar",
1752
+ style: { width: cappedValue },
1753
+ "aria-hidden": true
1754
+ });
1755
+ return capped ? withCap(bar) : bar;
1756
+ }
1772
1757
  }
1773
1758
  }
1774
1759
  //#endregion
@@ -1878,29 +1863,27 @@ function formatUnknown(v) {
1878
1863
  function DimensionScale({ filter, visual = "length", caption, sortBy = "value", sortDir = "asc" }) {
1879
1864
  const project = useProject();
1880
1865
  const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
1866
+ const rootFontSize = useRootFontSize();
1881
1867
  const rows = useMemo(() => {
1882
1868
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
1883
1869
  if (token.$type !== "dimension") return false;
1884
1870
  return matchPath(path, filter);
1885
1871
  }), {
1886
1872
  by: sortBy,
1887
- dir: sortDir
1888
- }).map(([path, token]) => {
1889
- const pxValue = toPixels(token.$value);
1890
- return {
1891
- path,
1892
- cssVar: resolveCssVar(path, project),
1893
- displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path]),
1894
- pxValue,
1895
- capped: Number.isFinite(pxValue) && pxValue > 480
1896
- };
1897
- });
1873
+ dir: sortDir,
1874
+ rootFontSizePx: rootFontSize
1875
+ }).map(([path, token]) => ({
1876
+ path,
1877
+ cssVar: resolveCssVar(path, project),
1878
+ displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path])
1879
+ }));
1898
1880
  }, [
1899
1881
  resolved,
1900
1882
  filter,
1901
1883
  project,
1902
1884
  sortBy,
1903
- sortDir
1885
+ sortDir,
1886
+ rootFontSize
1904
1887
  ]);
1905
1888
  const captionText = caption ?? `${rows.length} dimension${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
1906
1889
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
@@ -1928,19 +1911,12 @@ function DimensionScale({ filter, visual = "length", caption, sortBy = "value",
1928
1911
  children: row.displayValue
1929
1912
  })]
1930
1913
  }),
1931
- /* @__PURE__ */ jsxs("div", {
1914
+ /* @__PURE__ */ jsx("div", {
1932
1915
  className: "sb-dimension-scale__visual-cell",
1933
- children: [/* @__PURE__ */ jsx(DimensionBar, {
1916
+ children: /* @__PURE__ */ jsx(DimensionBar, {
1934
1917
  path: row.path,
1935
1918
  visual
1936
- }), row.capped && /* @__PURE__ */ jsxs("span", {
1937
- className: "sb-dimension-scale__cap",
1938
- children: [
1939
- "capped at ",
1940
- 480,
1941
- "px"
1942
- ]
1943
- })]
1919
+ })
1944
1920
  }),
1945
1921
  /* @__PURE__ */ jsx("span", {
1946
1922
  className: "sb-dimension-scale__css-var",
@@ -2235,29 +2211,6 @@ function usePrefersReducedMotion() {
2235
2211
  //#region src/motion-preview/MotionSample.tsx
2236
2212
  const DEFAULT_DURATION_MS = 300;
2237
2213
  const DEFAULT_EASING = "cubic-bezier(0.2, 0, 0, 1)";
2238
- const styles = {
2239
- track: {
2240
- position: "relative",
2241
- height: 36,
2242
- background: SURFACE_MUTED,
2243
- borderRadius: 18,
2244
- overflow: "hidden"
2245
- },
2246
- ball: {
2247
- position: "absolute",
2248
- top: "50%",
2249
- width: 28,
2250
- height: 28,
2251
- marginTop: -14,
2252
- borderRadius: "50%",
2253
- background: "var(--swatchbook-accent-bg, #3b82f6)"
2254
- },
2255
- reducedMotion: {
2256
- fontSize: 11,
2257
- color: TEXT_MUTED,
2258
- fontStyle: "italic"
2259
- }
2260
- };
2261
2214
  function extractDurationMs(raw) {
2262
2215
  if (raw == null) return NaN;
2263
2216
  if (typeof raw === "object") {
@@ -2352,7 +2305,7 @@ function MotionSample({ path, speed = 1, runKey = 0 }) {
2352
2305
  reducedMotion
2353
2306
  ]);
2354
2307
  if (reducedMotion) return /* @__PURE__ */ jsxs("div", {
2355
- style: styles.reducedMotion,
2308
+ className: "sb-motion-sample__reduced-motion",
2356
2309
  children: [
2357
2310
  "Animation suppressed by ",
2358
2311
  /* @__PURE__ */ jsx("code", { children: "prefers-reduced-motion: reduce" }),
@@ -2360,13 +2313,10 @@ function MotionSample({ path, speed = 1, runKey = 0 }) {
2360
2313
  ]
2361
2314
  });
2362
2315
  return /* @__PURE__ */ jsx("div", {
2363
- style: styles.track,
2316
+ className: "sb-motion-sample__track",
2364
2317
  children: /* @__PURE__ */ jsx("div", {
2365
- style: {
2366
- ...styles.ball,
2367
- left: phase === 1 ? "calc(100% - 32px)" : "4px",
2368
- transition: `left ${scaledDuration}ms ${easing}`
2369
- },
2318
+ className: cx("sb-motion-sample__ball", phase === 1 ? "sb-motion-sample__ball--end" : "sb-motion-sample__ball--start"),
2319
+ style: { transition: `left ${scaledDuration}ms ${easing}` },
2370
2320
  "aria-hidden": true
2371
2321
  })
2372
2322
  });
@@ -2554,8 +2504,8 @@ function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg"
2554
2504
  children: [/* @__PURE__ */ jsx("div", {
2555
2505
  className: "sb-opacity-scale__swatch",
2556
2506
  style: {
2557
- "--sb-opacity-scale-color": sampleColorVar,
2558
- "--sb-opacity-scale-alpha": String(row.opacity)
2507
+ "--swatchbook-opacity-scale-color": sampleColorVar,
2508
+ "--swatchbook-opacity-scale-alpha": String(row.opacity)
2559
2509
  },
2560
2510
  "aria-hidden": true
2561
2511
  }), /* @__PURE__ */ jsxs("div", {
@@ -2608,20 +2558,10 @@ function useSwatchbookData() {
2608
2558
  }
2609
2559
  //#endregion
2610
2560
  //#region src/shadow-preview/ShadowSample.tsx
2611
- const sampleStyle = {
2612
- width: 120,
2613
- height: 56,
2614
- background: SURFACE_RAISED,
2615
- border: BORDER_FAINT,
2616
- borderRadius: 6
2617
- };
2618
2561
  function ShadowSample({ path }) {
2619
- const cssVar = resolveCssVar(path, useProject());
2620
2562
  return /* @__PURE__ */ jsx("div", {
2621
- style: {
2622
- ...sampleStyle,
2623
- boxShadow: cssVar
2624
- },
2563
+ className: "sb-shadow-sample",
2564
+ style: { boxShadow: resolveCssVar(path, useProject()) },
2625
2565
  "aria-hidden": true
2626
2566
  });
2627
2567
  }
@@ -2996,10 +2936,7 @@ function AxisVariance({ path }) {
2996
2936
  }),
2997
2937
  value,
2998
2938
  /* @__PURE__ */ jsx("span", {
2999
- style: {
3000
- opacity: .6,
3001
- marginLeft: 8
3002
- },
2939
+ className: "sb-token-detail__constant-label-note",
3003
2940
  children: "same across every axis"
3004
2941
  })
3005
2942
  ]
@@ -3033,8 +2970,7 @@ function AxisVariance({ path }) {
3033
2970
  "data-axis": axisName,
3034
2971
  "data-context": row.ctx,
3035
2972
  children: [/* @__PURE__ */ jsx("td", {
3036
- className: "sb-token-detail__theme-cell",
3037
- style: { width: "30%" },
2973
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--label",
3038
2974
  children: row.ctx
3039
2975
  }), /* @__PURE__ */ jsxs("td", {
3040
2976
  className: "sb-token-detail__theme-cell",
@@ -3061,22 +2997,14 @@ function AxisVariance({ path }) {
3061
2997
  children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
3062
2998
  className: "sb-token-detail__theme-row",
3063
2999
  children: [/* @__PURE__ */ jsxs("th", {
3064
- className: "sb-token-detail__theme-cell",
3065
- style: {
3066
- textAlign: "left",
3067
- opacity: .7
3068
- },
3000
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--header",
3069
3001
  children: [
3070
3002
  rowAxis.name,
3071
3003
  " \\ ",
3072
3004
  colAxis.name
3073
3005
  ]
3074
3006
  }), colAxis.contexts.map((col) => /* @__PURE__ */ jsx("th", {
3075
- className: "sb-token-detail__theme-cell",
3076
- style: {
3077
- textAlign: "left",
3078
- opacity: .7
3079
- },
3007
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--header",
3080
3008
  children: col
3081
3009
  }, col))]
3082
3010
  }) }), /* @__PURE__ */ jsx("tbody", { children: rowAxis.contexts.map((row) => /* @__PURE__ */ jsxs("tr", {
@@ -3106,8 +3034,7 @@ function AxisVariance({ path }) {
3106
3034
  }, row)) })]
3107
3035
  }),
3108
3036
  extra.length > 0 && /* @__PURE__ */ jsxs("div", {
3109
- className: "sb-token-detail__aliased-by-truncated",
3110
- style: { marginTop: 6 },
3037
+ className: "sb-token-detail__aliased-by-truncated sb-token-detail__aliased-by-truncated--axis-note",
3111
3038
  children: [
3112
3039
  "Values also vary with ",
3113
3040
  extra.map((a) => a.name).join(", "),
@@ -3218,7 +3145,7 @@ function CompositeBreakdownContent({ type, rawValue, partialAliasOf, resolved, c
3218
3145
  children: layers.map((layer, i) => {
3219
3146
  const v = layer;
3220
3147
  return /* @__PURE__ */ jsxs("div", {
3221
- style: { display: "contents" },
3148
+ className: "sb-token-detail__shadow-layer",
3222
3149
  children: [
3223
3150
  multi && /* @__PURE__ */ jsxs("div", {
3224
3151
  className: "sb-token-detail__breakdown-layer-header",
@@ -3560,11 +3487,8 @@ function TransitionSample({ transition, durationMs }) {
3560
3487
  return /* @__PURE__ */ jsx("div", {
3561
3488
  className: "sb-token-detail__motion-track",
3562
3489
  children: /* @__PURE__ */ jsx("div", {
3563
- className: "sb-token-detail__motion-ball",
3564
- style: {
3565
- left: phase === 1 ? "calc(100% - 28px)" : "4px",
3566
- transition
3567
- },
3490
+ className: cx("sb-token-detail__motion-ball", phase === 1 ? "sb-token-detail__motion-ball--end" : "sb-token-detail__motion-ball--start"),
3491
+ style: { transition },
3568
3492
  "aria-hidden": true
3569
3493
  })
3570
3494
  });
@@ -3788,7 +3712,7 @@ function TokenDetail({ path, heading }) {
3788
3712
  outOfGamut && /* @__PURE__ */ jsx("span", {
3789
3713
  title: "Out of sRGB gamut for this format",
3790
3714
  "aria-label": "out of gamut",
3791
- style: { marginLeft: 6 },
3715
+ className: "sb-token-detail__out-of-gamut-icon",
3792
3716
  children: "⚠"
3793
3717
  }),
3794
3718
  /* @__PURE__ */ jsx(CopyButton$1, {
@@ -4262,7 +4186,10 @@ function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true,
4262
4186
  }, [visibleTree, searchExpanded]);
4263
4187
  if (tree.length === 0) return /* @__PURE__ */ jsx("div", {
4264
4188
  ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4265
- 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." })
4189
+ children: /* @__PURE__ */ jsx("div", {
4190
+ className: "sb-block__empty",
4191
+ 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."
4192
+ })
4266
4193
  });
4267
4194
  return /* @__PURE__ */ jsxs("div", {
4268
4195
  ...blockWrapperAttrs(cssVarPrefix, activeAxes),
@@ -4523,6 +4450,7 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
4523
4450
  function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, id, indicators }) {
4524
4451
  const { resolved, activeTheme, activeAxes, cssVarPrefix, listing, varianceByPath, indicators: indicatorBaseline } = useProject();
4525
4452
  const colorFormat = useColorFormat();
4453
+ const rootFontSize = useRootFontSize();
4526
4454
  const blockKey = useBlockKey("TokenTable", [
4527
4455
  filter,
4528
4456
  type,
@@ -4544,7 +4472,8 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
4544
4472
  return true;
4545
4473
  }), {
4546
4474
  by: sortBy,
4547
- dir: sortDir
4475
+ dir: sortDir,
4476
+ rootFontSizePx: rootFontSize
4548
4477
  }).map(([path, token]) => {
4549
4478
  const isColor = token.$type === "color";
4550
4479
  const color = isColor ? resolveColorValue(path, token.$value, colorFormat, projectFields) : null;
@@ -4565,7 +4494,8 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
4565
4494
  type,
4566
4495
  colorFormat,
4567
4496
  sortBy,
4568
- sortDir
4497
+ sortDir,
4498
+ rootFontSize
4569
4499
  ]);
4570
4500
  const visibleRows = useMemo(() => {
4571
4501
  if (!searchable || deferredQuery.trim() === "") return rows;