@unpunnyfuns/swatchbook-blocks 0.17.0 → 0.19.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.d.mts CHANGED
@@ -84,6 +84,34 @@ interface VirtualTokenShape {
84
84
  aliasChain?: readonly string[];
85
85
  aliasedBy?: readonly string[];
86
86
  }
87
+ /**
88
+ * Subset of `@terrazzo/plugin-token-listing`'s `ListedToken` that the
89
+ * snapshot carries. Blocks read `names.css` for the authoritative CSS
90
+ * variable name and `previewValue` for the display-ready CSS string.
91
+ * `source.loc` enables "jump to authoring source" affordances.
92
+ *
93
+ * Only the fields blocks consume are typed here; the plugin's full shape
94
+ * lives in `@unpunnyfuns/swatchbook-core`.
95
+ */
96
+ interface VirtualTokenListingShape {
97
+ names: Record<string, string>;
98
+ previewValue?: string | number;
99
+ source?: {
100
+ resource: string;
101
+ loc?: {
102
+ start: {
103
+ line: number;
104
+ column: number;
105
+ offset: number;
106
+ };
107
+ end: {
108
+ line: number;
109
+ column: number;
110
+ offset: number;
111
+ };
112
+ };
113
+ };
114
+ }
87
115
  interface VirtualPresetShape {
88
116
  name: string;
89
117
  axes: Partial<Record<string, string>>;
@@ -106,6 +134,14 @@ interface ProjectSnapshot {
106
134
  cssVarPrefix: string;
107
135
  diagnostics: readonly VirtualDiagnosticShape[];
108
136
  css: string;
137
+ /**
138
+ * Path-indexed Token Listing data produced by
139
+ * `@terrazzo/plugin-token-listing`. Blocks prefer reading authoritative
140
+ * CSS var names and preview values from here; empty for non-resolver
141
+ * projects. Treat as enrichment — fall back gracefully when a path is
142
+ * absent.
143
+ */
144
+ listing?: Readonly<Record<string, VirtualTokenListingShape>>;
109
145
  }
110
146
  /**
111
147
  * Context carrying the full {@link ProjectSnapshot}. `null` sentinel lets
@@ -767,5 +803,5 @@ declare function TypographyScale({
767
803
  sortDir
768
804
  }: TypographyScaleProps): ReactElement;
769
805
  //#endregion
770
- export { AliasChain, type AliasChainProps, AliasedBy, type AliasedByProps, AxesContext, AxisVariance, type AxisVarianceProps, BorderPreview, type BorderPreviewProps, BorderSample, type BorderSampleProps, COLOR_FORMATS, type ColorFormat, ColorFormatContext, ColorPalette, type ColorPaletteProps, ColorTable, type ColorTableProps, CompositeBreakdown, type CompositeBreakdownProps, CompositePreview, type CompositePreviewProps, ConsumerOutput, type ConsumerOutputProps, Diagnostics, type DiagnosticsProps, DimensionBar, type DimensionBarProps, type DimensionKind, DimensionScale, type DimensionScaleProps, FontFamilySample, type FontFamilySampleProps, FontWeightScale, type FontWeightScaleProps, type FormatColorResult, GradientPalette, type GradientPaletteProps, MotionPreview, type MotionPreviewProps, MotionSample, type MotionSampleProps, type MotionSpeed, type NormalizedColor, type ProjectSnapshot, ShadowPreview, type ShadowPreviewProps, ShadowSample, type ShadowSampleProps, StrokeStyleSample, type StrokeStyleSampleProps, SwatchbookContext, SwatchbookProvider, type SwatchbookProviderProps, ThemeContext, TokenDetail, type TokenDetailProps, TokenHeader, type TokenHeaderProps, TokenNavigator, type TokenNavigatorProps, TokenTable, type TokenTableProps, TokenUsageSnippet, type TokenUsageSnippetProps, TypographyScale, type TypographyScaleProps, type VirtualAxisShape as VirtualAxis, type VirtualAxisShape, type VirtualDiagnosticShape as VirtualDiagnostic, type VirtualDiagnosticShape, type VirtualPresetShape as VirtualPreset, type VirtualPresetShape, type VirtualThemeShape as VirtualTheme, type VirtualThemeShape, type VirtualTokenShape as VirtualToken, type VirtualTokenShape, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
806
+ export { AliasChain, type AliasChainProps, AliasedBy, type AliasedByProps, AxesContext, AxisVariance, type AxisVarianceProps, BorderPreview, type BorderPreviewProps, BorderSample, type BorderSampleProps, COLOR_FORMATS, type ColorFormat, ColorFormatContext, ColorPalette, type ColorPaletteProps, ColorTable, type ColorTableProps, CompositeBreakdown, type CompositeBreakdownProps, CompositePreview, type CompositePreviewProps, ConsumerOutput, type ConsumerOutputProps, Diagnostics, type DiagnosticsProps, DimensionBar, type DimensionBarProps, type DimensionKind, DimensionScale, type DimensionScaleProps, FontFamilySample, type FontFamilySampleProps, FontWeightScale, type FontWeightScaleProps, type FormatColorResult, GradientPalette, type GradientPaletteProps, MotionPreview, type MotionPreviewProps, MotionSample, type MotionSampleProps, type MotionSpeed, type NormalizedColor, type ProjectSnapshot, ShadowPreview, type ShadowPreviewProps, ShadowSample, type ShadowSampleProps, StrokeStyleSample, type StrokeStyleSampleProps, SwatchbookContext, SwatchbookProvider, type SwatchbookProviderProps, ThemeContext, TokenDetail, type TokenDetailProps, TokenHeader, type TokenHeaderProps, TokenNavigator, type TokenNavigatorProps, TokenTable, type TokenTableProps, TokenUsageSnippet, type TokenUsageSnippetProps, TypographyScale, type TypographyScaleProps, type VirtualAxisShape as VirtualAxis, type VirtualAxisShape, type VirtualDiagnosticShape as VirtualDiagnostic, type VirtualDiagnosticShape, type VirtualPresetShape as VirtualPreset, type VirtualPresetShape, type VirtualThemeShape as VirtualTheme, type VirtualThemeShape, type VirtualTokenShape as VirtualToken, type VirtualTokenShape, type VirtualTokenListingShape, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
771
807
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -2,8 +2,9 @@ import './style.css';
2
2
  import Color from "colorjs.io";
3
3
  import { Fragment, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
+ import { makeCSSVar } from "@terrazzo/token-tools/css";
5
6
  import { addons } from "storybook/preview-api";
6
- import { axes, css, cssVarPrefix, defaultTheme, diagnostics, presets, themes, themesResolved } from "virtual:swatchbook/tokens";
7
+ import { axes, css, cssVarPrefix, defaultTheme, diagnostics, listing, presets, themes, themesResolved } from "virtual:swatchbook/tokens";
7
8
  import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
8
9
  import cx from "clsx";
9
10
  import { analyzeAxisVariance } from "@unpunnyfuns/swatchbook-core/variance";
@@ -355,6 +356,7 @@ let snapshot = {
355
356
  diagnostics,
356
357
  css,
357
358
  cssVarPrefix,
359
+ listing: listing ?? {},
358
360
  version: 0
359
361
  };
360
362
  const listeners = /* @__PURE__ */ new Set();
@@ -372,6 +374,7 @@ function ensureSubscribed() {
372
374
  diagnostics: payload.diagnostics ?? snapshot.diagnostics,
373
375
  css: payload.css ?? snapshot.css,
374
376
  cssVarPrefix: payload.cssVarPrefix ?? snapshot.cssVarPrefix,
377
+ listing: payload.listing ?? snapshot.listing,
375
378
  version: snapshot.version + 1
376
379
  };
377
380
  for (const cb of listeners) cb();
@@ -429,7 +432,8 @@ function snapshotToData(snapshot) {
429
432
  themesResolved: snapshot.themesResolved,
430
433
  resolved: snapshot.themesResolved[snapshot.activeTheme] ?? {},
431
434
  diagnostics: snapshot.diagnostics,
432
- cssVarPrefix: snapshot.cssVarPrefix
435
+ cssVarPrefix: snapshot.cssVarPrefix,
436
+ listing: snapshot.listing ?? {}
433
437
  };
434
438
  }
435
439
  /**
@@ -478,12 +482,56 @@ function useVirtualModuleFallback(enabled) {
478
482
  themesResolved: tokens.themesResolved,
479
483
  resolved: tokens.themesResolved[activeTheme] ?? {},
480
484
  diagnostics: tokens.diagnostics,
481
- cssVarPrefix: tokens.cssVarPrefix
485
+ cssVarPrefix: tokens.cssVarPrefix,
486
+ listing: tokens.listing
482
487
  };
483
488
  }
489
+ /**
490
+ * Thin wrapper around Terrazzo's `makeCSSVar` so the block-display surface
491
+ * and `packages/core/src/css.ts`'s emitter share one implementation. Any
492
+ * future naming-policy shift in Terrazzo (casing, unicode, prefix handling)
493
+ * reaches both surfaces at once instead of needing a parallel update here.
494
+ */
484
495
  function makeCssVar(path, prefix) {
485
- const tail = path.split(".").map((segment) => segment.replaceAll(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase()).join("-");
486
- return prefix ? `var(--${prefix}-${tail})` : `var(--${tail})`;
496
+ return prefix ? makeCSSVar(path, {
497
+ prefix,
498
+ wrapVar: true
499
+ }) : makeCSSVar(path, { wrapVar: true });
500
+ }
501
+ /**
502
+ * Resolve a token's CSS var reference, preferring the authoritative name
503
+ * emitted by `@terrazzo/plugin-css` (as recorded by
504
+ * `@terrazzo/plugin-token-listing` in the snapshot's `listing` field).
505
+ * Falls back to `makeCssVar` when the listing lacks an entry for this
506
+ * path — covers non-resolver projects, hand-built snapshots, and any
507
+ * listing-plugin miss.
508
+ */
509
+ function resolveCssVar(path, project) {
510
+ const listed = project.listing[path]?.names?.["css"];
511
+ if (listed) return `var(${listed})`;
512
+ return makeCssVar(path, project.cssVarPrefix);
513
+ }
514
+ /**
515
+ * Resolve a color value's display string + gamut flag, preferring the
516
+ * listing's `previewValue` when the user's active color-format matches
517
+ * plugin-css's output (hex). For any other format we fall back to
518
+ * `formatColor` so the toolbar's inspection modes (rgb / hsl / oklch /
519
+ * raw) keep working — the listing has only one canonical format.
520
+ *
521
+ * Pass `path === undefined` when resolving a sub-color inside a composite
522
+ * (shadow / border / gradient stop): composites' `previewValue` covers
523
+ * the whole token's rendering, not the individual channel, so there's no
524
+ * listing entry to key against.
525
+ */
526
+ function resolveColorValue(path, raw, colorFormat, project) {
527
+ if (path !== void 0 && colorFormat === "hex") {
528
+ const listed = project.listing[path]?.previewValue;
529
+ if (typeof listed === "string") return {
530
+ value: listed,
531
+ outOfGamut: false
532
+ };
533
+ }
534
+ return formatColor(raw, colorFormat);
487
535
  }
488
536
  /**
489
537
  * Match a dot-separated DTCG token path against a block `filter` prop.
@@ -519,8 +567,7 @@ const sampleStyle$1 = {
519
567
  borderRadius: 6
520
568
  };
521
569
  function BorderSample({ path }) {
522
- const { cssVarPrefix } = useProject();
523
- const cssVar = makeCssVar(path, cssVarPrefix);
570
+ const cssVar = resolveCssVar(path, useProject());
524
571
  return /* @__PURE__ */ jsx("div", {
525
572
  style: {
526
573
  ...sampleStyle$1,
@@ -694,7 +741,8 @@ function formatSubColor$1(raw, format) {
694
741
  return formatColor(raw, format).value;
695
742
  }
696
743
  function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
697
- const { resolved, activeTheme, cssVarPrefix } = useProject();
744
+ const project = useProject();
745
+ const { resolved, activeTheme, cssVarPrefix } = project;
698
746
  const colorFormat = useColorFormat();
699
747
  const rows = useMemo(() => {
700
748
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
@@ -705,13 +753,13 @@ function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
705
753
  dir: sortDir
706
754
  }).map(([path, token]) => ({
707
755
  path,
708
- cssVar: makeCssVar(path, cssVarPrefix),
756
+ cssVar: resolveCssVar(path, project),
709
757
  value: token.$value ?? {}
710
758
  }));
711
759
  }, [
712
760
  resolved,
713
761
  filter,
714
- cssVarPrefix,
762
+ project,
715
763
  sortBy,
716
764
  sortDir
717
765
  ]);
@@ -786,7 +834,8 @@ function fixedPrefixLength(filter) {
786
834
  return fixed;
787
835
  }
788
836
  function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "asc" }) {
789
- const { resolved, activeTheme, cssVarPrefix } = useProject();
837
+ const project = useProject();
838
+ const { resolved, activeTheme, cssVarPrefix } = project;
790
839
  const colorFormat = useColorFormat();
791
840
  const groups = useMemo(() => {
792
841
  const entries = sortTokens(Object.entries(resolved).filter(([path, token]) => {
@@ -804,11 +853,11 @@ function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "as
804
853
  const groupKey = segments.slice(0, effectiveGroupBy).join(".");
805
854
  const leaf = segments.slice(effectiveGroupBy).join(".") || segments.at(-1) || path;
806
855
  const list = bucket.get(groupKey) ?? [];
807
- const formatted = formatColor(token.$value, colorFormat);
856
+ const formatted = resolveColorValue(path, token.$value, colorFormat, project);
808
857
  list.push({
809
858
  path,
810
859
  leaf,
811
- cssVar: makeCssVar(path, cssVarPrefix),
860
+ cssVar: resolveCssVar(path, project),
812
861
  value: formatted.value,
813
862
  outOfGamut: formatted.outOfGamut
814
863
  });
@@ -819,7 +868,7 @@ function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "as
819
868
  resolved,
820
869
  filter,
821
870
  groupBy,
822
- cssVarPrefix,
871
+ project,
823
872
  colorFormat,
824
873
  sortBy,
825
874
  sortDir
@@ -924,7 +973,8 @@ function CopyButton$1({ value, label, variant = "icon", className }) {
924
973
  const BASE_LABEL = "base";
925
974
  const COLUMN_COUNT = 6;
926
975
  function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, variants }) {
927
- const { resolved, activeTheme, cssVarPrefix } = useProject();
976
+ const project = useProject();
977
+ const { resolved, activeTheme, cssVarPrefix } = project;
928
978
  const colorFormat = useColorFormat();
929
979
  const [query, setQuery] = useState("");
930
980
  const [selectedByBase, setSelectedByBase] = useState({});
@@ -941,7 +991,7 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
941
991
  const groupMap = /* @__PURE__ */ new Map();
942
992
  for (const [path, token] of sorted) {
943
993
  const raw = token.$value;
944
- const hex = formatColor(raw, "hex");
994
+ const hex = resolveColorValue(path, raw, "hex", project);
945
995
  const hsl = formatColor(raw, "hsl");
946
996
  const oklch = formatColor(raw, "oklch");
947
997
  const active = pickActiveFormat(raw, colorFormat, hex, hsl, oklch);
@@ -949,7 +999,7 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
949
999
  const variant = {
950
1000
  label: match?.label ?? BASE_LABEL,
951
1001
  path,
952
- cssVar: makeCssVar(path, cssVarPrefix),
1002
+ cssVar: resolveCssVar(path, project),
953
1003
  value: active.value,
954
1004
  outOfGamut: active.outOfGamut,
955
1005
  hex: hex.value,
@@ -981,7 +1031,7 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
981
1031
  }, [
982
1032
  resolved,
983
1033
  filter,
984
- cssVarPrefix,
1034
+ project,
985
1035
  sortBy,
986
1036
  sortDir,
987
1037
  defs,
@@ -1512,8 +1562,9 @@ function toPixels$1(raw) {
1512
1562
  }
1513
1563
  }
1514
1564
  function DimensionBar({ path, kind = "length" }) {
1515
- const { resolved, cssVarPrefix } = useProject();
1516
- const cssVar = makeCssVar(path, cssVarPrefix);
1565
+ const project = useProject();
1566
+ const { resolved } = project;
1567
+ const cssVar = resolveCssVar(path, project);
1517
1568
  const token = resolved[path];
1518
1569
  const pxValue = toPixels$1(token?.$value);
1519
1570
  const cappedValue = Number.isFinite(pxValue) && pxValue > MAX_RENDER_PX$1 ? `${MAX_RENDER_PX$1}px` : cssVar;
@@ -1564,9 +1615,15 @@ function DimensionBar({ path, kind = "length" }) {
1564
1615
  *
1565
1616
  * Unknown object shapes fall through to truncated JSON.
1566
1617
  */
1567
- function formatTokenValue(value, $type, colorFormat) {
1618
+ function formatTokenValue(value, $type, colorFormat, listingEntry) {
1568
1619
  if (value == null) return "";
1569
1620
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
1621
+ const preview = listingEntry?.previewValue;
1622
+ if (preview !== void 0) {
1623
+ const previewStr = typeof preview === "string" ? preview : String(preview);
1624
+ if ($type !== "color") return previewStr;
1625
+ if (colorFormat === "hex") return previewStr;
1626
+ }
1570
1627
  switch ($type) {
1571
1628
  case "color": return formatColor(value, colorFormat).value;
1572
1629
  case "dimension":
@@ -1696,7 +1753,8 @@ function toPixels(raw) {
1696
1753
  }
1697
1754
  }
1698
1755
  function DimensionScale({ filter, kind = "length", caption, sortBy = "value", sortDir = "asc" }) {
1699
- const { resolved, activeTheme, cssVarPrefix } = useProject();
1756
+ const project = useProject();
1757
+ const { resolved, activeTheme, cssVarPrefix } = project;
1700
1758
  const rows = useMemo(() => {
1701
1759
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
1702
1760
  if (token.$type !== "dimension") return false;
@@ -1708,8 +1766,8 @@ function DimensionScale({ filter, kind = "length", caption, sortBy = "value", so
1708
1766
  const pxValue = toPixels(token.$value);
1709
1767
  return {
1710
1768
  path,
1711
- cssVar: makeCssVar(path, cssVarPrefix),
1712
- displayValue: formatTokenValue(token.$value, token.$type, "raw"),
1769
+ cssVar: resolveCssVar(path, project),
1770
+ displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path]),
1713
1771
  pxValue,
1714
1772
  capped: Number.isFinite(pxValue) && pxValue > MAX_RENDER_PX
1715
1773
  };
@@ -1717,7 +1775,7 @@ function DimensionScale({ filter, kind = "length", caption, sortBy = "value", so
1717
1775
  }, [
1718
1776
  resolved,
1719
1777
  filter,
1720
- cssVarPrefix,
1778
+ project,
1721
1779
  sortBy,
1722
1780
  sortDir
1723
1781
  ]);
@@ -1777,7 +1835,8 @@ function stackString(raw) {
1777
1835
  return "";
1778
1836
  }
1779
1837
  function FontFamilySample({ filter, sample = "The quick brown fox jumps over the lazy dog.", caption, sortBy = "path", sortDir = "asc" }) {
1780
- const { resolved, activeTheme, cssVarPrefix } = useProject();
1838
+ const project = useProject();
1839
+ const { resolved, activeTheme, cssVarPrefix } = project;
1781
1840
  const rows = useMemo(() => {
1782
1841
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
1783
1842
  if (token.$type !== "fontFamily") return false;
@@ -1787,13 +1846,13 @@ function FontFamilySample({ filter, sample = "The quick brown fox jumps over the
1787
1846
  dir: sortDir
1788
1847
  }).map(([path, token]) => ({
1789
1848
  path,
1790
- cssVar: makeCssVar(path, cssVarPrefix),
1849
+ cssVar: resolveCssVar(path, project),
1791
1850
  stack: stackString(token.$value)
1792
1851
  }));
1793
1852
  }, [
1794
1853
  resolved,
1795
1854
  filter,
1796
- cssVarPrefix,
1855
+ project,
1797
1856
  sortBy,
1798
1857
  sortDir
1799
1858
  ]);
@@ -1847,7 +1906,8 @@ function toWeight(raw) {
1847
1906
  return NaN;
1848
1907
  }
1849
1908
  function FontWeightScale({ filter, sample = "Aa", caption, sortBy = "value", sortDir = "asc" }) {
1850
- const { resolved, activeTheme, cssVarPrefix } = useProject();
1909
+ const project = useProject();
1910
+ const { resolved, activeTheme, cssVarPrefix } = project;
1851
1911
  const rows = useMemo(() => {
1852
1912
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
1853
1913
  if (token.$type !== "fontWeight") return false;
@@ -1857,14 +1917,14 @@ function FontWeightScale({ filter, sample = "Aa", caption, sortBy = "value", sor
1857
1917
  dir: sortDir
1858
1918
  }).map(([path, token]) => ({
1859
1919
  path,
1860
- cssVar: makeCssVar(path, cssVarPrefix),
1920
+ cssVar: resolveCssVar(path, project),
1861
1921
  display: token.$value == null ? "" : String(token.$value),
1862
1922
  weight: toWeight(token.$value)
1863
1923
  }));
1864
1924
  }, [
1865
1925
  resolved,
1866
1926
  filter,
1867
- cssVarPrefix,
1927
+ project,
1868
1928
  sortBy,
1869
1929
  sortDir
1870
1930
  ]);
@@ -1926,7 +1986,8 @@ function stopKey(path, stop, fallback) {
1926
1986
  return `${path}|${stop.position ?? fallback}|${stopCssColor(stop)}`;
1927
1987
  }
1928
1988
  function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" }) {
1929
- const { resolved, activeTheme, cssVarPrefix } = useProject();
1989
+ const project = useProject();
1990
+ const { resolved, activeTheme, cssVarPrefix } = project;
1930
1991
  const colorFormat = useColorFormat();
1931
1992
  const rows = useMemo(() => {
1932
1993
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
@@ -1937,13 +1998,13 @@ function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" })
1937
1998
  dir: sortDir
1938
1999
  }).map(([path, token]) => ({
1939
2000
  path,
1940
- cssVar: makeCssVar(path, cssVarPrefix),
2001
+ cssVar: resolveCssVar(path, project),
1941
2002
  stops: asStops(token.$value)
1942
2003
  }));
1943
2004
  }, [
1944
2005
  resolved,
1945
2006
  filter,
1946
- cssVarPrefix,
2007
+ project,
1947
2008
  sortBy,
1948
2009
  sortDir
1949
2010
  ]);
@@ -2174,7 +2235,8 @@ function formatSpec(row) {
2174
2235
  }
2175
2236
  }
2176
2237
  function MotionPreview({ filter, caption }) {
2177
- const { resolved, activeTheme, cssVarPrefix } = useProject();
2238
+ const project = useProject();
2239
+ const { resolved, activeTheme, cssVarPrefix } = project;
2178
2240
  const [speed, setSpeed] = useState(1);
2179
2241
  const [run, setRun] = useState(0);
2180
2242
  const reducedMotion = usePrefersReducedMotion();
@@ -2193,7 +2255,7 @@ function MotionPreview({ filter, caption }) {
2193
2255
  if (!spec) continue;
2194
2256
  collected.push({
2195
2257
  path,
2196
- cssVar: makeCssVar(path, cssVarPrefix),
2258
+ cssVar: resolveCssVar(path, project),
2197
2259
  durationMs: spec.durationMs,
2198
2260
  easing: spec.easing,
2199
2261
  kind
@@ -2207,7 +2269,7 @@ function MotionPreview({ filter, caption }) {
2207
2269
  }, [
2208
2270
  resolved,
2209
2271
  filter,
2210
- cssVarPrefix
2272
+ project
2211
2273
  ]);
2212
2274
  const captionText = caption ?? `${rows.length} motion token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
2213
2275
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
@@ -2311,8 +2373,7 @@ const sampleStyle = {
2311
2373
  borderRadius: 6
2312
2374
  };
2313
2375
  function ShadowSample({ path }) {
2314
- const { cssVarPrefix } = useProject();
2315
- const cssVar = makeCssVar(path, cssVarPrefix);
2376
+ const cssVar = resolveCssVar(path, useProject());
2316
2377
  return /* @__PURE__ */ jsx("div", {
2317
2378
  style: {
2318
2379
  ...sampleStyle,
@@ -2346,7 +2407,8 @@ function layerKey(path, layer, fallback) {
2346
2407
  return `${path}|${`${formatDimension(layer.offsetX)},${formatDimension(layer.offsetY)}`}|${formatDimension(layer.blur)}|${formatDimension(layer.spread)}|${fallback}`;
2347
2408
  }
2348
2409
  function ShadowPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2349
- const { resolved, activeTheme, cssVarPrefix } = useProject();
2410
+ const project = useProject();
2411
+ const { resolved, activeTheme, cssVarPrefix } = project;
2350
2412
  const colorFormat = useColorFormat();
2351
2413
  const rows = useMemo(() => {
2352
2414
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
@@ -2357,13 +2419,13 @@ function ShadowPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2357
2419
  dir: sortDir
2358
2420
  }).map(([path, token]) => ({
2359
2421
  path,
2360
- cssVar: makeCssVar(path, cssVarPrefix),
2422
+ cssVar: resolveCssVar(path, project),
2361
2423
  layers: asLayers(token.$value)
2362
2424
  }));
2363
2425
  }, [
2364
2426
  resolved,
2365
2427
  filter,
2366
- cssVarPrefix,
2428
+ project,
2367
2429
  sortBy,
2368
2430
  sortDir
2369
2431
  ]);
@@ -2458,7 +2520,8 @@ function extractCssStyle(value) {
2458
2520
  return null;
2459
2521
  }
2460
2522
  function StrokeStyleSample({ filter, caption, sortBy = "path", sortDir = "asc" }) {
2461
- const { resolved, activeTheme, cssVarPrefix } = useProject();
2523
+ const project = useProject();
2524
+ const { resolved, activeTheme, cssVarPrefix } = project;
2462
2525
  const rows = useMemo(() => {
2463
2526
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
2464
2527
  if (token.$type !== "strokeStyle") return false;
@@ -2468,14 +2531,14 @@ function StrokeStyleSample({ filter, caption, sortBy = "path", sortDir = "asc" }
2468
2531
  dir: sortDir
2469
2532
  }).map(([path, token]) => ({
2470
2533
  path,
2471
- cssVar: makeCssVar(path, cssVarPrefix),
2472
- displayValue: formatTokenValue(token.$value, token.$type, "raw"),
2534
+ cssVar: resolveCssVar(path, project),
2535
+ displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path]),
2473
2536
  cssStyle: extractCssStyle(token.$value)
2474
2537
  }));
2475
2538
  }, [
2476
2539
  resolved,
2477
2540
  filter,
2478
- cssVarPrefix,
2541
+ project,
2479
2542
  sortBy,
2480
2543
  sortDir
2481
2544
  ]);
@@ -2524,11 +2587,12 @@ function StrokeStyleSample({ filter, caption, sortBy = "path", sortDir = "asc" }
2524
2587
  //#endregion
2525
2588
  //#region src/token-detail/internal.ts
2526
2589
  function useTokenDetailData(path) {
2527
- const { activeTheme, activeAxes, axes, themes, themesResolved, resolved, cssVarPrefix } = useProject();
2590
+ const project = useProject();
2591
+ const { activeTheme, activeAxes, axes, themes, themesResolved, resolved, cssVarPrefix } = project;
2528
2592
  const typedResolved = resolved;
2529
2593
  return {
2530
2594
  token: typedResolved[path],
2531
- cssVar: makeCssVar(path, cssVarPrefix),
2595
+ cssVar: resolveCssVar(path, project),
2532
2596
  activeTheme,
2533
2597
  activeAxes,
2534
2598
  axes,
@@ -3262,8 +3326,11 @@ function TransitionSample({ transition }) {
3262
3326
  //#region src/token-detail/ConsumerOutput.tsx
3263
3327
  function ConsumerOutput({ path }) {
3264
3328
  const { token, cssVar, activeAxes } = useTokenDetailData(path);
3329
+ const { listing } = useProject();
3265
3330
  if (!token) return null;
3266
3331
  const tupleLabel = Object.entries(activeAxes).map(([k, v]) => `${k}=${v}`).join(", ");
3332
+ const names = listing[path]?.names ?? {};
3333
+ const extraPlatforms = Object.keys(names).filter((platform) => platform !== "css" && names[platform]).toSorted();
3267
3334
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
3268
3335
  /* @__PURE__ */ jsx("div", {
3269
3336
  className: "sb-token-detail__section-header",
@@ -3282,9 +3349,18 @@ function ConsumerOutput({ path }) {
3282
3349
  label: "CSS",
3283
3350
  value: cssVar,
3284
3351
  testId: "consumer-output-css"
3285
- })
3352
+ }),
3353
+ extraPlatforms.map((platform) => /* @__PURE__ */ jsx(OutputRow, {
3354
+ label: formatPlatformLabel(platform),
3355
+ value: names[platform],
3356
+ testId: `consumer-output-${platform}`
3357
+ }, platform))
3286
3358
  ] });
3287
3359
  }
3360
+ function formatPlatformLabel(platform) {
3361
+ if (platform.length === 0) return platform;
3362
+ return platform[0].toUpperCase() + platform.slice(1);
3363
+ }
3288
3364
  function OutputRow({ label, value, testId }) {
3289
3365
  return /* @__PURE__ */ jsxs("div", {
3290
3366
  className: "sb-token-detail__consumer-row",
@@ -3402,9 +3478,10 @@ function TokenDetail({ path, heading }) {
3402
3478
  ]
3403
3479
  })
3404
3480
  });
3481
+ const { listing } = useProject();
3405
3482
  const isColor = token.$type === "color";
3406
3483
  const gamut = isColor ? formatColor(token.$value, colorFormat) : null;
3407
- const value = formatTokenValue(token.$value, token.$type, colorFormat);
3484
+ const value = formatTokenValue(token.$value, token.$type, colorFormat, listing[path]);
3408
3485
  const outOfGamut = gamut?.outOfGamut ?? false;
3409
3486
  return /* @__PURE__ */ jsxs("div", {
3410
3487
  ...theme,
@@ -3787,16 +3864,16 @@ function LeafRow({ node, onLeafClick }) {
3787
3864
  });
3788
3865
  }
3789
3866
  function LeafPreview({ path, token }) {
3790
- const { cssVarPrefix } = useProject();
3867
+ const project = useProject();
3791
3868
  const colorFormat = useColorFormat();
3792
3869
  const type = token.$type;
3793
3870
  if (type === "color") {
3794
- const cssVar = makeCssVar(path, cssVarPrefix);
3871
+ const cssVar = resolveCssVar(path, project);
3795
3872
  return /* @__PURE__ */ jsxs("span", {
3796
3873
  className: "sb-token-navigator__preview-box",
3797
3874
  children: [/* @__PURE__ */ jsx("span", {
3798
3875
  className: "sb-token-navigator__value",
3799
- children: formatTokenValue(token.$value, type, colorFormat)
3876
+ children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
3800
3877
  }), /* @__PURE__ */ jsx("span", {
3801
3878
  className: "sb-token-navigator__color-swatch",
3802
3879
  style: { background: cssVar },
@@ -3808,7 +3885,7 @@ function LeafPreview({ path, token }) {
3808
3885
  className: "sb-token-navigator__preview-box",
3809
3886
  children: [/* @__PURE__ */ jsx("span", {
3810
3887
  className: "sb-token-navigator__value",
3811
- children: formatTokenValue(token.$value, type, colorFormat)
3888
+ children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
3812
3889
  }), /* @__PURE__ */ jsx("span", {
3813
3890
  className: "sb-token-navigator__preview-dimension",
3814
3891
  children: /* @__PURE__ */ jsx(DimensionBar, {
@@ -3842,14 +3919,15 @@ function LeafPreview({ path, token }) {
3842
3919
  className: "sb-token-navigator__preview-box",
3843
3920
  children: /* @__PURE__ */ jsx("span", {
3844
3921
  className: "sb-token-navigator__value",
3845
- children: formatTokenValue(token.$value, type, colorFormat)
3922
+ children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
3846
3923
  })
3847
3924
  });
3848
3925
  }
3849
3926
  //#endregion
3850
3927
  //#region src/TokenTable.tsx
3851
3928
  function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect }) {
3852
- const { resolved, activeTheme, cssVarPrefix } = useProject();
3929
+ const project = useProject();
3930
+ const { resolved, activeTheme, cssVarPrefix } = project;
3853
3931
  const colorFormat = useColorFormat();
3854
3932
  const [selectedPath, setSelectedPath] = useState(null);
3855
3933
  const [query, setQuery] = useState("");
@@ -3863,13 +3941,13 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
3863
3941
  dir: sortDir
3864
3942
  }).map(([path, token]) => {
3865
3943
  const isColor = token.$type === "color";
3866
- const color = isColor ? formatColor(token.$value, colorFormat) : null;
3944
+ const color = isColor ? resolveColorValue(path, token.$value, colorFormat, project) : null;
3867
3945
  return {
3868
3946
  path,
3869
3947
  type: token.$type ?? "",
3870
- value: formatTokenValue(token.$value, token.$type, colorFormat),
3948
+ value: formatTokenValue(token.$value, token.$type, colorFormat, project.listing[path]),
3871
3949
  outOfGamut: color?.outOfGamut ?? false,
3872
- cssVar: makeCssVar(path, cssVarPrefix),
3950
+ cssVar: resolveCssVar(path, project),
3873
3951
  isColor
3874
3952
  };
3875
3953
  });
@@ -3877,7 +3955,7 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
3877
3955
  resolved,
3878
3956
  filter,
3879
3957
  type,
3880
- cssVarPrefix,
3958
+ project,
3881
3959
  colorFormat,
3882
3960
  sortBy,
3883
3961
  sortDir