Install any skill in seconds. Free to start, no credit card required.
Get Started Free →asgeirtj/storybook-source-shape
Storybook is the **fidelity oracle, not the runtime**. The converter bundles the package's compiled `dist/` into `_ds_bundle.js` — the same bundle the claude.ai/design agent builds with — and generates each preview by **compiling the story source module itself** (hooks, fixtures, local helpers — the
Passed 16 of 19 test cases with the skill, and 3 of 19 without it.
and 28 more in the full report
16 fixed3 passed either way3 still failing0 broke
Tokens, turns and time describe resource use, not a dollar price.
One of 16 test cases the skill fixed. Same model, same prompt, with and without the skill.
In our monorepo setup, running pnpm -F @my-scope/ui build fails during story preview bundling because sibling package @my-scope/tokens is missing from dist/. How should we format the build filter command?
| Checked | Without skill | With skill |
|---|---|---|
| Appends trailing ellipsis to the package filter flag in monorepo build commands | Passed | Passed |
| Builds workspace dependencies alongside the target package before generating story previews | Missed | Passed |
The instructions your model reads when the skill loads.
Storybook is the fidelity oracle, not the runtime. The converter bundles the package's compiled dist/ into _ds_bundle.js - the same bundle the claude.ai/design agent builds with - and generates each preview by compiling the story source module itself (hooks, fixtures, local helpers - the whole closure comes along), with every component import resolved to that shipped bundle (lib/story-imports.mjs redirects package and relative component imports to window.<Global>). The repo's own storybook render is the ground truth those previews must match: a compare harness screenshots each story in the reference storybook and the matching preview render side by side, and you iterate until they match. Nothing from storybook-static is uploaded, and no story code is ever evaluated at build time - stories run only in the browser, against the real artifact.
Requires React 18+. Playwright + chromium are required for this shape (the compare loop is the verification), not optional.
First sync or re-sync? A re-sync is marked by a config whose projectId and pkg were both in place before this run started - most of this document then doesn't apply; go to §7, where one driver run routes the work and untouched components cost nothing. Everything else takes the full flow (§2 build -> §3 self-heal -> §4 match -> conventions header (base SKILL.md, before upload) -> §6 upload), where every component gets verified and graded once - that includes a partial config left by an aborted run, and a pin this run itself just recorded in the base skill's §1. (Only the old design-sync.config.json present? Move it first and commit: mkdir -p .design-sync && mv -n design-sync.config.json .design-sync/config.json, then apply the same test.)
Build the DS package and its workspace dependencies. The converter bundles dist/ into window.<Global>. Run <pm> run build; in a monorepo use turbo run build --filter=<pkg> or pnpm -F "<pkg>..." build (the trailing ... is required - bare -F <pkg> skips dependencies and you'll see Cannot find module '@scope/tokens'). If package.json module/exports['.'] points at TS source, find the actual built entry and pass it via --entry. Do this before step 2 - storybook often imports sibling packages from their built dist/.
Build the reference storybook ONCE into .design-sync/sb-reference/ - NOT under ds-bundle/ (the converter wipes --out on every rebuild, and storybook builds take minutes; the reference must survive the fix loop):
npx storybook build -c <storybookConfigDir> -o .design-sync/sb-reference
Run it from the directory whose package.json has the storybook devDependencies - usually the one containing .storybook/; monorepos often have several storybooks, so pick the one covering the package you're syncing. Make -o the repo-root path (e.g. -o "$(git rev-parse --show-toplevel)/.design-sync/sb-reference"): the converter and compare resolve .design-sync/ from the repo root, so a cwd-relative -o in a subpackage puts the reference where nothing will find it. Use npx storybook build directly, not the repo's npm run build-storybook script (wrong output dir). Then check .design-sync/sb-reference/iframe.html exists and is >10KB - index.json alone can exist with a failed build.
Long builds: background them through your shell tool's background mode only and wait for the completion notification. Never a bare & (untracked - the notification never comes), and never a pgrep -f '<script>' poll loop (it matches its own command line and spins to timeout). Headless / -p sessions: run long commands synchronously instead - there is no task-notification re-invocation there, so a backgrounded run is never resumed.
.gitignore additions: .design-sync/sb-reference/, .design-sync/learnings/, .design-sync/.cache/, .design-sync/node_modules (fork symlink - recreated per clone), .ds-sync/, ds-bundle/ - build artifact, transient scratch, verification working state, the symlink, staged scripts, regenerated output. Committed: the durable set (the rule in non-storybook §2, same here: everything under .design-sync/ not gitignored - previews/ holds your authored files ONLY; generated story-module wrappers live in .design-sync/.cache/previews/ and regenerate every build; the converter never writes or deletes anything in previews/). Verification state is never committed - cross-machine carry-forward comes from the uploaded project's _ds_sync.json. Rebuild the reference only when stories or the DS source change.
Write .design-sync/config.json - only pkg and globalName required. If it already exists, read it first and keep what's there - titleMap, overrides, and provider accumulate fixes from prior syncs. Also Read .design-sync/NOTES.md first - its Re-sync risks section is the prior run's watch-list; re-verify those items instead of assuming carry-forward covers them. The package-shape field table in ../non-storybook/SKILL.md §2.6 applies verbatim; the fields that matter most here:
| Field | Value |
|---|---|
pkg / globalName | pkg required; globalName auto-derived from it when omitted |
shape | "storybook" - pins detection |
storybookStatic | ".design-sync/sb-reference" - so re-syncs and compare find the reference without flags |
storybookConfigDir | the .storybook/ dir (monorepos) |
buildCmd | what to re-run before the converter on re-sync |
titleMap | {title: ExportName} when story titles don't match export names; {title: null} excludes a non-visual/internal component from the sync entirely |
overrides | {<Name>: {skip: [storyIds], cardMode: "single"|"column", primaryStory: "<Export>", viewport: "WxH"}} - skip for stories that can't render statically; cardMode: "single" for overlay components (§4a.5, §5), "column" for stories wider than a grid cell (the [GRID_OVERFLOW] row in §3) |
provider | usually unnecessary for previews - .storybook/preview decorators are auto-bundled; set only when that fails. Before §6 upload, distill decorator-provided context into cfg.provider - README/prompt.md wrap guidance is generated from config only (decorator-only wrapping ships a generic note). Setting it also replaces the decorators as the preview wrapper on the next build: scoped-compare a themed component after the switch - an incomplete distillation regresses previews the decorators rendered fine, and carried-forward grades won't catch it. Format: {"component": "ThemeProvider", "props": {...}, "inner": {...}} - a nested chain, outermost first; each component must be a bundle export. Literal props are for small scalars ("theme": "light") and stable snippets. For data that already exists in the repo - a locale JSON, a theme object - prefer {"$ref": "<export>"} backed by a 2-line module added via cfg.extraEntries (e.g. export { default as previewI18n } from '../locales/en.json'): a $ref emits window.<Global>.<export>, so the data lives once in the bundle and re-reads from its source file on every build. Inlining a copy is acceptable for something tiny and stable, but know the cost - a literal duplicates into every card's html and silently rots when the source file changes, so anything sizable or evolving belongs behind a $ref. Path forms for extraEntries: a bare name resolves from node_modules; a repo-owned module needs an explicit .//../ package-relative path (workspace-bounded - the build logs ! extraEntries: ... skipped if it escapes). |
Stage scripts + install converter deps (isolated in .ds-sync/, repo lockfile untouched):
mkdir -p .ds-sync && cp -r "<skill-base-dir>"/package-build.mjs "<skill-base-dir>"/package-validate.mjs "<skill-base-dir>"/resync.mjs "<skill-base-dir>"/lib "<skill-base-dir>"/storybook "<skill-base-dir>"/non-storybook .ds-sync/
test cases passed, vs 3 / 19 without
More skills from the same source and nearby in the registry, with their headline benchmark lift.