generating-figma-design
Use when the user requests to design, create, build, generate, or make a new Figma component or screen — including phrases like "make a button", "design a settings page", "build a new card", "generate X". Produces a CSpec, compiles it to a scene graph, executes it in Figma via MCP, and verifies the output.
Generating Figma Design
Overview
Unified make flow: CSpec → scene graph → compiler → Figma execute → screenshot → verify. Replaces the legacy spec → design → review cycle with a single compiler-driven action. All tokens resolve against the knowledge base; all Figma API rules are enforced by the compiler.
When to Use
Invoke when the user:
- says
make,design,create,build,generate, or asks for a "new component" / "new screen" - has already run
setup(knowledge base exists) - has an MCP transport available (console or official)
Do NOT use if:
- the user is adjusting an existing design that is already in Figma — use
learning-from-correctionsinstead - the user is archiving or shipping — use
shipping-and-archiving - the knowledge base is missing — run
extracting-design-systemfirst
Procedure
Before starting, load:
references/compiler-reference.md(repo-root) — scene graph JSON format and rulesreferences/transport-adapter.md(repo-root) — transport detection and tool mapping
Prerequisites
- Knowledge base exists (registries populated) — if not: "Run
setupfirst" - MCP transport available (see
references/transport-adapter.md(repo-root) Section A)
Phase A — Context (target: 30s)
A1. Detect transport
Read references/transport-adapter.md (repo-root) Section A. Determine console vs official transport.
Console: figma_get_status() -> setup.valid: true
Official: whoami() + test use_figma call
Report:
Transport: {console | official}
A2. Load registry index
Load the summary of available DS components — names and types only, not full registry data:
registries/components.json— extract component names, variant property names, and keysregistries/variables.json— extract variable name paths (for token reference validation)registries/text-styles.json— extract style names (for$text/reference validation)registries/icons.json— extract icon names (if file exists)registries/logos.json— extract logo names (if file exists)
Do NOT load guides, patterns, or figma-api-rules.md. The compiler handles all Figma API rules.
A3. Load learnings
Load knowledge-base/learnings.json (skip if file doesn't exist).
Filter by context matching the user's description:
- Include all global learnings (
scope: "global") - Include contextual learnings where
context.screenTypeorcontext.componentmatches
A4. Load recipe index
Load knowledge-base/recipes/_index.json (skip if file doesn't exist — no recipes yet).
Phase B — Recipe Match
B1. Extract archetype
From the user's description, identify:
- Mode: component or screen (ask if ambiguous)
- Archetype: screen type (settings, dashboard, form, detail, list...) or component type
- Keywords: key terms from the description (sidebar, form, table, cards, navigation...)
B2. Score against recipe index
For each recipe in _index.json, compute a match score:
| Dimension | Weight | Method |
|---|---|---|
| Archetype match | 0.40 | Exact match on meta.archetype vs extracted archetype |
| Tag overlap | 0.25 | Jaccard similarity between recipe tags and extracted keywords |
| Structural match | 0.20 | Zone count, component types, parameter compatibility |
| Confidence | 0.15 | Recipe's current confidence score |
B3. Apply match result
| Score | Action |
|---|---|
| >= 0.85 | Exact match. Load recipe file, pre-fill CSpec from recipe parameters. Report: "Recipe match: {name} (score: {score}). Using as template." |
| 0.60 -- 0.84 | Partial match. Load recipe as scaffold. Report: "Partial recipe match: {name} (score: {score}). Using as starting point, will supplement missing zones." |
| < 0.60 | No match. Proceed from scratch. Report: "No recipe match. Generating from scratch." |
Phase C — CSpec (target: 30-60s)
C1. Generate CSpec YAML
Choose the appropriate template:
- Screen mode:
skills/generating-figma-design/references/templates/screen-cspec.yaml - Component mode:
skills/generating-figma-design/references/templates/component-cspec.yaml
Fill the CSpec based on:
- User description (intent, sections, components)
- Recipe template (if match found in Phase B)
- Registry data (available components, tokens, text styles)
If a recipe was matched (>= 0.60):
- Start from the recipe's
graphstructure - Replace
{{ param }}placeholders with values from the user's description - Resolve
@lookup:ComponentNamereferences against the live registry - Add/remove zones as needed for the specific request
If no recipe match (from scratch):
- Build the layout tree node by node using the CSpec template structure
- Reference DS components as INSTANCE nodes (by name — the compiler resolves keys)
- Use
$tokenreferences for all spacing, colors, radius, typography - Use REPEAT nodes for lists/grids with repeated structure
C2. Apply learnings
Integrate learnings from A3 into the CSpec:
- Global learnings (
scope: "global"): auto-apply — replace default token values with learned preferences - Contextual learnings (
scope: "contextual"): suggest — note in comments, apply if context matches
Report applied learnings:
LEARNINGS APPLIED ({n}):
- {rule} (signals: {n}, scope: {scope})
- {rule} (signals: {n}, scope: {scope})
C3. Detect new DS components (screen mode only)
For each UI pattern described in the CSpec, check if it exists in the component registry:
- If covered by an existing DS component -> use INSTANCE node
- If NOT covered -> add to
new_componentssection of the CSpec
If new components are identified:
{N} new DS component(s) needed:
1. {name} — {description}
These must be created before this screen. Starting with: {name}
-> Trigger a nested make flow for each new component. When all are done, resume the screen make.
C4. Present plan to user
Show a readable summary of the CSpec (NOT raw YAML). Format as a plan tree:
PLAN: {name}
Mode: {screen | component}
Canvas: {1440px (web) | 390px (mobile) | 1024px (tablet)}
Recipe: {recipe name or "from scratch"}
Learnings: {n} applied
STRUCTURE:
Root ({width}x{height}, {layout direction})
+-- {Zone 1} ({width}, {layout})
| +-- {Component} ({variant})
| +-- {Component} ({variant})
+-- {Zone 2} ({fillH}, {layout})
| +-- {Section 1}
| | +-- {Component} ({variant})
| +-- {Section 2}
| | +-- REPEAT x{n}: {Component}
DS COMPONENTS: {n} instances
TOKENS: {n} references
STATES: {list or "populated only"}
Generate this design?
C5. User validates
Wait for explicit user confirmation. The user can:
- Approve -> proceed to Phase D
- Adjust -> modify the CSpec based on feedback, re-present plan
- Cancel -> abort
C6. Save CSpec
Save the CSpec YAML to specs/active/{name}.cspec.yaml.
Phase D — Compile + Execute
D1. Convert CSpec to scene graph JSON
Transform the CSpec's layout tree into the scene graph JSON format defined in references/compiler-reference.md (repo-root):
- CSpec
layoutnodes map directly to scene graphnodes - All
$tokenreferences are preserved (the compiler resolves them) - Component names in INSTANCE nodes are preserved (the compiler resolves keys)
- REPEAT and CONDITIONAL nodes pass through to the compiler
Add the root wrapper:
{
"version": "3.0",
"metadata": {
"name": "{CSpec meta.name}",
"width": {CSpec meta.width},
"height": {CSpec meta.height},
"transport": "{detected transport}",
"fileKey": "{user's file key}"
},
"fonts": [ ... ],
"nodes": [ ... ]
}
Font list: Collect all unique font families and styles referenced by $text/ tokens in the scene graph. Cross-reference against registries/text-styles.json to get actual font family + style values.
D2. Write scene graph to temp file
# Write JSON to temp file
cat > /tmp/bridge-scene-{name}.json << 'EOF'
{ ... scene graph JSON ... }
EOF
D3. Run the compiler
bridge-ds compile \
--input /tmp/bridge-scene-{name}.json \
--kb {kb-path} \
--transport {console|official}
The compiler outputs a JSON array of { id, code, description } chunks to stdout.
D4. Handle compiler errors
If the compiler returns errors to stderr:
- Read the error messages and suggestions
- Fix the scene graph JSON based on the suggestions (see
references/compiler-reference.md(repo-root) Section 8 for common errors) - Re-write the temp file and re-run the compiler
- Maximum 3 attempts. If still failing after 3, report errors to user and ask for guidance.
D5. Execute chunks in Figma
For each output chunk from the compiler:
Console transport:
figma_execute({ code: "{chunk.code}" })
Official transport:
use_figma({
fileKey: "{fileKey}",
description: "{chunk.description}",
code: "{chunk.code}"
})
Execute chunks sequentially. If a chunk fails:
- Read the error
- Report to user
- Attempt fix if the error is clear (e.g., font not loaded, component not found)
D6. Take screenshot
Take a screenshot AFTER the final chunk (not after each chunk):
Console: figma_take_screenshot({ node_id: "{rootNodeId}", file_key: "{fileKey}" })
Official: get_screenshot({ nodeId: "{rootNodeId}", fileKey: "{fileKey}" })
D7. Save snapshot
Save a snapshot of the design's node tree for future fix diffing.
Run a node tree extraction script via figma_execute (or use_figma), using the root node ID from D5.
Save to specs/active/{name}-snapshot.json:
{
"meta": {
"spec": "{name}",
"generatedAt": "{ISO timestamp}",
"rootNodeId": "{rootId}",
"fileKey": "{fileKey}",
"recipe": "{recipe ID or null}",
"learningsApplied": ["{learning IDs}"]
},
"tree": { ... extracted node tree ... }
}
Phase E — Present
E1. Show screenshot
Display the screenshot taken in D6.
E2. Report
Design compiled and executed.
File: {figma_url}
Created:
- {n} component instances
- {n} bound variables (colors + spacing + radius)
- {n} learnings applied
- Recipe: {recipe name or "from scratch"}
- Chunks: {n} executed
Warnings:
- {any issues}
E3. Offer next step
Looks good? Options:
- Describe changes -> I'll modify and recompile
- "I adjusted in Figma" -> triggers fix flow
- "done" / "ship it" -> triggers done flow
Iteration Loop
User describes changes (in conversation)
- Identify which parts of the scene graph need modification
- Update the scene graph JSON (modify affected nodes only)
- Re-write temp file
- Re-run the compiler
- Execute only the affected chunks (not the entire design)
- Take new screenshot
- Update snapshot
- Present result and offer next step again
User says "I adjusted in Figma"
Trigger the fix flow via the learning-from-corrections skill.
User says "done" / "ship it"
Trigger the done flow via the shipping-and-archiving skill.
Target Turn Budget
| Turn | Action |
|---|---|
| 1 | Phase A (context load) + Phase B (recipe match) |
| 2 | Phase C (CSpec generation, present plan to user) |
| 3 | User confirms or adjusts |
| 4 | Phase D (compile + execute all chunks) |
| 5 | Phase E (screenshot, report, offer next step) |
| 6+ | Iteration loop (if changes requested) |
Target: 5 turns for a first-pass generation. Each iteration adds 2-3 turns.
Compiler vs Raw Scripts
In v3, Claude NEVER writes raw Figma Plugin API scripts. The workflow is:
Claude produces scene graph JSON
-> Compiler resolves tokens, validates structure, generates code
-> Code chunks are executed via MCP
NOT:
Claude writes figma_execute scripts directly
This means:
- No need to load
figma-api-rules.md - No need to remember FILL-after-appendChild, resize-before-sizing, etc.
- No pre-script element audits (the compiler validates against registries)
- No script line counting or splitting logic
- Errors are caught by the compiler with actionable suggestions
The only time Claude touches raw Plugin API code is:
- Snapshot extraction scripts (small, standardized)
- Emergency fixes when the compiler cannot handle an edge case (rare)
NEVER write raw Figma Plugin API code. All scene graphs go through
lib/compiler/compile.ts (shipped as bridge-ds compile).
NEVER use hardcoded primitive values in the scene graph. Only
$token references.
</HARD-GATE>
Red Flags
See the full catalog at references/red-flags-catalog.md (repo-root).
Top flags for this skill:
- "I'll hardcode this hex once, it's faster" → Always use a semantic token.
- "The compiler is overkill for this tiny thing" → The compiler is the only path.
- "I remember this nodeId from my last session" → NodeIds are session-scoped, re-search.
Verification
This skill is gated by references/verification-gates.md (repo-root):
- Gate A (Compile) — mandatory before any
figma_execute/use_figma. - Gate B (Visual) — mandatory at the end of each iteration. Fresh screenshot in this turn + user confirmation.
Evidence to surface: compiler stdout, scene graph JSON, screenshot tool result, user confirmation text.
Skill-specific references
./references/templates/component-cspec.yaml— CSpec template for components./references/templates/screen-cspec.yaml— CSpec template for screens
The make flow (decision diagram)
digraph make_flow {
"User says 'make X'" [shape=doublecircle];
"Load context (recipes, learnings)" [shape=box];
"Generate CSpec" [shape=box];
"Compile" [shape=box];
"Compile exit 0?" [shape=diamond];
"Surface compile error" [shape=box style=filled fillcolor=lightcoral];
"Execute via MCP" [shape=box];
"Screenshot" [shape=box];
"User satisfied?" [shape=diamond];
"Gate A passed" [shape=doublecircle style=filled fillcolor=lightgreen];
"Capture intent diff" [shape=box];
"User says 'make X'" -> "Load context (recipes, learnings)";
"Load context (recipes, learnings)" -> "Generate CSpec";
"Generate CSpec" -> "Compile";
"Compile" -> "Compile exit 0?";
"Compile exit 0?" -> "Surface compile error" [label="no"];
"Surface compile error" -> "Generate CSpec";
"Compile exit 0?" -> "Execute via MCP" [label="yes"];
"Execute via MCP" -> "Screenshot";
"Screenshot" -> "User satisfied?";
"User satisfied?" -> "Gate A passed" [label="yes"];
"User satisfied?" -> "Capture intent diff" [label="no"];
"Capture intent diff" -> "Generate CSpec";
}