From throughline
Sets up Storybook in a monorepo, builds code components from Figma design tokens, generates stories, configures Chromatic for visual regression testing, and wires Code Connect.
How this skill is triggered — by the user, by Claude, or both
Slash command
/throughline:storybook-chromatic-builderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Builds the code half of the component library: real components consuming the
Builds the code half of the component library: real components consuming the synced tokens, stories documenting them, Chromatic for visual regression, and Code Connect tying Figma to code when available.
Read user.codingLevel (${CLAUDE_PLUGIN_ROOT}/references/coding-level.md) and scale explanation.
This skill needs:
repository-builder, at least
local-git). Offer to run it if missing.packages/tokens (token-sync-layer) so components consume
real token files. Offer to run sync if missing.components.built) with their slot contracts, so stories
reflect the real component APIs.Strongly recommend github stage so Chromatic's CI integration works; it can be
set up locally first and wired to CI when the remote exists.
Initialize Storybook in packages/ui (or the components package), configured for
the framework the tokens were synced for (e.g. React + Vite for a shadcn/Tailwind
system). Wire it to consume packages/tokens output so stories render with the
real design tokens (import the generated CSS/theme). Checkpoint: confirm
Storybook runs and shows the token-themed canvas.
Install the documentation scripts alongside the token scripts (copy from the
plugin's scripts/ — build-docs-digest.mjs, docs-check.mjs, and
lib/doc-record.mjs — into the repo and register npm scripts):
"docs:digest": "node scripts/build-docs-digest.mjs""docs:check": "node scripts/docs-check.mjs"These are the documentation analog of tokens:validate; see
${CLAUDE_PLUGIN_ROOT}/scripts/README.md.
pnpm build-script allowlist (first-run gotcha). On pnpm workspaces, the
@storybook/react-vite install pulls esbuild, whose postinstall is blocked by
pnpm's default onlyBuiltDependencies policy — Storybook then fails to start with a
binary-not-found error. When installing Storybook in a pnpm workspace, add esbuild
(and any other native postinstall dep Storybook pulls) to root package.json
"pnpm": { "onlyBuiltDependencies": [...] } as part of setup, so first run works.
Don't duplicate the app's CSS — share it. Storybook needs the same Tailwind v4
@utility typography rules the app uses to render correctly. Before adding any,
check whether the app's global stylesheet (e.g. apps/web/app/globals.css) already
defines them. If it does, extract the shared @utility blocks into a single
source — e.g. packages/ui/src/typography.css, added to the UI package's exports
("./typography.css") — and replace the inline copies in both the app and the UI
package with one @import. If they don't exist yet, create that shared file from
the start. Copying the rules inline into a second file starts immediate drift (one
side ends up on the wrong --font-* var and nobody notices). Keep Storybook-only
concerns (Google Fonts @import, :root font-var fallbacks) in a separate
styles.css layer so the shared typography file stays clean and app-shareable.
For each Figma component (components.built), build its code counterpart
consuming tokens and implementing the slot contracts captured by
component-builder:
leadingIcon?), defaulting
to the canonical icon name if specified, imported from the installed icon
package (lucide-react etc.).avatar?),
resolved via deterministic naming.ReactNode prop (endAdornment?); Figma
slots on composites (card body, modal content) → children / a composition
prop, since a Figma slot is the design-tool expression of React composition.Match the deterministic naming so Button (Figma) ↔ Button (code).
Story generation is independent and verifiable per component, so it parallelizes — one worker per component, unlike the concurrency-1 Figma lane.
Execution model — parallel subagents with model routing. If your host
supports subagent dispatch, dispatch one code-executor per component (fast
tier) to write its stories — a story per meaningful variant, controls wired to
props, slot props demonstrated — each verifying its own work (the story builds
and renders); then a reviewer (balanced) two-stage pass (does it match the
component spec; is it quality code) before combining. Route per
${CLAUDE_PLUGIN_ROOT}/references/agent-routing.md, and only dispatch a
component once its story spec is complete enough to transcribe. If your host has
no subagent dispatch, generate and verify each component's stories inline
instead.
Controls must actually drive the component (args-through render). A render
that ignores its args silently breaks the Controls panel — the toggle writes to
args but nothing re-renders. So:
render: (args) => <Component {...args} />, never
render: () => <Component variant="..." /> (hardcoded props make the variant
radio and other controls dead). Add fixed children/body after the spread:
render: (args) => <Card {...args}>{body}</Card>.ReactElement slot props (e.g. avatar?: React.ReactElement) can't be driven
by an auto-generated object control — the panel renders a broken [object Object]
input. Suppress it (avatar: { control: false, table: { disable: true } }) and
add a boolean helper arg under a Slots category that the render function maps
to a concrete element — e.g. a showAvatar toggle → avatar={showAvatar ? <Avatar … /> : undefined}. This gives a real, working slot toggle without asking the user
to type JSX into the panel.Icon gallery story — don't write a story per library icon. Generate ONE searchable gallery story that imports the icon package and renders the grid (optionally with click-to-copy import names). This mirrors the Figma Icons page. Custom icons (SVGR-generated, owned by the repo) get normal individual stories like any component.
Set up Chromatic for visual regression testing. Generate the config and the CI
workflow that runs Chromatic on PRs. This needs a CHROMATIC_PROJECT_TOKEN:
${CLAUDE_PLUGIN_ROOT}/references/coding-level.md), the token
value never passes through chat. Tell the user where to get it (Chromatic's
project setup page after signing in) and where it goes — .env locally
(gitignored) and the GitHub Actions secrets vault for CI. The user places it.Scale all of this to codingLevel — full teaching for new, terse for
comfortable.
Recommendation: leave TurboSnap OFF for a design system. Snapshot every story
on every run. TurboSnap (onlyChanged: true) only re-snapshots stories whose
changed files it can trace incrementally — and for a token-driven system that
model is fundamentally fragile, because token changes are global: one token
edit can restyle every component, the opposite of the localized change TurboSnap
is built for.
Concretely, TurboSnap keeps missing token changes in two independent ways:
node_modules (e.g. @<scope>/tokens → packages/tokens build output), so a
token-only PR — the everyday /sync-figma-tokens loop — traces nothing and
reports "Capturing 0 snapshots." False green.externals option is only a partial
mitigation and, given the incremental model, is easy to defeat in practice.So for a design system the robust default is: snapshot all stories, always. At typical counts (dozens of stories) this is cheap and can never miss a global token change. Only consider TurboSnap if the story count grows large enough that full-run cost genuinely matters — and even then, treat any token change as requiring a full run.
Configure Chromatic to snapshot everything (do not set onlyChanged), and
verify a token-only PR re-snapshots all stories — they should flip orange against
the green baseline.
The full-snapshot default above is the right call for catching regressions, and it is also the maximum-usage choice: every story, every run. Chromatic bills per snapshot, so name this cost shape to the user when you set Chromatic up, and put the guardrails in before the first big token PR, not after the bill.
What Chromatic actually offers (re-verify the live numbers at
chromatic.com/pricing — they drift):
The math, so the user sizes the plan honestly: snapshots ≈ stories × modes ×
builds. One /sync-figma-tokens PR re-snapshots the entire suite × every mode
in a single build — e.g. 40 components × 2 modes = 80 snapshots per build, and a
handful of token PRs plus daily main builds clears a free tier in a week.
So the guardrails, all of them user-controlled (Chromatic will not cap you):
main only —
never on every branch push — path-filter out docs-only changes, and keep it to
one Chromatic build per commit (no duplicate runs).Do not silently pick a plan or leave the trigger wide open. Name the tradeoff and let the user choose with the numbers in front of them.
Code Connect ties Figma components to their code counterparts so Figma's dev mode shows the real code. It's plan-gated (Figma Organization/Enterprise).
storybook.codeConnect = true.${CLAUDE_PLUGIN_ROOT}/references/figma-publishing.md). If
components.instanceSwapUpgradePending is non-empty, those components still owe
a typed instance-swap dropdown in Figma — added by a later component-builder
run after the user publishes. This does not block the code side: implement
each slot prop from the recorded slot contract regardless of the Figma dropdown.For each component that has a canonical record
(design-system/docs/components/<Name>.doc.json), render the code-side surfaces
from it (read ${CLAUDE_PLUGIN_ROOT}/references/component-doc-schema.md for the
projection contract):
<Name>.mdx next to the component (e.g.
packages/ui/src/<Name>/<Name>.mdx) rendering summary, description,
when-to-use/not, do's/don'ts, accessibility, and a variant/state table. Put the
record's fingerprint in MDX frontmatter as docFingerprint: <fp>.summary + description
and per-prop descriptions from variants/states meanings, so argTypes
descriptions surface in the Storybook controls table.docs:digest to (re)generate design-system/docs/index.json
design-system/docs/llms.txt from all records.Update the manifest: add the storybookMdx surface to
components.meta[<Name>].doc.surfaces as { src: <fp>, render: <hash of the MDX file>, file: "<repo-relative MDX path>" }.
Wire the gate. Ensure docs:check is part of the repo's verification (a CI
step and/or a Turbo task). It compares every surface against its record and exits
non-zero on drift; Figma surfaces report edit-unverified (checked live in a Figma
session). Run docs:check once here and confirm it passes before handing off.
A component built and storied here is now done — but its Figma doc card was
stamped draft by component-builder and won't change on its own. Once the code
component renders and its stories build (and the user has approved the result),
promote each finalized component to stable so the design system tells the
truth in both places.
Confirm the write-back once, up front. Before touching Figma, state the batched change in one line and get a yes — "I'll update the N doc cards in Figma: flip the status chips amber → green and set Last Updated to today. Confirm?" The user approved the components, but writing to their Figma file is a separate external- system action that needs explicit consent (and an unannounced write trips the safety classifier, forcing the round-trip anyway). One confirmation covers all cards.
For every component you finalized in this run, follow the "Promoting a
component's status (write-back on finalize)" routine in
${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md (which also covers
the figma_execute scripting gotchas — getNodeByIdAsync and an explicit
timeout for the multi-card write):
components.meta[name].status = "stable" and refresh
components.meta[name].updatedAt to today.status + updatedAt in
the component's .doc.json, recompute its fingerprint, re-run docs:digest, and
re-render the affected surfaces so docs:check stays green.figma.mechanism), open the component's doc card and
update the Status Label text to stable, re-bind the Status chip fill to
the success semantic color variable (mode-aware, not a hardcoded hex), and
set Last Updated to today's date — then screenshot to confirm the chip
recolored and the date changed.Once confirmed, do the whole batch in one pass as part of finishing — don't make
the user re-approve each chip. (stable is the finalized status; if a component is
intentionally still experimental, leave it at beta and say so.)
Set storybook.initialized = true, storybook.chromatic accordingly,
storybook.codeConnect accordingly. Append storybook-chromatic-builder to
completedSkills. (Per-component status/updatedAt were already updated in
Step 6.) Note the ongoing loop: new components flow through the
component-pipeline orchestrator; token changes flow through /sync-figma-tokens.
On a retrofit (tokens.intakeMode: "retrofit"), the order of operations and the
verification bar are stricter than greenfield. Read
${CLAUDE_PLUGIN_ROOT}/references/brownfield-retrofit.md — the safe sequence and the
verification triad live there.
Capture the Chromatic baseline BEFORE the code retrofit. Run build-storybook +
Chromatic to establish a green baseline before any token/color code is changed. Then,
when the retrofit lands, every diff is either an intended drift-fix (a color the
audit flagged as wrong) or a regression — and the baseline is the only thing that
tells them apart. Baseline after the retrofit and you've thrown away that signal.
The verification triad — all three are necessary; no single check catches everything:
check-types (TypeScript) — catches type errors, but is blind to Tailwind
silent no-ops: a deleted color utility just stops applying, with no type error
(guardrail 4).build-storybook + Chromatic snapshots — the visual-regression net, but blind
to story-unreachable code. build-storybook only compiles SCSS that some story
actually imports; a dead @import of a deleted partial, or a route's styles no story
renders, compiles "fine" here and breaks only in the app. Chromatic — not
tsc/build — is the source of truth for whether a color-utility removal was safe.Don't assume the stack (§11). This triad names Chromatic + build-storybook
because that's the case-study tooling. Detect what the repo actually uses — read its
package.json scripts for the real type-check / build / visual-test commands — and map
the triad onto them (or degrade gracefully and say so) rather than asserting commands
that may not exist.
draft
— promote the status and write it back (Step 6).packages/tokens.onlyChanged: true) for a token-driven design system
— its incremental model keeps missing global token changes. Default to full
snapshots (every story, every run); revisit only at large story counts.main
only, and size the plan to stories × modes × builds before the first token PR.tsc/build to catch Tailwind color-utility removal — it's a silent
no-op; Chromatic is the source of truth (guardrail 4).npx claudepluginhub jrpease/throughline --plugin throughlineBridges Figma and Storybook bidirectionally: maps design tokens from Figma variables, delivers approved components, and builds Code Connect mappings for Dev Mode. For sync tokens, check parity, or connect components.
Storybook 9+ setup, CSF3 story authoring, args/controls, decorators, play-function testing, a11y axe-core integration, autodocs, design tokens, Figma linking, Chromatic deployment, CI caching, and on-demand build performance.
Creates and reviews Storybook component documentation with stories, MDX, decorators, interaction tests, and visual baselines. Useful for setting up design system presentations or isolated component state previews.