Skip to main content
Version: 0.13

Quickstart

Assumes an existing Storybook 10 project with the Vite builder. Install, register, author the first doc page.

Install

One package pulls the whole surface — toolbar, preview decorator, useToken(), every MDX doc block, the standalone ThemeSwitcher:

npm install -D @unpunnyfuns/swatchbook-addon

swatchbook-core, -blocks, and -switcher come along transitively. If you only need a slice (say, the switcher in a Docusaurus site), each is also published independently.

Peer requirements: Storybook 10.3+ on the Vite builder, React 18+, Node 24+.

Configure

Register the addon with an inline config in .storybook/main.ts:

.storybook/main.ts
import { defineMain } from '@storybook/react-vite/node';

export default defineMain({
framework: '@storybook/react-vite',
addons: [
{
name: '@unpunnyfuns/swatchbook-addon',
options: {
config: {
resolver: 'tokens/resolver.json',
cssVarPrefix: 'ds',
},
},
},
],
});

Opt the preview into the addon's annotations (decorator, globals, CSS injection):

.storybook/preview.ts
import { definePreview } from '@storybook/react-vite';
import swatchbookAddon from '@unpunnyfuns/swatchbook-addon';

export default definePreview({
addons: [swatchbookAddon()],
});

Inline config is the primary path. If you'd rather keep the config in its own file (useful when the same config is consumed by a CLI or CI lint outside Storybook), write a swatchbook.config.ts and point at it with options.configPath: '../swatchbook.config.ts' — see config reference for details.

Start Storybook:

pnpm storybook

What you should see

Toolbar: a single Swatchbook icon button in the top bar. Click it to open a popover containing preset pills, one dropdown per modifier in your resolver, and a color-format picker. If you've written a single theme modifier with Light/Dark contexts, you get one dropdown inside the popover; if you have mode × brand, you get two. Escape or outside-click closes it.

Your first doc page

Create an MDX file under your stories glob. The addon's blocks render against whichever tuple the toolbar has active, so everything below re-renders live as you flip modifiers:

src/docs/Tokens.mdx
import { Meta } from '@storybook/addon-docs/blocks';
import {
ColorPalette,
Diagnostics,
TokenDetail,
TokenNavigator,
TokenTable,
} from '@unpunnyfuns/swatchbook-addon';

<Meta title="Docs/Tokens" />

# Tokens

<Diagnostics />

## Palette
<ColorPalette filter="color.palette.*" />

## Semantic roles
<ColorPalette filter="color.*" />

## Everything
<TokenNavigator initiallyExpanded={0} />

## Inspect a single token
<TokenDetail path="color.accent.bg" />

Each block takes filter/scoping props so you can pick what shows up:

  • <ColorPalette filter="…" /> — swatch grid grouped by sub-path. Filter by a glob ("color.palette.*", "color.brand.*").
  • <TokenTable filter="…" type="…" /> — searchable two-column table (path + value). Filter by path glob, scope by DTCG $type.
  • <TokenNavigator root="…" type="…" initiallyExpanded={0..∞} /> — expandable tree. Scope to a subtree (root="color") and/or a $type; initiallyExpanded={0} opens fully collapsed.
  • <TokenDetail path="…" /> — alias chain, per-theme values, CSS var reference, and useToken snippet for one token.
  • <Diagnostics /> — collapsible list of parser / resolver / validation warnings. Auto-opens when anything's non-zero.
  • <TypographyScale>, <DimensionScale>, <FontFamilySample>, <FontWeightScale>, <BorderPreview>, <ShadowPreview>, <GradientPalette>, <MotionPreview>, <StrokeStyleSample> — type-specific blocks with built-in samples. Filter by path glob when you want a subset.

The blocks reference has the full prop list for each. The authoring guide walks through composing them in real pages.

Theme the block chrome against your tokens

The MDX doc blocks read their surfaces, text, and accent colours from a fixed --swatchbook-* CSS-variable namespace. Out of the box those variables fall back to light/dark literals that flip with the active color-scheme, which means the blocks render legibly before you wire anything up. To have them reflect your own tokens, supply a chrome map — one entry per role, each pointing at a token path in your project:

.storybook/main.ts
{
name: '@unpunnyfuns/swatchbook-addon',
options: {
config: {
resolver: 'tokens/resolver.json',
cssVarPrefix: 'ds',
chrome: {
surfaceDefault: 'color.surface.default',
surfaceMuted: 'color.surface.muted',
surfaceRaised: 'color.surface.raised',
textDefault: 'color.text.default',
textMuted: 'color.text.muted',
borderDefault: 'color.border.default',
accentBg: 'color.accent.bg',
accentFg: 'color.accent.fg',
bodyFontFamily: 'typography.body.font-family',
bodyFontSize: 'typography.body.font-size',
},
},
},
},

With a chrome map in place the blocks track every toolbar flip — switch brand or contrast and the block chrome repaints alongside your stories. The closed set of roles (and what each maps to internally) is in the chrome config reference.

No resolver yet?

If you haven't written a DTCG 2025.10 resolver, you can use the layered config form instead:

export default defineSwatchbookConfig({
tokens: ['tokens/**/*.json'],
axes: [
{
name: 'mode',
contexts: {
Light: ['tokens/themes/light.json'],
Dark: ['tokens/themes/dark.json'],
},
default: 'Light',
},
],
cssVarPrefix: 'ds',
});

Each context names an ordered list of file paths (or globs) that layer on top of the base tokens files for that context. See theming inputs for when to pick which.

Next