Token docs in MDX and stories
The doc blocks are React components that render against the active token graph. Use them in MDX doc pages to compose reference pages (a ColorPalette, a TypographyScale, a TokenTable filtered to a subpath), all of which update live as the toolbar changes axes.
The same blocks also mount as CSF stories, covered in Rendering blocks in CSF stories below.
Prerequisite
Section titled “Prerequisite”These doc blocks ship with @unpunnyfuns/swatchbook-addon (the quickstart installs and registers it) and are re-exported from its entry, so you import them from the addon with no separate dependency. They read the active token graph from a SwatchbookProvider that the addon’s preview decorator mounts for every story, so they render inside MDX as long as the addon is registered in your Storybook.
A minimal docs page
Section titled “A minimal docs page”import { Meta } from '@storybook/addon-docs/blocks';import { ColorPalette, ColorTable, TokenTable } from '@unpunnyfuns/swatchbook-addon';
<Meta title="Docs/Colors" />
# Colors
## Primitive palette
<ColorPalette filter="color.palette.**" />
## Semantic slots
<ColorTable filter="color.**" />
## Everything as a table
<TokenTable filter="color.**" type="color" />ColorTable groups variants under their base path: color.accent.bg + color.accent.bg-hover collapse to one accent.bg row with a default / hover pill selector, and each row expands inline to show all variants side-by-side. <TokenTable> is the flat view for comparison.
A dashboard composition
Section titled “A dashboard composition”Several blocks compose into a single dashboard page:
import { Meta } from '@storybook/addon-docs/blocks';import { Diagnostics, TokenNavigator, TokenTable } from '@unpunnyfuns/swatchbook-addon';
<Meta title="Docs/Tokens" />
# Design Tokens
<Diagnostics />
## Tree<TokenNavigator />
## Table<TokenTable />Diagnostics auto-collapses on clean loads, so it’s safe at the top of every page without adding noise. Scope the tree or table with a prop: <TokenNavigator root="color" />, <TokenTable filter="space.**" />.
Blocks at a glance
Section titled “Blocks at a glance”| Block | What it renders |
|---|---|
TokenDetail | A single token’s full picture: alias chain, aliased-by tree, per-axis variance, composite preview. |
TokenTable | Filterable table of tokens. Supports path glob and $type filters. |
TokenNavigator | Expandable tree; scope to a subtree or $type. |
ColorPalette | Swatch grid grouped by sub-path. |
ColorTable | Color-specific table; variant suffixes (-hover, -disabled) collapse onto the base row with a pill selector + inline expand. |
TypographyScale | Each typography token as a sample line using its own value. |
FontFamilyPreview | Pangram per fontFamily primitive. |
FontWeightScale | Same “Aa” sample at each fontWeight. |
StrokeStylePreview | Border rendered per strokeStyle primitive. |
Diagnostics | Collapsible list of parser / resolver warnings; auto-opens on non-zero. |
| Motion / shadow / border / gradient previews | Inside TokenDetail’s composite rendering, or standalone blocks. |
See the blocks reference for the full prop surface.
Typed token access from components
Section titled “Typed token access from components”If you need a token’s resolved value (not just a CSS var reference), use useToken. It reads from the virtual graph + active tuple and returns { value, cssVar, type, description } with typed paths:
import { useToken } from '@unpunnyfuns/swatchbook-addon/hooks';
function Badge({ level }: { level: 'info' | 'warn' }) { const tokenName = level === 'info' ? 'color.accent.bg' : 'color.warning.bg'; const bg = useToken(tokenName); return <span style={{ background: bg.cssVar }}>{bg.description}</span>;}cssVar is stable across themes; value reflects the active tuple. Paths autocomplete from a generated .swatchbook/tokens.d.ts once the addon has run.
Overriding axes on a specific doc
Section titled “Overriding axes on a specific doc”Force a specific tuple on a single doc page:
<Meta title="Docs/Colors (Dark)" parameters={{ swatchbook: { axes: { mode: 'Dark' } } }} />The toolbar still reflects the story’s active tuple. Any axis you omit falls back to its default. The pin takes priority over the toolbar for that page and reverts when you navigate away.
What MDX can’t do
Section titled “What MDX can’t do”Storybook’s preview hooks (useGlobals, useArgs, useChannel) throw when called from an MDX doc block; the preview HooksContext only exists while a story is rendering. The blocks in this package subscribe to Storybook’s channel directly instead. If you write a custom block for your project, same pattern:
import { addons } from 'storybook/preview-api';
const channel = addons.getChannel();channel.on('globalsUpdated', (payload) => { // react to axis / theme changes});Rendering blocks in CSF stories
Section titled “Rendering blocks in CSF stories”Mounting them as stories puts your token catalog in the Storybook sidebar, gives each view the Controls panel, and brings it under the interaction, a11y, and visual-regression test surfaces every other story gets.
The pattern
Section titled “The pattern”One short stories file per token type, mounting the matching block:
import { ColorTable } from '@unpunnyfuns/swatchbook-addon';import preview from '../../../.storybook/preview';
const meta = preview.meta({ title: 'Tokens/Colors', component: ColorTable });export default meta;
export const Palette = meta.story({ args: { filter: 'color.**' } });The sidebar tree comes from the title. Each export is an ordinary story.
What becomes a control, and what does not
Section titled “What becomes a control, and what does not”A token story is parametrised in three tiers:
- Display configuration becomes Controls. Expose the block’s scalar props as
argTypes:filter,sortBy,sortDir,searchable, and any block-specific scalar likeDimensionScale’svisual. Defaults define the canonical view; the controls let a reader narrow or re-sort it live. (Color format is a toolbar-level toggle, not a block prop, so it stays on the toolbar like theme.) - Identity stays fixed. The token
$typeand the block choice are the story’s identity, not knobs. Complex props (indicators,variants) do not render as clean controls; set them statically in a story’sargswhen a view needs them. - Theme stays on the toolbar. Mode, brand, and contrast are driven by the swatchbook toolbar, which flips every story at once. A story follows the toolbar by default. Pin a specific tuple the same way as Overriding axes on a specific doc, through
parameters.swatchbook.axes:
export const DarkPalette = meta.story({ args: { filter: 'color.**' }, parameters: { swatchbook: { axes: { mode: 'Dark' } } },});Primitive and composite types
Section titled “Primitive and composite types”Primitive types (color, fontFamily, fontWeight, dimension, strokeStyle) hold a single value. Composite types (typography, shadow, border, gradient, transition) bundle several values: a typography token, for instance, composes a fontFamily, fontSize, fontWeight, lineHeight, and letterSpacing. Enable the composes indicator (off by default) to see the bundling at a glance: it adds a ⊞ N parts count to composite rows in the table and tree blocks.
See also
Section titled “See also”- Reference: blocks: every block’s props.
- Reference: addon: the
useTokenhook and its types. - Live Storybook: doc stories you can steal from.