@unpunnyfuns/swatchbook-blocks 0.66.3 → 0.67.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
@@ -75,7 +75,7 @@ function ensureSubscribed$1() {
75
75
  channel.on("setGlobals", onGlobals);
76
76
  }
77
77
  ensureSubscribed$1();
78
- function subscribe$1(cb) {
78
+ function subscribe$2(cb) {
79
79
  ensureSubscribed$1();
80
80
  listeners$1.add(cb);
81
81
  return () => {
@@ -89,7 +89,7 @@ function getServerSnapshot$1() {
89
89
  return snapshot$1;
90
90
  }
91
91
  function useChannelGlobals() {
92
- return useSyncExternalStore(subscribe$1, getSnapshot$1, getServerSnapshot$1);
92
+ return useSyncExternalStore(subscribe$2, getSnapshot$1, getServerSnapshot$1);
93
93
  }
94
94
  //#endregion
95
95
  //#region src/contexts.ts
@@ -212,7 +212,7 @@ function ensureSubscribed() {
212
212
  });
213
213
  }
214
214
  ensureSubscribed();
215
- function subscribe(cb) {
215
+ function subscribe$1(cb) {
216
216
  ensureSubscribed();
217
217
  listeners.add(cb);
218
218
  return () => {
@@ -226,7 +226,7 @@ function getServerSnapshot() {
226
226
  return snapshot;
227
227
  }
228
228
  function useTokenSnapshot() {
229
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
229
+ return useSyncExternalStore(subscribe$1, getSnapshot, getServerSnapshot);
230
230
  }
231
231
  //#endregion
232
232
  //#region src/internal/use-project.ts
@@ -484,11 +484,11 @@ const NUMERIC_TYPES = new Set([
484
484
  "lineHeight"
485
485
  ]);
486
486
  const STRING_TYPES = new Set(["fontFamily", "strokeStyle"]);
487
- function computeSortKey(token) {
487
+ function computeSortKey(token, rootFontSizePx) {
488
488
  const type = token.$type;
489
489
  if (!type) return { kind: "none" };
490
490
  if (NUMERIC_TYPES.has(type)) {
491
- const value = toMagnitude(token.$value);
491
+ const value = toMagnitude(token.$value, rootFontSizePx);
492
492
  return {
493
493
  kind: "numeric",
494
494
  value,
@@ -511,8 +511,9 @@ function sortTokens(entries, options = {}) {
511
511
  const sign = dir === "desc" ? -1 : 1;
512
512
  if (by === "none") return dir === "desc" ? [...entries].toReversed() : [...entries];
513
513
  if (by === "path") return [...entries].toSorted(([a], [b]) => sign * a.localeCompare(b, void 0, { numeric: true }));
514
+ const rootFontSizePx = options.rootFontSizePx ?? 16;
514
515
  const keys = /* @__PURE__ */ new Map();
515
- for (const [, token] of entries) keys.set(token, computeSortKey(token));
516
+ for (const [, token] of entries) keys.set(token, computeSortKey(token, rootFontSizePx));
516
517
  return [...entries].toSorted(([aPath, aTok], [bPath, bTok]) => {
517
518
  const cmp = compareValue(aTok, bTok, keys);
518
519
  if (cmp !== 0) return sign * cmp;
@@ -544,7 +545,7 @@ function compareValue(a, b, keys) {
544
545
  if (ak.kind === "string" && bk.kind === "string") return ak.value.localeCompare(bk.value, void 0, { numeric: true });
545
546
  return 0;
546
547
  }
547
- function toMagnitude(v) {
548
+ function toMagnitude(v, rootFontSizePx) {
548
549
  if (typeof v === "number") return v;
549
550
  if (v && typeof v === "object") {
550
551
  const d = v;
@@ -554,8 +555,7 @@ function toMagnitude(v) {
554
555
  case "px":
555
556
  case "ms": return d.value;
556
557
  case "s": return d.value * 1e3;
557
- case "rem":
558
- case "em": return d.value * 16;
558
+ case "rem": return d.value * rootFontSizePx;
559
559
  default: return d.value;
560
560
  }
561
561
  }
@@ -1702,29 +1702,70 @@ function Diagnostics({ caption } = {}) {
1702
1702
  //#region src/dimension-scale/dimension-px.ts
1703
1703
  /**
1704
1704
  * 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".
1705
+ * purpose of deciding whether to cap the rendered size. `rootFontSizePx`
1706
+ * scales `rem` against the rendering context's actual root font-size
1707
+ * (default 16 for the no-DOM / SSR path); the bar paints at the same
1708
+ * context's `var()`, so passing the measured root keeps the cap decision
1709
+ * aligned with what's drawn. Returns `NaN` for anything other than `px` /
1710
+ * `rem` — `ex` / `ch` / `%`, and the non-DTCG `em` (the `dimension` type
1711
+ * permits only `px | rem`) — which the caller treats as "render at cssVar
1712
+ * but don't cap".
1708
1713
  */
1709
- function toPixels(raw) {
1714
+ function toPixels(raw, rootFontSizePx = 16) {
1710
1715
  if (raw == null || typeof raw !== "object") return NaN;
1711
1716
  const v = raw;
1712
1717
  if (typeof v.value !== "number" || typeof v.unit !== "string") return NaN;
1713
1718
  switch (v.unit) {
1714
1719
  case "px": return v.value;
1715
- case "rem":
1716
- case "em": return v.value * 16;
1720
+ case "rem": return v.value * rootFontSizePx;
1717
1721
  default: return NaN;
1718
1722
  }
1719
1723
  }
1720
1724
  //#endregion
1725
+ //#region src/internal/use-root-font-size.ts
1726
+ function readRootFontSize() {
1727
+ if (typeof document === "undefined") return 16;
1728
+ const fontSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
1729
+ return Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 16;
1730
+ }
1731
+ function subscribe(onChange) {
1732
+ if (typeof window === "undefined") return () => {};
1733
+ window.addEventListener("resize", onChange);
1734
+ return () => window.removeEventListener("resize", onChange);
1735
+ }
1736
+ /**
1737
+ * Root font-size (px) of the context a block renders in, tracked across
1738
+ * viewport changes. `rem` dimension values resolve their `var()` against this
1739
+ * root, so the cap math (`toPixels`) and value sort (`sortTokens`) must scale
1740
+ * `rem` by it rather than a literal 16: a Storybook Docs page, or a responsive
1741
+ * desktop/tablet/mobile breakpoint, can apply a different root to the same
1742
+ * token. Falls back to 16 with no DOM.
1743
+ */
1744
+ function useRootFontSize() {
1745
+ return useSyncExternalStore(subscribe, readRootFontSize, () => 16);
1746
+ }
1747
+ //#endregion
1721
1748
  //#region src/dimension-scale/DimensionBar.tsx
1722
1749
  const styles$1 = {
1750
+ cappedWrap: {
1751
+ display: "inline-flex",
1752
+ alignItems: "center",
1753
+ gap: "var(--swatchbook-space-3xs)",
1754
+ maxWidth: "100%",
1755
+ minWidth: 0
1756
+ },
1757
+ cap: {
1758
+ color: "var(--swatchbook-text-muted)",
1759
+ fontSize: 11,
1760
+ lineHeight: 1,
1761
+ userSelect: "none"
1762
+ },
1723
1763
  bar: {
1724
1764
  height: 14,
1725
1765
  background: "var(--swatchbook-accent-bg, #3b82f6)",
1726
1766
  borderRadius: 2,
1727
- minWidth: 1
1767
+ minWidth: 1,
1768
+ maxWidth: "100%"
1728
1769
  },
1729
1770
  radiusSample: {
1730
1771
  width: 56,
@@ -1739,13 +1780,27 @@ const styles$1 = {
1739
1780
  minHeight: 1
1740
1781
  }
1741
1782
  };
1783
+ function withCap(visual) {
1784
+ return /* @__PURE__ */ jsxs("span", {
1785
+ className: "sb-dimension-bar sb-dimension-bar--capped",
1786
+ style: styles$1.cappedWrap,
1787
+ title: `capped at 480px`,
1788
+ children: [visual, /* @__PURE__ */ jsx("span", {
1789
+ className: "sb-dimension-bar__cap",
1790
+ "aria-hidden": true,
1791
+ children: "…"
1792
+ })]
1793
+ });
1794
+ }
1742
1795
  function DimensionBar({ path, visual = "length" }) {
1743
1796
  const project = useProject();
1744
1797
  const { resolved } = project;
1798
+ const rootFontSize = useRootFontSize();
1745
1799
  const cssVar = resolveCssVar(path, project);
1746
1800
  const token = resolved[path];
1747
- const pxValue = toPixels(token?.$value);
1748
- const cappedValue = Number.isFinite(pxValue) && pxValue > 480 ? `480px` : cssVar;
1801
+ const pxValue = toPixels(token?.$value, rootFontSize);
1802
+ const capped = Number.isFinite(pxValue) && pxValue > 480;
1803
+ const cappedValue = capped ? `480px` : cssVar;
1749
1804
  switch (visual) {
1750
1805
  case "radius": return /* @__PURE__ */ jsx("div", {
1751
1806
  style: {
@@ -1754,21 +1809,27 @@ function DimensionBar({ path, visual = "length" }) {
1754
1809
  },
1755
1810
  "aria-hidden": true
1756
1811
  });
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
- },
1770
- "aria-hidden": true
1771
- });
1812
+ case "size": {
1813
+ const sample = /* @__PURE__ */ jsx("div", {
1814
+ style: {
1815
+ ...styles$1.sizeSample,
1816
+ width: cappedValue,
1817
+ height: cappedValue
1818
+ },
1819
+ "aria-hidden": true
1820
+ });
1821
+ return capped ? withCap(sample) : sample;
1822
+ }
1823
+ default: {
1824
+ const bar = /* @__PURE__ */ jsx("div", {
1825
+ style: {
1826
+ ...styles$1.bar,
1827
+ width: cappedValue
1828
+ },
1829
+ "aria-hidden": true
1830
+ });
1831
+ return capped ? withCap(bar) : bar;
1832
+ }
1772
1833
  }
1773
1834
  }
1774
1835
  //#endregion
@@ -1878,29 +1939,27 @@ function formatUnknown(v) {
1878
1939
  function DimensionScale({ filter, visual = "length", caption, sortBy = "value", sortDir = "asc" }) {
1879
1940
  const project = useProject();
1880
1941
  const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
1942
+ const rootFontSize = useRootFontSize();
1881
1943
  const rows = useMemo(() => {
1882
1944
  return sortTokens(Object.entries(resolved).filter(([path, token]) => {
1883
1945
  if (token.$type !== "dimension") return false;
1884
1946
  return matchPath(path, filter);
1885
1947
  }), {
1886
1948
  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
- });
1949
+ dir: sortDir,
1950
+ rootFontSizePx: rootFontSize
1951
+ }).map(([path, token]) => ({
1952
+ path,
1953
+ cssVar: resolveCssVar(path, project),
1954
+ displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path])
1955
+ }));
1898
1956
  }, [
1899
1957
  resolved,
1900
1958
  filter,
1901
1959
  project,
1902
1960
  sortBy,
1903
- sortDir
1961
+ sortDir,
1962
+ rootFontSize
1904
1963
  ]);
1905
1964
  const captionText = caption ?? `${rows.length} dimension${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
1906
1965
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
@@ -1928,19 +1987,12 @@ function DimensionScale({ filter, visual = "length", caption, sortBy = "value",
1928
1987
  children: row.displayValue
1929
1988
  })]
1930
1989
  }),
1931
- /* @__PURE__ */ jsxs("div", {
1990
+ /* @__PURE__ */ jsx("div", {
1932
1991
  className: "sb-dimension-scale__visual-cell",
1933
- children: [/* @__PURE__ */ jsx(DimensionBar, {
1992
+ children: /* @__PURE__ */ jsx(DimensionBar, {
1934
1993
  path: row.path,
1935
1994
  visual
1936
- }), row.capped && /* @__PURE__ */ jsxs("span", {
1937
- className: "sb-dimension-scale__cap",
1938
- children: [
1939
- "capped at ",
1940
- 480,
1941
- "px"
1942
- ]
1943
- })]
1995
+ })
1944
1996
  }),
1945
1997
  /* @__PURE__ */ jsx("span", {
1946
1998
  className: "sb-dimension-scale__css-var",
@@ -2554,8 +2606,8 @@ function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg"
2554
2606
  children: [/* @__PURE__ */ jsx("div", {
2555
2607
  className: "sb-opacity-scale__swatch",
2556
2608
  style: {
2557
- "--sb-opacity-scale-color": sampleColorVar,
2558
- "--sb-opacity-scale-alpha": String(row.opacity)
2609
+ "--swatchbook-opacity-scale-color": sampleColorVar,
2610
+ "--swatchbook-opacity-scale-alpha": String(row.opacity)
2559
2611
  },
2560
2612
  "aria-hidden": true
2561
2613
  }), /* @__PURE__ */ jsxs("div", {
@@ -4523,6 +4575,7 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
4523
4575
  function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, id, indicators }) {
4524
4576
  const { resolved, activeTheme, activeAxes, cssVarPrefix, listing, varianceByPath, indicators: indicatorBaseline } = useProject();
4525
4577
  const colorFormat = useColorFormat();
4578
+ const rootFontSize = useRootFontSize();
4526
4579
  const blockKey = useBlockKey("TokenTable", [
4527
4580
  filter,
4528
4581
  type,
@@ -4544,7 +4597,8 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
4544
4597
  return true;
4545
4598
  }), {
4546
4599
  by: sortBy,
4547
- dir: sortDir
4600
+ dir: sortDir,
4601
+ rootFontSizePx: rootFontSize
4548
4602
  }).map(([path, token]) => {
4549
4603
  const isColor = token.$type === "color";
4550
4604
  const color = isColor ? resolveColorValue(path, token.$value, colorFormat, projectFields) : null;
@@ -4565,7 +4619,8 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
4565
4619
  type,
4566
4620
  colorFormat,
4567
4621
  sortBy,
4568
- sortDir
4622
+ sortDir,
4623
+ rootFontSize
4569
4624
  ]);
4570
4625
  const visibleRows = useMemo(() => {
4571
4626
  if (!searchable || deferredQuery.trim() === "") return rows;