Design
One skill, three routes:
- Design route — manages project design specification (pages, user flows, design principles, components) in
.project/project.json → design, or generates a Claude Design brief. Modes: Capture, Brief, Build. Build is game-only: it emits Godot.tscnviarender-godot.md; web spec→code build now runs via/design-ship(build → content → check), so the Build action redirects web targets there. - Convert route — converts visual input (sketch, wireframe, Figma/Canva, Figma Make, screenshot, URL, pasted image) into working code using project tokens (web, interactive — including hover/scroll interactions via
$INTERACTION_SPECcapture). Modes: Sketch → high-fi, 1:1 copy, Inspiration. Each mode loads its own procedure file (references/convert-mode-{mode}.md) after mode selection. Also reachable mid-convert via its own 0.4c aspect branch, for section-scoped partial builds (build layout now, fill content later). - Content route — fills already-built pages/components with real, on-brand copy (
/design-convert --content [name], or the"contenting"backlog auto-trigger). Content-filling has no visual source, so it cannot pass through the Convert route's PHASE 0.1 — it gets its own route instead. Archetype → brief → scan → generate → review → apply.
The router below classifies the argument and dispatches to the appropriate route reference file. Each route file is only loaded in sessions where it is needed.
Related skills: /design-tokens · /design-ship · /core-setup
References
../shared/DASHBOARD.md— project.json schema and merge strategies../shared/DESIGN.md— Anti-patterns, color, typography, motion, UX writing../shared/DEVINFO.md— Session tracking, cross-skill handoff../shared/BACKLOG.md— Backlog HTML+JSON format, read/write protocol../shared/DOMAIN.md— Domain resolution (web / game / native) — selects the codegen targetreferences/render-godot.md— Game-domain codegen: spec → Godot.tscnscenes (loaded when$DOMAIN === "game")../shared/CODEGEN.md— Web code-gen patterns (Build and Convert routes — web domain)../shared/FRONTEND-RULES.md— React/TypeScript coding rules (Convert route — web domain)../shared/PATTERNS.md— Component patterns (Convert route)../shared/PLAYWRIGHT.md— Playwright CLI, screenshot capture (Convert route)references/convert-source-fallbacks.md— Figma MCP not connected (REST ladder) or a Figma Make URL (Convert route PHASE 0.1)references/convert-audit-scope.md— Whether the audit option is recommended, and what it reconciles (Convert route PHASE 0.4)references/convert-scope-followup.md— Section-subset + build-vs-content-fill aspect choice (Convert route PHASE 0.4c)references/convert-refine-round.md— Show the result and iterate with the user (Convert route PHASE 3.5)references/route-content.md— Content route: fills built pages/components with real copy (standalone--contententry, or Convert route PHASE 2c)references/convert-content-scope.md,convert-content-generate.md,convert-content-review.md,convert-content-apply.md— Content route phases (archetype/brief → generate → review → apply)scripts/extract-computed-styles.mjs— Ground-truth computed styles, section boxes and seams; copy into.project/tmp/and run the copy, never in place (Convert route PHASE 1 §1.0 and PHASE 3 §3.2c)./examples/— Conversion examples (1:1, inspiration, Apple-style)- External:
vercel-labs/web-interface-guidelines— setup-context for Convert route (seereferences/route-convert.md § Step 0)
State Machine
[*] → PREFLIGHT
PREFLIGHT → CONVERT_PATCH (handoff build-incomplete + user picks Patch)
PREFLIGHT → DESIGN_ROUTE ($ROUTE = design — no visual input)
PREFLIGHT → CONVERT_ROUTE ($ROUTE = convert — visual input detected)
PREFLIGHT → CONTENT_ROUTE ($ROUTE = content — --content arg or "contenting" transition)
PREFLIGHT → ERROR (pre-flight fail)
DESIGN_ROUTE → [route-design.md state machine]
CONVERT_ROUTE → [route-convert.md state machine]
CONVERT_ROUTE → CONVERT_ROUTE#PHASE_2c (0.4c aspect branch = "Fill content" — reads the content
route's phase files directly, never re-enters CONTENT_ROUTE/route-content.md)
CONTENT_ROUTE → [route-content.md state machine]
CONVERT_PATCH → CONVERT_ROUTE (with $PATCH_MODE = true)
PHASE 0: Pre-flight & Route Classification
0.0 Directory Check
Check .project/ exists. If not, create it.
Janitor — prune stale working screenshots: find .project/tmp -name '*.png' -mtime +7 -delete 2>/dev/null
Directory: [✓|✗] .project/ — [exists | created | error]
0.1 Session Check
Read .project/session/devinfo.json. Store as $DEVINFO.
Session: [✓] [New session | Continuing from {devinfo.handoff.source}]
0.1b Domain Resolution
Resolve $DOMAIN per shared/DOMAIN.md (explicit theme.domain → infer from
stack.framework/language → codebase fallback → ask). This selects the codegen target
(web TSX vs Godot .tscn) and gates the Convert route below.
Domain: [✓ web | ✓ game | ⚠ native (spec-only)]
0.2 Handoff Detection
Check $DEVINFO.handoff.source === "build-incomplete".
Staleness auto-cleanup (before any branch below): if a handoff is present, check handoff.timestamp. Older than 24h → show "Handoff is {N}h old — no longer relevant; cleaned up (devinfo.handoff = null)", set devinfo.handoff = null (write devinfo.json), and proceed to PHASE 0.3 as if no handoff exists. Do NOT offer the patch flow for a stale handoff.
If handoff present AND $SKILL_ARG is empty AND no pasted image:
header: "Handoff from Build detected"
question: "Build of '{handoff.target}' is incomplete ({handoff.failedChecks}). Continue with patch on those files?"
options:
- label: "Yes, patch (Recommended)", description: "Scope = patch, files from handoff"
- label: "New screenshot", description: "Ignore handoff, continue normally"
- label: "Cancel", description: "Stop, handoff remains for a later run"
multiSelect: false
On "Yes, patch":
- Ask:
"Paste the desired final state as a screenshot" - Store as
$SOURCE_IMAGE. Set$PATCH_MODE = true,$PATCH_FILE = handoff.files[0],$BEFORE_SCREENSHOT = handoff.buildScreenshot(if null: skip before-screenshot in patch-detection Step 2). - Set
$ROUTE = convert. Handoff is cleaned up in the Convert route PHASE 4 after success. - Proceed to PHASE 0.3 (classification will be skipped — $ROUTE already set).
On "New screenshot": clear handoff signal, proceed to PHASE 0.3 normally. On "Cancel": exit.
If handoff present AND ($SKILL_ARG is not empty OR image is pasted):
header: "Handoff conflict"
question: "Build of '{handoff.target}' is incomplete. Patch it, or proceed with your new input?"
options:
- label: "Patch the incomplete build (Recommended if {handoff.target} === {arg})", description: "Use patch flow for the previous build"
- label: "Use my new input", description: "Ignore handoff, continue with the provided argument/image"
- label: "Cancel", description: "Stop"
multiSelect: false
If handoff.target === $SKILL_ARG: mark "Patch" as Recommended. On "Patch": follow "Yes, patch" steps above. On "New input": proceed to PHASE 0.3 with the argument.
If no handoff (or handoff empty/absent): proceed directly to PHASE 0.3.
0.3 Route Classification
Classify the argument to set $ROUTE:
Step 0 — Content flag (checked before every other step):
$SKILL_ARG starts with --content → $ROUTE = "content". The remainder of the argument (after
--content, trimmed) is $CONTENT_TARGET — empty when none was given (batch/queue mode, resolved
by route-content.md PHASE 0). → Stop (proceed to dispatch).
Step 1 — Visual input (highest priority among the remaining steps — checked before Steps 2-4):
$SKILL_ARG matches any of:
- Protocol prefix:
http://orhttps:// - Design-tool domain: contains
figma.comorcanva.com - Image extension: ends with
.png,.jpg,.jpeg,.webp,.gif,.svg - Local file path: starts with
./,/,~/, or matches drive-letter[A-Z]:\
OR an image is pasted in the chat.
→ $ROUTE = convert. Stop (proceed to dispatch).
Domain guard (game/native): the Convert route relies on Playwright DOM capture and is
web-only. If $DOMAIN !== "web" and visual input was detected, print:
"Visual conversion (Figma/URL/screenshot → code) is web-only. On a {domain} project I'll capture the intent into the design spec and generate from there."
then set $ROUTE = design and continue (the Design route's Build mode emits Godot .tscn via
references/render-godot.md).
Step 2 — Name (design route):
$SKILL_ARG is a non-empty string that does not match step 1 → $ROUTE = design (tentative — see Step 3).
Pass $SKILL_ARG to route-design.md; argument-to-entity resolution ($ARG_MODE / $ARG_TYPE / $ARG_ENTITY / $ARG_NAME) happens in route-design.md PHASE 0.3.
Step 3 — Backlog transition lookup (named entities only):
Triggers when Step 2 set $ROUTE = design AND $SKILL_ARG is non-empty.
- Check
.project/backlog.jsonexists. If not → keep$ROUTE = design. Skip. - Read backlog per
shared/BACKLOG.md → Lifecycle Protocol → Read. Find feature wheref.name === $SKILL_ARG(case-sensitive). - No match → keep
$ROUTE = design. Skip. - Match found and
f.transition === "converting"→- Set
$ROUTE = convert - Set
$CONVERT_TARGET = $SKILL_ARG - Set
$BACKLOG_ROUTE_HINT = "transition=converting"
- Set
- Match found and
f.transition === "designing"→ keep$ROUTE = design. The design route's Mode A will offer "Convert from sketch/mockup" as a sub-option for entities without a spec — that is the canonical path from "designing" to convert. Do not auto-route here. - Match found and
f.transition === "contenting"→- Set
$ROUTE = content - Set
$CONTENT_TARGET = $SKILL_ARG
- Set
- Any other transition value (or absent) → keep
$ROUTE = design.
Step 4 — No argument:
$SKILL_ARG is empty AND no image pasted AND $ROUTE not already set → $ROUTE = design.
Pre-flight summary (the Mode: line is shown only when non-default):
PRE-FLIGHT CHECK
════════════════════════════════════════════════
Directory: [✓|✗] .project/
Session: [✓] [New session | Continuing from {skill}]
Route: [Design | Convert | Content]
Mode: [— | patch (handoff) | backlog transition]
════════════════════════════════════════════════
Dispatch
Codegen target by $DOMAIN: the routes below describe the web codegen (React/TSX via
shared/CODEGEN.md). When $DOMAIN === "game", the code-generation step instead emits Godot
.tscn scenes — at the Build/codegen step, follow references/render-godot.md in place of the
TSX codegen (spec management itself is identical). When $DOMAIN === "native": spec-only, no codegen.
If $ROUTE = design:
Todo: Read
.claude/skills/design-convert/references/route-design.md(game: at the Build codegen step, usereferences/render-godot.md.)
If $ROUTE = convert: (web domain only — game/native are redirected to Design in 0.3)
Todo: Read
.claude/skills/design-convert/references/route-convert.md
If $ROUTE = content:
Todo: Read
.claude/skills/design-convert/references/route-content.md
Restrictions
- Always run PHASE 0 before dispatching
- Never skip handoff detection
- Never guess $ROUTE — follow the classification steps exactly
- Never load more than one route file in the same session, except: (a) the Design route's convert dispatches — Mode A → "Convert from sketch/mockup", or Mode C → "Convert visual input" / "Other" convert (each loads
route-convert.mdand abandons the Design state machine); (b) the Convert route's own PHASE 0.4c aspect branch — "Fill content" (PHASE 2c) reads the content route's phase files directly (convert-content-scope.md→convert-content-generate.md→convert-content-review.md→convert-content-apply.md) on top of an already-loadedroute-convert.md, without ever loadingroute-content.mditself, and returns toconvert-completion.mdfor completion. Any other simultaneous load is forbidden.