| name | export-design-system |
| description | Exports generated components + design-tokens.lock.json as a publishable pnpm workspace. Generates a framework-agnostic tokens package and a framework-specific component library (React/Vue/Svelte via Vite library mode, React Native via tsc). Includes Tailwind preset, ThemeProvider, Changesets versioning, and a properly configured exports map. Keywords: design system export, component library, npm package, vite lib mode, tailwind preset, changesets, monorepo, publishable |
Export Design System — Publishable Component Library Generator
Purpose
Take generated components and a design-tokens.lock.json and emit a versioned, publishable pnpm workspace:
@scope/design-tokens — framework-agnostic. Ships CSS custom properties, a typed token export, a Tailwind preset, and a JSON snapshot of the lockfile. Anyone (web app, RN app, Storybook, docs site) can consume it.
@scope/<framework>-components — framework-specific. Ships compiled components, a ThemeProvider + useTheme, and proper peer dependencies. React/Vue/Svelte build with Vite library mode; React Native uses tsc (Vite is not part of the RN runtime).
The tokens package is the bridge — every component package depends on it via workspace:^, so updating tokens cascades through every framework target.
When to Use
- After a successful
/build-from-figma, /build-from-canva, or /build-from-screenshot run, when you want to extract the result as a reusable library rather than ship the full app.
- When a design system needs to be published to a private npm registry or installed across multiple consumer apps.
- When migrating from a single-app codebase to a shared component package.
This skill is not part of the autonomous build pipeline — it is invoked on demand via /export-design-system.
Inputs
- Required:
design-tokens.lock.json (auto-detected at src/styles/design-tokens.lock.json or design-tokens.lock.json)
- At least one component file under
src/components/ (or override with --components-dir)
- Optional:
build-spec.json (auto-detected at .claude/plans/build-spec.json or build-spec.json) — read for outputTarget. If absent, framework is inferred from package.json deps or supplied via --framework.
--scope <name> — npm scope. Defaults to the existing package.json scope, or @my-app.
--output <dir> — where to write the workspace. Default: dist/design-system/.
Outputs
A complete pnpm workspace at <output>/:
<output>/
├── package.json # workspace root with pnpm scripts and changesets
├── pnpm-workspace.yaml
├── tsconfig.base.json
├── .changeset/ # versioning config
├── .npmrc, .gitignore
├── README.md
└── packages/
├── design-tokens/
│ ├── package.json # exports map for ".", "./tokens.css", "./tailwind-preset"
│ ├── tsconfig.json
│ ├── vite.config.ts # vite lib mode
│ ├── README.md
│ └── src/
│ ├── index.ts # barrel
│ ├── tokens.ts # typed token export
│ ├── tokens.css # CSS custom properties
│ ├── tokens.json # raw lockfile snapshot
│ └── tailwind-preset.ts
└── <framework>-components/
├── package.json # peerDeps: react|vue|svelte; depends on design-tokens via workspace:^
├── tsconfig.json
├── vite.config.ts # vite lib mode (or omitted for react-native)
├── README.md
└── src/
├── index.ts # barrel re-exports ThemeProvider + every component
├── components/ # copied from project, tests/stories/types stripped
└── theme/
└── ThemeProvider.{tsx,ts,svelte}
Process
Step 1: Detection
Auto-detect inputs in this order:
lockfile: src/styles/design-tokens.lock.json → design-tokens.lock.json
buildSpec: .claude/plans/build-spec.json → build-spec.json
componentsDir: src/components → components → src/lib/components
framework: buildSpec.outputTarget → package.json deps fallback
Refuse with a clear error if lockfile is missing or framework cannot be determined.
Step 2: Workspace scaffolding
Write the workspace root files: package.json, pnpm-workspace.yaml, tsconfig.base.json, .changeset/, .npmrc, .gitignore, README.md.
tsconfig.base.json enables strict, isolatedModules, bundler resolution. Both packages extend it.
Step 3: Tokens package
Generate packages/design-tokens/:
tokens.ts — typed tokens constant from the lockfile (as const for inference).
tokens.css — :root { --color-...: ...; --space-...; --radius-...; } derived from lockfile.
tokens.json — verbatim copy of the lockfile.
tailwind-preset.ts — Tailwind preset that consumers extend; pulls colors, spacing, radii, fontFamily from the lockfile.
vite.config.ts — Vite lib mode with vite-plugin-dts for declaration rollup. Two entries: index and tailwind-preset.
package.json — declares exports map for ., ./tokens.css, ./tailwind-preset, ./tokens.json. sideEffects: ["./dist/tokens.css"].
Step 4: Components package
Generate packages/<framework>-components/:
- Copy components from the source project into
src/components/, excluding *.test.*, *.spec.*, *.stories.*, and *.d.ts files (filter by extension matching the framework: .tsx/.ts for React/RN, .vue/.ts for Vue, .svelte/.ts for Svelte).
- Generate
theme/ThemeProvider.{tsx,ts,svelte} with framework-idiomatic context API and a useTheme hook.
- Generate
src/index.ts barrel that re-exports ThemeProvider, useTheme, and every detected component.
- Generate
vite.config.ts (Vite lib mode) for React/Vue/Svelte. Externalize the framework runtime (react, react-dom, react/jsx-runtime; vue; svelte, svelte/internal).
- Generate
package.json with peerDependencies for the framework, dependencies: { @scope/design-tokens: workspace:^ }, proper exports map, sideEffects including CSS.
- For
react-native: skip vite.config.ts, use tsc -p tsconfig.json for build, set peerDependencies to react + react-native. Add a clear note in the README.
Step 5: Versioning
If --no-changesets is not set, scaffold .changeset/config.json with sensible defaults (commit: false, access: public, baseBranch: main). Workspace root scripts: pnpm changeset, pnpm version, pnpm release.
Step 6: Report
Emit a summary (text or JSON via --json):
- Framework + scope + output path
- Package names
- Component count and files copied
- Total files written
- Next steps:
pnpm install && pnpm build
Invocation
Direct script (the slash command wraps this):
node scripts/export-design-system.js [options]
./scripts/export-design-system.sh [options]
Common flag combinations:
./scripts/export-design-system.sh
./scripts/export-design-system.sh --scope @acme --output packages/
./scripts/export-design-system.sh --dry-run
./scripts/export-design-system.sh --force
./scripts/export-design-system.sh --no-storybook --no-changesets
./scripts/export-design-system.sh --json
Error Recovery
- No
design-tokens.lock.json → exit 2. Run the relevant build pipeline (/build-from-figma, etc.) first to produce a lockfile.
- Cannot detect framework → exit 2. Pass
--framework <react|vue|svelte|react-native> or set outputTarget in build-spec.json.
- Output directory exists → exit 1. Either pass
--force to overwrite, or pick a different --output.
- Unsupported framework → exit 3. Only
react, vue, svelte, react-native are supported.
Constraints & Gotchas
- Vite lib mode does not apply to React Native. RN apps run on Metro and have their own JSX/runtime concerns. The script falls back to
tsc -p tsconfig.json for RN. Documented in the generated README.
- Tokens are a snapshot. Re-running this skill regenerates the token package from the current lockfile. Bump versions via Changesets when the lockfile changes.
- Peer dependencies are conservative. React peer is
^18.0.0 || ^19.0.0. Vue is ^3.4.0. Svelte is ^5.0.0. Adjust per-package after generation if your consumers require narrower ranges.
- The script does not run
pnpm install or pnpm build. It only writes files. The user runs install/build to verify the workspace.
- Component file filtering is conservative.
*.test.*, *.spec.*, *.stories.*, *.d.ts are excluded. Co-located CSS modules and helper files with matching framework extensions ARE included.
Re-importing & Round-Trip
The exported tokens.json is a lossless snapshot of the lockfile, so the
export is fully reversible. scripts/import-design-tokens.js (the inverse of this
skill) reconstructs a design-tokens.lock.json from an exported package:
./scripts/import-design-tokens.sh --from dist/design-system --out design-tokens.lock.json
./scripts/import-design-tokens.sh --from dist/design-system --verify src/styles/design-tokens.lock.json
--from also accepts an installed package or a raw tokens.json path. Only
tokens.json/tokens.ts round-trip; tokens.css and tailwind-preset.ts are
derived, one-way views. See docs/design-system-export/consumers.md for the
interchange contract and how the named consumers ingest the export — Flavian
(WordPress/FSE theme.json) and Nerva (typed tokens in a Hono/Workers
backend). The guarantee is proved by scripts/__tests__/design-system-roundtrip.test.js.
Reference
- Script:
scripts/export-design-system.js
- Wrapper:
scripts/export-design-system.sh
- Slash command:
/export-design-system
- Re-importer:
scripts/import-design-tokens.js / scripts/import-design-tokens.sh
- Round-trip test:
scripts/__tests__/design-system-roundtrip.test.js
- Consumer contract:
docs/design-system-export/consumers.md
- Inputs:
design-tokens.lock.json (from design-token-lock skill), build-spec.json (from figma-intake/canva-intake/screenshot-intake skills)