with one click
with one click
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | sf-design-playground |
| description | Live design-token playground scaffolding. |
| disable-model-invocation | true |
| argument-hint | [route-path] (default: /design-system) |
Before resolving any ShipFlow-owned file, load $SHIPFLOW_ROOT/skills/references/canonical-paths.md ($SHIPFLOW_ROOT defaults to $HOME/shipflow). ShipFlow tools, shared references, skill-local references/*, templates, workflow docs, and internal scripts must resolve from $SHIPFLOW_ROOT, not from the project repo where the skill is running. Project artifacts and source files still resolve from the current project root unless explicitly stated otherwise.
Trace category: conditionnel.
Process role: support-de-chantier.
Before producing the final report, load $SHIPFLOW_ROOT/skills/references/chantier-tracking.md when this run is attached to a spec-first chantier. If exactly one active specs/*.md chantier is identified, append the current run to Skill Run History, update Current Chantier Flow when the run changes the chantier state, and include a final Chantier block. If no unique chantier is identified, do not write to any spec; report Chantier: non applicable or Chantier: non trace with the reason.
pwdhead -60 CLAUDE.md 2>/dev/null || echo "no CLAUDE.md"cat package.json 2>/dev/null | head -50 || echo "no package.json (non-web project?)"ls next.config.* nuxt.config.* astro.config.* svelte.config.* vite.config.* remix.config.* gatsby-config.* 2>/dev/null || echo "no framework config detected"find . -maxdepth 3 -type d \( -name "pages" -o -name "app" -o -name "routes" -o -name "src/pages" -o -name "src/app" -o -name "src/routes" \) 2>/dev/null | grep -v node_modules | head -10 || echo "none"find . -type f \( -name "tokens*" -o -name "theme*" -o -name "design-tokens*" -o -name "_variables*" -o -name "globals.css" -o -name "global.css" \) 2>/dev/null | grep -v node_modules | head -20 || echo "none — playground will prompt for target file"ls tailwind.config.* 2>/dev/null || echo "no tailwind"grep -rln --include="*.{ts,tsx,js,jsx}" -E "(next-auth|@clerk/|better-auth|@auth/|lucia|@supabase/auth|firebase/auth|getServerSession|useSession|useUser|currentUser)" src/ app/ pages/ 2>/dev/null | grep -v node_modules | head -3 || echo "none"find . -type d \( -name "design-system" -o -name "styleguide" -o -name "tokens-debug" -o -name "theme-debug" \) 2>/dev/null | grep -v node_modules | head -5 || echo "none"ls .env .env.local .env.development 2>/dev/null || echo "none"Scaffold a versioned /design-system page (route configurable via $ARGUMENTS) that:
.css / .json / .tsThe page is versioned in the repo (other contributors get the tool too) but gated in production by a 3-tier adaptive auth strategy.
Before scaffolding:
Verify token system exists: scan for --fs-*, --space-*, --color-*, --duration-* (or framework equivalent: Tailwind config, theme files, Flutter Theme.of(context)). If no tokens found, abort with:
⚠ No design token system detected.
The playground reads from your token files. Run /sf-design-from-scratch
to create a professional token system, then re-run /sf-design-playground.
If the user explicitly asks to scaffold "from scratch" with seed tokens,
route to /sf-design-from-scratch instead of creating ad hoc seed tokens
inside the playground workflow.
Verify framework is supported. Currently supported:
If framework is unsupported (Flutter, native, custom), abort with a message explaining the limitation. The playground concept can still be implemented manually using the patterns in this skill — list them.
Check if a playground already exists at the target route (see context block). If yes, ask using AskUserQuestion:
Read the existing token files and build a manifest of:
{
"colors": {
"source": "src/styles/tokens.css",
"format": "css-vars",
"tokens": ["--color-success", "--color-warning", "--color-danger", "--color-info", "--color-neutral", "--surface-base", ...]
},
"typography": {
"source": "src/styles/tokens.css",
"format": "css-vars",
"tokens": ["--fs-xs", "--fs-sm", "--fs-base", ...],
"bundled": true, // if --lh-* and --ls-* exist alongside
"naming": "t-shirt" // or "semantic"
},
"spacing": { ... },
"motion": { ... }
}
Supported source formats:
:root { --token: value }) — most commontailwind.config.js theme.extend.*)theme.ts exporting { colors, fontSize, ... })tokens.json with $value/$type)If multiple sources coexist, report them all and ask the user which is canonical (single source of truth for the playground to write back to).
Generate one page file at ${route}/index.{tsx,astro,vue,svelte} based on detected framework. The page renders five sections stacked vertically:
Light / Dark / Systemdata-theme on <html>For each color token detected:
--color-success)--surface-base and --text-primaryGroup by intent: Universal semantic (success, warning, danger, info, neutral) → Surfaces (base, raised, overlay, sunken) → Brand (brand-*) → Domain-specific (everything else).
For each typography token:
--fs-base)--font-* tokenShow the modular ratio between consecutive tokens at the top of the section ("ratio: 1.25 ✓ coherent" or "ratios: 1.1× / 1.4× / 1.2× ⚠ chaotic — consider Utopia.fyi").
For each spacing token:
For each motion token:
prefers-reduced-motion" — disables all animations on the pageThree buttons, always visible:
.css, .json, .ts — downloads the fileStatus indicator next to buttons: unsaved changes (3), saved 2s ago, error: ....
Generate a server-side gate (not client-side — client-side is cosmetic). The strategy is adaptive 3-tier:
1. NODE_ENV !== 'production' → open access (dev local)
2. Otherwise, project has auth → require admin role (reuse existing auth)
3. Otherwise (no auth detected) → password from env DESIGN_SYSTEM_PASSWORD
(if env var unset → return 404)
Implementation per framework:
middleware.ts matching the playground route, or a server component that does the check inline before renderinggetServerSideProps doing the checksrc/middleware.ts, returns Astro.redirect() or 404+layout.server.ts for the playground route groupserver/middleware/Auth role detection:
session.user.role === 'admin' (or whatever role field is configured — read auth options)auth().has({ role: 'admin' })session.user.roleIf the auth library is detected but the role mechanism is unclear, ask the user — don't guess.
Password fallback (if no auth):
process.env.DESIGN_SYSTEM_PASSWORDThe 💾 Save button POSTs to an endpoint that writes back to the canonical token file.
Critical: this endpoint MUST be:
NODE_ENV === 'production', regardless of auth${file}.backup-${timestamp} before overwritingImplementation per framework:
app/api/design-system/save/route.ts (App Router) or pages/api/design-system/save.ts (Pages)src/pages/api/design-system/save.ts (with export const prerender = false)src/routes/api/design-system/save/+server.tsserver/api/design-system/save.post.tsaction functionIf the user wants the font-family dropdown to actually switch fonts, scaffold a font loader:
playground-fonts.ts that loads a curated set (5-10 fonts covering serif, sans, mono, display) — only loaded on the playground routefont-display: swapDefault font set if none specified: Inter, IBM Plex Sans, IBM Plex Serif, JetBrains Mono, Playfair Display, Space Grotesk. Ask the user using AskUserQuestion if they want to customize this list.
After scaffolding the page + gate + endpoint:
.env.example: append # DESIGN_SYSTEM_PASSWORD=changeme # required in prod if no auth detectedREADME.md (if exists): add a ### Design system playground section explaining the route, the access tiers, and the three export modesCLAUDE.md (if exists): add a one-liner under conventions: Design system playground: visit /${route} in dev to live-edit tokensAUDIT_LOG.md (project-local): log the scaffold action with dateOutput to user:
DESIGN PLAYGROUND SCAFFOLDED
═══════════════════════════════════════
Framework: [detected framework]
Route: /${route}
Token sources: [list of files]
Canonical file: [the one Save writes to]
ACCESS STRATEGY
Dev: open (NODE_ENV !== 'production')
Prod + auth: [auth lib detected] — admin role required
Prod no auth: password via env DESIGN_SYSTEM_PASSWORD
FILES CREATED
[route]/index.[ext] — the playground page
[middleware/loader] — access gate
api/design-system/save — dev-only save endpoint
playground-fonts.ts — font loader (if applicable)
NEXT STEPS
1. Run dev server, visit /${route}
2. Edit tokens live, watch the page update
3. Use Copy / Save / Export when you're happy
4. Before deploying: set DESIGN_SYSTEM_PASSWORD env (if no auth)
5. If tokens were centralized but pages/components still use hardcoded values:
run /sf-design "migrer le site pour consommer les tokens design centralises sans changement visuel volontaire"
═══════════════════════════════════════
The playground makes tokens visible and editable. It does not prove that the whole site consumes the centralized token source.
When the playground is created after a token centralization pass, the report must state whether a migration sweep is still needed:
Token implementation: [complete / partial / unknown]
Next design route: /sf-design "migrer le site pour consommer les tokens design centralises sans changement visuel volontaire"
Expected proof: sf-audit-design-tokens -> sf-check -> sf-browser -> sf-verify
If hardcoded design values remain across pages, layouts, or components, do not present the playground as the end of the design-system chantier. Route to sf-design or sf-build for the site-wide implementation pass.