Skip to content

DOL Figma AI Workflows

activeUpdated

Runbooks for the 5 most-common Figma AI tasks. Each workflow has:

  • Precondition - what must be true before starting
  • Steps - sequence of MCP calls / decisions
  • Verify - deterministic check that the step worked
  • Failure modes - what goes wrong + recovery

🛑 Match-and-refuse (workflow entry-level): nếu bạn sắp run workflow mà KHÔNG biết precondition status (MCP connected? cache fresh? variable ID known?), STOP - check precondition block trước. Skipping precondition = silent runtime failure ở middle-of-workflow (hardest to debug). Prefer lookup.py + explicit verify BEFORE side-effect operations.


Workflow 1 - Bind a variable to fill / text-color / icon-color / border-color

Section titled “Workflow 1 - Bind a variable to fill / text-color / icon-color / border-color”

When to use: any design task where a color value needs to come from DS rather than hardcoded hex.

📌 Also available as Claude Code skill: .claude/skills/figma-bind-variable/SKILL.md (R23 extraction). Skill auto-invokes when task phrase matches “bind variable”, “apply token”, “set fill color”; lighter than reading this whole file. This workflow remains canonical reference; skill is the precise-task lean entry.

  • Know what you’re styling (fill / text / icon / border) - variable-selection.md §1
  • Know semantic role (brand / neutral / info / success / warning / danger / special / ai) - §2
  • Know intensity (dim / soft / primary / secondary / subtle) - §3
  • figma-console-mcp connected; variable collection loaded
Terminal window
# Get Figma ID + collection + scopes for a variable in <500 tokens:
python3 ../tools/lookup.py variable text-color/danger-primary

Then bind:

const target = await figma.variables.getVariableByIdAsync('VariableID:18484:36683');
node.setBoundVariable('fills', target);

Alternative - grep variable-id-map.md (R17+)

Section titled “Alternative - grep variable-id-map.md (R17+)”

If lookup.py unavailable, grep variable-id-map.md for the variable name (file is sectioned by collection). Same direct binding pattern.

If id-map stale OR cross-file work where IDs differ:

  1. Pick the variable name by resolving Category × Family × Level in variable-selection.md §4
  2. Verify it exists - grep variable-registry.md for the full name. If not found → anti-pattern figma-naming-6-binding#05 (don’t invent)
  3. Resolve Figma variable ID via MCP at runtime:
    const vars = await figma.variables.getLocalVariablesAsync();
    const target = vars.find(v => v.name === 'text-color/neutral-primary');
    // Note: Figma uses slash-separated name (text-color/neutral-primary), CSS uses hyphen (--text-color-neutral-primary)
  4. Bind to node property:
    const node = figma.currentPage.selection[0]; // or specific node lookup
    // For text-color:
    node.setBoundVariable('fills', target);
    // For fill (frame/shape bg):
    node.setBoundVariable('fills', target);
    // For border (stroke):
    node.setBoundVariable('strokes', target);
  5. Read back boundVariables to confirm: node.boundVariables.fills should show the variable ID.
  • node.boundVariables[<property>] contains the variable ID (not undefined)
  • Screenshot the node → color matches resolvedValue from registry
  • Switch Figma mode (GM ↔ KM / light ↔ dark) → color changes if darkValue differs, stays same otherwise
SymptomCauseRecovery
target is undefined after lookupVariable name mismatch (hyphen vs slash, wrong category prefix)Re-read variable-registry.md; Figma uses /, CSS uses -
setBoundVariable throwsWrong property name (e.g., fill instead of fills)Property names are plural: fills, strokes, effects
Color renders but doesn’t change on mode switchHardcoded hex instead of bound variableCheck boundVariables; if empty, bind the variable explicitly
Icon uses text-color/* instead of icon-color/*Category mismatchfigma-naming-6-binding#02: re-bind with icon-color/*

Workflow 2 - Extract boundVariables from reference component

Section titled “Workflow 2 - Extract boundVariables from reference component”

When to use: building a new component similar to existing one. Shortest path to DS-aligned bindings.

  • Know the reference component’s node ID (from Figma selection or MCP query)
  • figma-console-mcp connected
  1. Get the reference node:
    const ref = figma.root.findOne(n => n.id === '<nodeId>');
    // OR from current selection:
    const ref = figma.currentPage.selection[0];
  2. Read all bound variables:
    const recurse = (node, acc = {}) => {
    if (node.boundVariables && Object.keys(node.boundVariables).length > 0) {
    acc[node.name] = node.boundVariables;
    }
    if (node.children) node.children.forEach(c => recurse(c, acc));
    return acc;
    };
    const bindings = recurse(ref);
  3. Resolve variable IDs → names for readability:
    const allVars = await figma.variables.getLocalVariablesAsync();
    const byId = Object.fromEntries(allVars.map(v => [v.id, v.name]));
    // Map each boundVariables entry id → name via byId
  4. Apply the same bindings to the new component’s equivalent nodes (by name / by position).
  • New component’s boundVariables structure matches reference’s
  • Screenshot both → visual parity across mode switches (GM ↔ KM ↔ dark)
SymptomCauseRecovery
boundVariables empty on referenceReference uses hardcoded hex (anti-pattern figma-naming-6-binding#01)Don’t copy the hex - look up correct variable via variable-selection.md
Reference binds to DELETED_* variable IDSource variable was deleted; binding staleSee Workflow 4 (DELETED cleanup)
Binding copies but color renders wrongReference was in different mode (KM) when variable boundTest new component in both modes; may need mode-switch before applying binding

Workflow 3 - Audit GM/KM mode leaks in a component

Section titled “Workflow 3 - Audit GM/KM mode leaks in a component”

When to use: QA’ing a component for mode-switch integrity. Required before declaring component ready (Q-gate F6 in FIGMA-AI.md §5.1).

  • Component has boundVariables set on child nodes
  • figma-console-mcp connected
  1. Collect all bound variable IDs in the component (recurse into children):
    const ids = new Set();
    const recurse = (node) => {
    for (const prop in (node.boundVariables || {})) {
    const binding = node.boundVariables[prop];
    if (Array.isArray(binding)) binding.forEach(b => ids.add(b.id));
    else if (binding && binding.id) ids.add(binding.id);
    }
    if (node.children) node.children.forEach(recurse);
    };
    recurse(component);
  2. Inspect each variable’s valuesByMode:
    const allVars = await figma.variables.getLocalVariablesAsync();
    const mixed = [];
    for (const id of ids) {
    const v = allVars.find(x => x.id === id);
    if (!v) continue;
    // If variable only has GM mode value but component is in KM collection → leak
    const modes = Object.keys(v.valuesByMode);
    if (modes.length === 1 && !modes.includes('KM')) mixed.push(v.name);
    }
  3. Check collection consistency: ALL variables in a single component SHOULD come from the same variable collection (DOL Design System V2). Cross-collection mixing = leak.
  • Switching Figma mode from GM → KM → GM: color values change predictably and revert identically
  • No DELETED_* references (see Workflow 4)
  • All variable IDs resolve to same variableCollectionId
SymptomCauseRecovery
Variable has no KM valueCollection missing KM mode entryFlag to DS team; don’t hand-add KM value to individual node
Variables from multiple collections in one componentHistorical import driftRe-bind using current DS V2 collection variables
valuesByMode has unexpected mode IDsStale mode referencesfigma.variables.getVariableCollectionByIdAsync to inspect collection structure

Workflow 4 - Clean up DELETED_* references

Section titled “Workflow 4 - Clean up DELETED_* references”

When to use: component shows DELETED_<id> in boundVariables (source variable was deleted from DS).

  • Figma MCP connected
  • Know which variable name should replace the deleted one (consult variable-registry.md)
  1. Identify deleted bindings:
    const deleted = [];
    const recurse = (node) => {
    for (const prop in (node.boundVariables || {})) {
    const binding = node.boundVariables[prop];
    const ids = Array.isArray(binding) ? binding.map(b => b.id) : [binding?.id];
    ids.filter(Boolean).forEach(id => {
    if (id.startsWith('DELETED_')) {
    deleted.push({ node: node.name, nodeId: node.id, prop, id });
    }
    });
    }
    if (node.children) node.children.forEach(recurse);
    };
    recurse(figma.currentPage);
  2. For each DELETED_*, identify the intended replacement:
    • Check git history of DS for the deleted variable name (if possible)
    • OR infer from component context (this node is styled as error text → should be text-color-danger-primary)
    • OR ask user if ambiguous
  3. Rebind to the new variable per Workflow 1
  4. Re-read boundVariables - should no longer contain DELETED_*
  • grep DELETED_ in component’s serialized boundVariables → empty
  • Component renders correctly in all modes
  • Q-gate F7 (FIGMA-AI §5.1) now passes
SymptomCauseRecovery
Can’t identify replacementDeleted variable has no clear analogueAsk user; worst case, unbind property and use hardcoded hex with written fallback note
After rebind, DELETED_* still appearsBinding happened on wrong nodeRe-check node path; DELETED may live on a child node with same property
Multiple DELETED bindings on same nodeHistorical churnBatch re-bind: iterate through boundVariables map, apply replacements

Workflow 5 - Instantiate a published component from DS library

Section titled “Workflow 5 - Instantiate a published component from DS library”

When to use: building a screen that needs a standard DOL component (button, alert, input, card).

📌 Also available as Claude Code skill: .claude/skills/figma-instantiate-component/SKILL.md (R24 extraction). Pairs with figma-bind-variable skill - together cover the 2 primary Figma operations. Skill auto-invokes when task phrase matches “instantiate component”, “create instance”, “import this Button”, etc. This workflow remains canonical reference.

  • DS-Token component library is published in Figma (or you’re working in the DS file directly)
  • component-registry.md is current (re-sync via tools/resync-figma.md if DS changed)
Terminal window
# 1. Find component key by name:
python3 ../tools/lookup.py component "Button/Primary"
# 2. Get full variant property schema (only if you need to set variants):
python3 ../tools/lookup.py variants ".Button icon"

Then instantiate:

const key = '783b9c782d5b6dd44d920768ffa84c2d7f9ffe6a'; // from lookup.py output
const imported = await figma.importComponentByKeyAsync(key);
const instance = imported.createInstance();
figma.currentPage.appendChild(instance);
instance.x = 100;
instance.y = 100;
instance.setProperties({
Size: 'xl', // from `lookup.py variants` output
Shape: 'Square',
});

Alternative - read components/.md directly

Section titled “Alternative - read components/.md directly”

If browsing all components on a page (e.g., “show me all Modal variants”):

  • Open ../components/<page-slug>.md (e.g., components/modal.md)
  • File ≤300 lines; contains full variant property tables
  • Page slugs listed in component-registry.md slim index

If registry stale or component name unclear:

  1. Discover via MCP:
    const libs = await figma.teamLibrary.getAvailableLibrariesAsync();
    const target = libs.flatMap(l => l.components).find(c => c.name.includes('Button'));
    const key = target.key;
  2. Continue from “instantiate” above.
  3. After task: flag to update registry per tools/resync-figma.md.
  • instance.type === 'INSTANCE'
  • instance.mainComponent.key === key (matches registry row)
  • Component renders at expected position with correct variant
  • Variant properties set without silent failure (check instance.componentProperties)
SymptomCauseRecovery
target is undefined in library searchComponent name mismatch or library not publishedVerify name in published library; check user has DS V2 library enabled in file
importComponentByKeyAsync throwsComponent key stale or library brokenAsk user to re-publish library; check component wasn’t deleted
Variant property set failsProperty name case-sensitive or wrong typeRead instance.componentProperties to see available properties + types
Instance looks wrong vs expectedMode mismatch (file in KM, component designed in GM)Check file mode; switch temporarily to match component origin if needed

Meta - when any workflow’s MCP access fails

Section titled “Meta - when any workflow’s MCP access fails”

If figma-console-mcp is disconnected or rate-limited:

  1. Don’t retry blindly - check connection status first
  2. Fall back to: ask user to provide the node ID / component key / variable name manually
  3. Document the failure in ai-memory/queue/errors.jsonl via /reflect
  • naming-convention.md - 4-category rule + intensity levels
  • variable-registry.md - all 838 AI-facing tokens with resolved values
  • variable-selection.md - intent → variable decision tree
  • ../anti-pattern-registry.md - figma-naming-6-binding scope (5 binding anti-patterns)
  • ../ai-entry/FIGMA-AI.md §5.1 - F1-F7 quantified gates (apply these after any workflow completes)
  • Sessional lessons: ai-memory/lessons/figma-*.md (MCP performance, API patterns, screenshot hygiene)