Skip to content
Design
Skill

/figma-style-binding

Triggers on any visual property change in Figma — creating text, setting colors, adjusting spacing/padding/gap/radius. Enforces that ALL values bind to Figma Styles or Variables, never hardcoded. Includes post-write QA verification.

From plugin
claude2figma
2074 skills

Claude Code

$ npx -y skills add senlindesign/claude2figma --skill figma-style-binding --agent claude-code

SKILL.md

figma-style-binding.SKILL.md
name: figma-style-binding
description: "Triggers on any visual property change in Figma — creating text, setting colors, adjusting spacing/padding/gap/radius. Enforces that ALL values bind to Figma Styles or Variables, never hardcoded. Includes post-write QA verification."
disable-model-invocation: false

Style Binding + QA

Every visual value must come from the design system. Supplements `figma-generate-design`. Prerequisite: `figma-preflight` must have run this session.

---

Binding Hierarchy

For any visual property, follow this order. Stop at the first match.

1. Connected Library  →  search_design_system → import → apply
2. Local Style        →  Style Registry → apply by ID
3. Local Variable     →  Variable Registry → apply by ID
4. Gap found          →  Report to user, wait for decision

---

Text

Every text node must use `textStyleId`. Individual font properties (`fontSize`, `fontFamily`, etc.) are forbidden.

const style = await figma.getStyleByIdAsync("<id>");
await figma.loadFontAsync(style.fontName);
node.textStyleId = "<id>";

If no local style matches, search libraries via `search_design_system`. If no match anywhere:

⚠️ Text style gap: no style for "[role]". Available: [top 5]. Use closest, or add missing style?

---

Color Fills

Every fill/stroke must bind to a COLOR Variable (preferred, supports theming) or Paint Style.

// Variable binding (preferred)
const variable = await figma.variables.getVariableByIdAsync("<id>");
const fill = { type: "SOLID", color: { r: 0, g: 0, b: 0 } };
node.fills = [figma.variables.setBoundVariableForPaint(fill, "color", variable)];

// Paint Style binding
node.fillStyleId = "<id>";

Never use raw `{ r, g, b }` without a binding.

---

Spacing, Padding, Gap, Radius

Bind to FLOAT Variables. `layoutMode` must be set BEFORE `setBoundVariable`.

node.setBoundVariable("paddingTop", spacingVar);
node.setBoundVariable("paddingBottom", spacingVar);
node.setBoundVariable("paddingLeft", spacingVar);
node.setBoundVariable("paddingRight", spacingVar);
node.setBoundVariable("itemSpacing", spacingVar);
node.setBoundVariable("cornerRadius", radiusVar);

Spacing can fall back to raw values temporarily with user confirmation. Color and text cannot.

---

Forbidden / Required

| Forbidden | Required | |---|---| | `node.fontSize = 24` | `node.textStyleId = id` | | `node.fills = [{ type: "SOLID", color: { r: .2, g: .4, b: 1 } }]` | Variable or Style binding | | `node.paddingLeft = 16` | `node.setBoundVariable("paddingLeft", var)` | | `node.cornerRadius = 8` | `node.setBoundVariable("cornerRadius", var)` | | Creating a Button from scratch | `importComponentByKeyAsync` from library |

---

QA Verification

After every `use_figma` call that creates or modifies nodes, run this verification on the returned node IDs:

const nodeIdsToAudit = [/* paste returned IDs */];
const results = [];

for (const id of nodeIdsToAudit) {
  const node = await figma.getNodeByIdAsync(id);
  if (!node) { results.push({ id, status: "NOT_FOUND" }); continue; }

  const checks = [];

  if (node.type === "TEXT") {
    checks.push({ prop: "textStyleId", bound: !!node.textStyleId });
  }

  if ("fills" in node && Array.isArray(node.fills) && node.fills.length > 0) {
    const bound = !!node.fillStyleId || (node.boundVariables?.fills?.length > 0);
    checks.push({ prop: "fills", bound });
  }

  if ("layoutMode" in node && node.layoutMode !== "NONE") {
    for (const p of ["paddingLeft","paddingRight","paddingTop","paddingBottom","itemSpacing"]) {
      if (p in node) checks.push({ prop: p, bound: !!(node.boundVariables && p in node.boundVariables) });
    }
  }

  if ("cornerRadius" in node && node.cornerRadius > 0) {
    checks.push({ prop: "cornerRadius", bound: !!(node.boundVariables && "cornerRadius" in node.boundVariables) });
  }

  const failed = checks.filter(c => !c.bound);
  results.push({ id, name: node.name, type: node.type, status: failed.length === 0 ? "PASS" : "FAIL", failed: failed.map(c => c.prop) });
}

return { auditResults: results };

**If FAIL:** Fix each unbound property using the binding rules above, then re-audit. Do not proceed to the next design step until all pass.

**Report format:**

✅ All [N] nodes passed.
// or
❌ FAIL "Card" (FRAME) — paddingTop, cornerRadius unbound. Fixing...
Read more
@senlindesign
Ships withclaude2figma

Design System compliance for AI-generated Figma designs. 4 skills, 3-step preflight, zero raw values. Quick start: Clone, copy skills to .claude/skills/, paste your Figma URL into CLAUDE.md, say "let's start". Full install guide below.

Stars on GitHub

Momentum

stars in total
207
Star history of senlindesign/claude2figma on GitHub, a chart by star-history.com

Other skills on claude2figma.

component-rules

component-rules

Triggers when building any UI element in Figma — 'create a card', 'build a nav', 'add a section', 'make a component'. Enforces…

More skills from other plugins.

impeccable

impeccable

Use to design, redesign, critique, audit, polish or animate a frontend: websites, landing pages, dashboards, app UI, components,…

@pbakaus@pbakausInstall Skill

Questions about figma-style-binding.

Which coding agents can use figma-style-binding?
figma-style-binding installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.