@unpunnyfuns/swatchbook-blocks 0.67.0 → 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";
@@ -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
  }
@@ -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,
@@ -1746,44 +1709,9 @@ function useRootFontSize() {
1746
1709
  }
1747
1710
  //#endregion
1748
1711
  //#region src/dimension-scale/DimensionBar.tsx
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
- },
1763
- bar: {
1764
- height: 14,
1765
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1766
- borderRadius: 2,
1767
- minWidth: 1,
1768
- maxWidth: "100%"
1769
- },
1770
- radiusSample: {
1771
- width: 56,
1772
- height: 56,
1773
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1774
- border: BORDER_STRONG
1775
- },
1776
- sizeSample: {
1777
- background: "var(--swatchbook-accent-bg, #3b82f6)",
1778
- border: BORDER_STRONG,
1779
- minWidth: 1,
1780
- minHeight: 1
1781
- }
1782
- };
1783
1712
  function withCap(visual) {
1784
1713
  return /* @__PURE__ */ jsxs("span", {
1785
1714
  className: "sb-dimension-bar sb-dimension-bar--capped",
1786
- style: styles$1.cappedWrap,
1787
1715
  title: `capped at 480px`,
1788
1716
  children: [visual, /* @__PURE__ */ jsx("span", {
1789
1717
  className: "sb-dimension-bar__cap",
@@ -1803,16 +1731,14 @@ function DimensionBar({ path, visual = "length" }) {
1803
1731
  const cappedValue = capped ? `480px` : cssVar;
1804
1732
  switch (visual) {
1805
1733
  case "radius": return /* @__PURE__ */ jsx("div", {
1806
- style: {
1807
- ...styles$1.radiusSample,
1808
- borderRadius: cssVar
1809
- },
1734
+ className: "sb-dimension-bar__radius-sample",
1735
+ style: { borderRadius: cssVar },
1810
1736
  "aria-hidden": true
1811
1737
  });
1812
1738
  case "size": {
1813
1739
  const sample = /* @__PURE__ */ jsx("div", {
1740
+ className: "sb-dimension-bar__size-sample",
1814
1741
  style: {
1815
- ...styles$1.sizeSample,
1816
1742
  width: cappedValue,
1817
1743
  height: cappedValue
1818
1744
  },
@@ -1822,10 +1748,8 @@ function DimensionBar({ path, visual = "length" }) {
1822
1748
  }
1823
1749
  default: {
1824
1750
  const bar = /* @__PURE__ */ jsx("div", {
1825
- style: {
1826
- ...styles$1.bar,
1827
- width: cappedValue
1828
- },
1751
+ className: "sb-dimension-bar__bar",
1752
+ style: { width: cappedValue },
1829
1753
  "aria-hidden": true
1830
1754
  });
1831
1755
  return capped ? withCap(bar) : bar;
@@ -2287,29 +2211,6 @@ function usePrefersReducedMotion() {
2287
2211
  //#region src/motion-preview/MotionSample.tsx
2288
2212
  const DEFAULT_DURATION_MS = 300;
2289
2213
  const DEFAULT_EASING = "cubic-bezier(0.2, 0, 0, 1)";
2290
- const styles = {
2291
- track: {
2292
- position: "relative",
2293
- height: 36,
2294
- background: SURFACE_MUTED,
2295
- borderRadius: 18,
2296
- overflow: "hidden"
2297
- },
2298
- ball: {
2299
- position: "absolute",
2300
- top: "50%",
2301
- width: 28,
2302
- height: 28,
2303
- marginTop: -14,
2304
- borderRadius: "50%",
2305
- background: "var(--swatchbook-accent-bg, #3b82f6)"
2306
- },
2307
- reducedMotion: {
2308
- fontSize: 11,
2309
- color: TEXT_MUTED,
2310
- fontStyle: "italic"
2311
- }
2312
- };
2313
2214
  function extractDurationMs(raw) {
2314
2215
  if (raw == null) return NaN;
2315
2216
  if (typeof raw === "object") {
@@ -2404,7 +2305,7 @@ function MotionSample({ path, speed = 1, runKey = 0 }) {
2404
2305
  reducedMotion
2405
2306
  ]);
2406
2307
  if (reducedMotion) return /* @__PURE__ */ jsxs("div", {
2407
- style: styles.reducedMotion,
2308
+ className: "sb-motion-sample__reduced-motion",
2408
2309
  children: [
2409
2310
  "Animation suppressed by ",
2410
2311
  /* @__PURE__ */ jsx("code", { children: "prefers-reduced-motion: reduce" }),
@@ -2412,13 +2313,10 @@ function MotionSample({ path, speed = 1, runKey = 0 }) {
2412
2313
  ]
2413
2314
  });
2414
2315
  return /* @__PURE__ */ jsx("div", {
2415
- style: styles.track,
2316
+ className: "sb-motion-sample__track",
2416
2317
  children: /* @__PURE__ */ jsx("div", {
2417
- style: {
2418
- ...styles.ball,
2419
- left: phase === 1 ? "calc(100% - 32px)" : "4px",
2420
- transition: `left ${scaledDuration}ms ${easing}`
2421
- },
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}` },
2422
2320
  "aria-hidden": true
2423
2321
  })
2424
2322
  });
@@ -2660,20 +2558,10 @@ function useSwatchbookData() {
2660
2558
  }
2661
2559
  //#endregion
2662
2560
  //#region src/shadow-preview/ShadowSample.tsx
2663
- const sampleStyle = {
2664
- width: 120,
2665
- height: 56,
2666
- background: SURFACE_RAISED,
2667
- border: BORDER_FAINT,
2668
- borderRadius: 6
2669
- };
2670
2561
  function ShadowSample({ path }) {
2671
- const cssVar = resolveCssVar(path, useProject());
2672
2562
  return /* @__PURE__ */ jsx("div", {
2673
- style: {
2674
- ...sampleStyle,
2675
- boxShadow: cssVar
2676
- },
2563
+ className: "sb-shadow-sample",
2564
+ style: { boxShadow: resolveCssVar(path, useProject()) },
2677
2565
  "aria-hidden": true
2678
2566
  });
2679
2567
  }
@@ -3048,10 +2936,7 @@ function AxisVariance({ path }) {
3048
2936
  }),
3049
2937
  value,
3050
2938
  /* @__PURE__ */ jsx("span", {
3051
- style: {
3052
- opacity: .6,
3053
- marginLeft: 8
3054
- },
2939
+ className: "sb-token-detail__constant-label-note",
3055
2940
  children: "same across every axis"
3056
2941
  })
3057
2942
  ]
@@ -3085,8 +2970,7 @@ function AxisVariance({ path }) {
3085
2970
  "data-axis": axisName,
3086
2971
  "data-context": row.ctx,
3087
2972
  children: [/* @__PURE__ */ jsx("td", {
3088
- className: "sb-token-detail__theme-cell",
3089
- style: { width: "30%" },
2973
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--label",
3090
2974
  children: row.ctx
3091
2975
  }), /* @__PURE__ */ jsxs("td", {
3092
2976
  className: "sb-token-detail__theme-cell",
@@ -3113,22 +2997,14 @@ function AxisVariance({ path }) {
3113
2997
  children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
3114
2998
  className: "sb-token-detail__theme-row",
3115
2999
  children: [/* @__PURE__ */ jsxs("th", {
3116
- className: "sb-token-detail__theme-cell",
3117
- style: {
3118
- textAlign: "left",
3119
- opacity: .7
3120
- },
3000
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--header",
3121
3001
  children: [
3122
3002
  rowAxis.name,
3123
3003
  " \\ ",
3124
3004
  colAxis.name
3125
3005
  ]
3126
3006
  }), colAxis.contexts.map((col) => /* @__PURE__ */ jsx("th", {
3127
- className: "sb-token-detail__theme-cell",
3128
- style: {
3129
- textAlign: "left",
3130
- opacity: .7
3131
- },
3007
+ className: "sb-token-detail__theme-cell sb-token-detail__theme-cell--header",
3132
3008
  children: col
3133
3009
  }, col))]
3134
3010
  }) }), /* @__PURE__ */ jsx("tbody", { children: rowAxis.contexts.map((row) => /* @__PURE__ */ jsxs("tr", {
@@ -3158,8 +3034,7 @@ function AxisVariance({ path }) {
3158
3034
  }, row)) })]
3159
3035
  }),
3160
3036
  extra.length > 0 && /* @__PURE__ */ jsxs("div", {
3161
- className: "sb-token-detail__aliased-by-truncated",
3162
- style: { marginTop: 6 },
3037
+ className: "sb-token-detail__aliased-by-truncated sb-token-detail__aliased-by-truncated--axis-note",
3163
3038
  children: [
3164
3039
  "Values also vary with ",
3165
3040
  extra.map((a) => a.name).join(", "),
@@ -3270,7 +3145,7 @@ function CompositeBreakdownContent({ type, rawValue, partialAliasOf, resolved, c
3270
3145
  children: layers.map((layer, i) => {
3271
3146
  const v = layer;
3272
3147
  return /* @__PURE__ */ jsxs("div", {
3273
- style: { display: "contents" },
3148
+ className: "sb-token-detail__shadow-layer",
3274
3149
  children: [
3275
3150
  multi && /* @__PURE__ */ jsxs("div", {
3276
3151
  className: "sb-token-detail__breakdown-layer-header",
@@ -3612,11 +3487,8 @@ function TransitionSample({ transition, durationMs }) {
3612
3487
  return /* @__PURE__ */ jsx("div", {
3613
3488
  className: "sb-token-detail__motion-track",
3614
3489
  children: /* @__PURE__ */ jsx("div", {
3615
- className: "sb-token-detail__motion-ball",
3616
- style: {
3617
- left: phase === 1 ? "calc(100% - 28px)" : "4px",
3618
- transition
3619
- },
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 },
3620
3492
  "aria-hidden": true
3621
3493
  })
3622
3494
  });
@@ -3840,7 +3712,7 @@ function TokenDetail({ path, heading }) {
3840
3712
  outOfGamut && /* @__PURE__ */ jsx("span", {
3841
3713
  title: "Out of sRGB gamut for this format",
3842
3714
  "aria-label": "out of gamut",
3843
- style: { marginLeft: 6 },
3715
+ className: "sb-token-detail__out-of-gamut-icon",
3844
3716
  children: "⚠"
3845
3717
  }),
3846
3718
  /* @__PURE__ */ jsx(CopyButton$1, {
@@ -4314,7 +4186,10 @@ function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true,
4314
4186
  }, [visibleTree, searchExpanded]);
4315
4187
  if (tree.length === 0) return /* @__PURE__ */ jsx("div", {
4316
4188
  ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4317
- 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
+ })
4318
4193
  });
4319
4194
  return /* @__PURE__ */ jsxs("div", {
4320
4195
  ...blockWrapperAttrs(cssVarPrefix, activeAxes),