All skills
openai avatar

/figma-use

@0e7823c official
by openaiopenai/skills28k stars
1,891

**MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `use_figma` tool call. NEVER call `use_figma` directly without loading this skill first. Skipping it causes common, hard-to-debug failures. Trigger whenever the user wants to perform a write action or a unique read action that requires JavaScript execution in the Figma file context — e.g. create/edit/delete nodes, set up variables or tokens, build components and variants, modify auto-layout or fills, bind variables to properties, or inspect file structure programmatically.

Use this Skill: https://skilld.dev/gh/openai/skills/figma-use

This session only. Nothing lands on disk.

referencesvariable-patterns.md

≈3.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Variable & Token API Patterns

Part of the use_figma skill. How to correctly create, bind, scope, and alias variables using the Plugin API.

For design system context (aliasing strategy, mode decisions, code syntax philosophy, grouping conventions), see wwds-variables.

Contents

  • Creating Variable Collections and Modes
  • Creating Variables (All Types)
  • Binding Variables to Node Properties
  • Variable Scopes: What They Are and How to Set Them
  • Variable Aliasing (VARIABLE_ALIAS)
  • Code Syntax (setVariableCodeSyntax)
  • Discovering Existing Variables in the File
  • Effect Styles (For Shadows)

Creating Variable Collections and Modes

const collection = figma.variables.createVariableCollection("MyCollection");

// A new collection starts with 1 mode named "Mode 1" — always rename it
collection.renameMode(collection.modes[0].modeId, "Light");

// Add additional modes (returns the new modeId)
const darkModeId = collection.addMode("Dark");
const lightModeId = collection.modes[0].modeId;

Mode limits are plan-dependent: Free = 1 mode, Professional = up to 4, Organization/Enterprise = 40+. If you need many modes, split across multiple collections.

Creating Variables (All Types)

figma.variables.createVariable(name, collection, resolvedType) — the second argument accepts a collection object or ID string (object preferred).

// COLOR — values use {r, g, b, a} (all 0–1 range, includes alpha)
const colorVar = figma.variables.createVariable("my-color", collection, "COLOR");
colorVar.setValueForMode(modeId, { r: 0.2, g: 0.36, b: 0.96, a: 1 });

// FLOAT — for spacing, radii, sizing, numeric values
const floatVar = figma.variables.createVariable("my-spacing", collection, "FLOAT");
floatVar.setValueForMode(modeId, 16);

// STRING — for font families, font style names, any text value
const stringVar = figma.variables.createVariable("my-font", collection, "STRING");
stringVar.setValueForMode(modeId, "Inter");

// BOOLEAN
const boolVar = figma.variables.createVariable("my-flag", collection, "BOOLEAN");
boolVar.setValueForMode(modeId, true);

Note: Paint colors use {r, g, b} (no alpha), but COLOR variable values use {r, g, b, a} (with alpha). Don't mix them up.

Binding Variables to Node Properties

Color Bindings (Fills, Strokes)

setBoundVariableForPaint returns a NEW paint — you must capture the return value:

// Create a base paint, bind the variable, assign the result
const basePaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } };
const boundPaint = figma.variables.setBoundVariableForPaint(basePaint, "color", colorVar);
node.fills = [boundPaint];

// Only SOLID paints support color variable binding — gradients/images will throw

Numeric Bindings (Spacing, Radii, Sizing)

setBoundVariable binds FLOAT/STRING/BOOLEAN variables to node properties:

// Padding
node.setBoundVariable("paddingTop", spacingVar);
node.setBoundVariable("paddingBottom", spacingVar);
node.setBoundVariable("paddingLeft", spacingVar);
node.setBoundVariable("paddingRight", spacingVar);

// Gap
node.setBoundVariable("itemSpacing", gapVar);
node.setBoundVariable("counterAxisSpacing", gapVar);

// Corner radius — use individual corners, NOT cornerRadius
node.setBoundVariable("topLeftRadius", radiusVar);
node.setBoundVariable("topRightRadius", radiusVar);
node.setBoundVariable("bottomLeftRadius", radiusVar);
node.setBoundVariable("bottomRightRadius", radiusVar);

// Size
node.setBoundVariable("width", sizeVar);
node.setBoundVariable("height", sizeVar);
node.setBoundVariable("minWidth", sizeVar);
node.setBoundVariable("maxWidth", sizeVar);

// Other
node.setBoundVariable("opacity", opacityVar);
node.setBoundVariable("strokeWeight", strokeVar);

Not bindable via setBoundVariable: fontSize, fontWeight, lineHeight — set these directly on text nodes.

Effect Bindings

const effectCopy = JSON.parse(JSON.stringify(node.effects[0]));
const newEffect = figma.variables.setBoundVariableForEffect(effectCopy, "color", colorVar);
// ⚠️ Returns a NEW effect — must capture return value!
node.effects = [newEffect];
// Valid fields: "color" (COLOR), "radius" | "spread" | "offsetX" | "offsetY" (FLOAT)

Applying a Mode to a Frame

// All bound children of this frame will resolve to the specified mode's values
frame.setExplicitVariableModeForCollection(collection.id, modeId);

Without this, all nodes use the collection's default (first) mode.

Variable Scopes: What They Are and How to Set Them

variable.scopes controls which Figma property pickers show the variable. The default is ["ALL_SCOPES"] which shows it everywhere — this is almost never what you want.

variable.scopes = ["FRAME_FILL", "SHAPE_FILL"];  // only fill pickers
variable.scopes = ["TEXT_FILL"];                   // only text color picker
variable.scopes = ["GAP"];                         // only gap/spacing pickers
variable.scopes = ["CORNER_RADIUS"];               // only radius pickers
variable.scopes = [];                              // hidden from all pickers

All valid scope values: ALL_SCOPES, TEXT_CONTENT, CORNER_RADIUS, WIDTH_HEIGHT, GAP, ALL_FILLS, FRAME_FILL, SHAPE_FILL, TEXT_FILL, STROKE_COLOR, STROKE_FLOAT, EFFECT_FLOAT, EFFECT_COLOR, OPACITY, FONT_FAMILY, FONT_STYLE, FONT_WEIGHT, FONT_SIZE, LINE_HEIGHT, LETTER_SPACING, PARAGRAPH_SPACING, PARAGRAPH_INDENT

Always check the existing file's scope patterns before creating variables — match whatever convention is already in use. See "Discovering Existing Variables" below.

Variable Aliasing (VARIABLE_ALIAS)

A variable's value can reference another variable via alias. This is how semantic tokens reference primitive tokens:

// Set a variable's value as an alias to another variable
semanticVar.setValueForMode(modeId, {
  type: 'VARIABLE_ALIAS',
  id: primitiveVar.id
});

When the primitive changes, the semantic variable updates automatically across all modes.

Code Syntax (setVariableCodeSyntax)

Links a Figma variable back to its code counterpart. Call once per platform:

variable.setVariableCodeSyntax('WEB', 'var(--color-bg-default)');
variable.setVariableCodeSyntax('ANDROID', 'colorBgDefault');
variable.setVariableCodeSyntax('iOS', 'Color.bgDefault');

// Read back: variable.codeSyntax → { WEB: '...', ANDROID: '...', iOS: '...' }

When deriving CSS names from Figma names, replace both slashes AND spaces with hyphens:

// WRONG — leaves spaces in CSS variable name
`var(--${figmaName.replace(/\//g, '-').toLowerCase()})`

// CORRECT — replace all whitespace and slashes
`var(--${figmaName.replace(/[\s\/]+/g, '-').toLowerCase()})`

// BEST — use the original CSS variable name from the source, not a derived one
`var(${token.cssVar})`

Discovering Existing Variables in the File

Always inspect the file's existing variables before creating new ones. Different files use different naming conventions, scope patterns, and collection structures. Match what's already there.

List collections with mode info

(async () => {
  try {
    const collections = figma.variables.getLocalVariableCollections();
    const results = collections.map(c => ({
      name: c.name,
      id: c.id,
      varCount: c.variableIds.length,
      modes: c.modes.map(m => ({ name: m.name, id: m.modeId }))
    }));
    figma.closePlugin(JSON.stringify(results));
  } catch(e) { figma.closePluginWithFailure(e.toString()); }
})()

Inspect scope patterns used in existing variables

(async () => {
  try {
    const collections = figma.variables.getLocalVariableCollections();
    const scopeGroups = {};
    for (const c of collections) {
      for (const id of c.variableIds) {
        const v = figma.variables.getVariableById(id);
        const key = JSON.stringify(v.scopes);
        if (!scopeGroups[key]) scopeGroups[key] = [];
        scopeGroups[key].push(v.name);
      }
    }
    figma.closePlugin(JSON.stringify(scopeGroups));
  } catch(e) { figma.closePluginWithFailure(e.toString()); }
})()

Build a name→variable lookup for reuse

const varByName = {};
for (const v of figma.variables.getLocalVariables()) {
  varByName[v.name] = v;
}

// Bind to existing variable by name — no hex values needed
function bindFill(node, varName) {
  const v = varByName[varName];
  if (!v) throw new Error(`Variable not found: ${varName}`);
  const paint = figma.variables.setBoundVariableForPaint(
    { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', v
  );
  node.fills = [paint];
}

Only create new variables for tokens that have no match in the file. After building the lookup, compare against the needed tokens and create variables only for the delta.

Listing Collections with Full Variable Details

The async API returns richer data including code syntax and scopes per variable:

/**
 * Lists all local variable collections defined in the current Figma file,
 * including metadata for their modes and variables.
 *
 * @returns {Promise<Array<{
 *   name: string,
 *   id: string,
 *   modes: Array<[name: string, modeId: string]>,
 *   variables: Array<[name: string, id: string, codeSyntax: object, scopes: string[]]>
 * }>>}
 */
async function listVariableCollectionsAndVariables() {
  const collections = await figma.variables.getLocalVariableCollectionsAsync();
  const results = [];
  for (const collection of collections) {
    const vars = [];
    for (const id of collection.variableIds) {
      const v = await figma.variables.getVariableByIdAsync(id);
      vars.push([v.name, v.id, v.codeSyntax, v.scopes]);
    }
    results.push({
      name: collection.name,
      id: collection.id,
      modes: collection.modes.map(m => [m.name, m.modeId]),
      variables: vars
    });
  }
  return results;
}

Full runnable script:

(async () => {
  try {
    const results = await listVariableCollectionsAndVariables();
    figma.closePlugin(JSON.stringify(results));
  } catch(e) { figma.closePluginWithFailure(e.toString()); }
})()

Setting and Removing Code Syntax

Must be executed in the file the variable is defined in:

/**
 * Set the code syntax for a variable for a specific platform.
 *
 * @param {string} variableId
 * @param {'WEB'|'ANDROID'|'iOS'} platform
 * @param {string} syntax
 */
async function setVariableCodeSyntax(variableId, platform, syntax) {
  const variable = await figma.variables.getVariableByIdAsync(variableId);
  variable.setVariableCodeSyntax(platform, syntax);
}

/**
 * Remove code syntax for a variable for one or more platforms.
 *
 * @param {string} variableId
 * @param {Array<'WEB'|'ANDROID'|'iOS'>} platforms — defaults to all three
 */
async function removeVariableCodeSyntax(variableId, platforms = ["WEB", "ANDROID", "iOS"]) {
  const variable = await figma.variables.getVariableByIdAsync(variableId);
  for (const platform of platforms) {
    variable.removeVariableCodeSyntax(platform);
  }
}

/**
 * Set a value for a variable in a specific mode.
 * For aliases, value must be: { type: 'VARIABLE_ALIAS', id: '<variableId>' }
 *
 * @param {string} variableId
 * @param {string} modeId
 * @param {string|number|boolean|RGB|RGBA|{type: 'VARIABLE_ALIAS', id: string}} value
 */
async function setVariableValueForMode(variableId, modeId, value) {
  const variable = await figma.variables.getVariableByIdAsync(variableId);
  variable.setValueForMode(modeId, value);
}

Effect Styles (For Shadows)

Shadows can't be stored as variables. Use effect styles. For comprehensive patterns, see effect-style-patterns.md.

const shadow = figma.createEffectStyle();
shadow.name = "Shadow/Subtle";
shadow.effects = [{
  type: "DROP_SHADOW",
  color: { r: 0, g: 0, b: 0, a: 0.06 },
  offset: { x: 0, y: 2 },
  radius: 8,
  spread: 0,
  visible: true,
  blendMode: "NORMAL"
}];

// Apply to a node
frame.effectStyleId = shadow.id;

Source: SKILL.md on GitHub

No alerts16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides a robust framework for interacting with the Figma Plugin API via a Model Context Protocol (MCP) server. It includes comprehensive documentation, design system alignment guidelines, and architectural best practices. While the skill enables dynamic code execution and processes external document data, these are standard features of its intended use as a development tool for the Figma platform. The instructions emphasize validation and incremental workflows to manage these capabilities safely.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 0e7823c. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Activeupdated 6 months ago
  • figma
  • plugin-api
  • javascript
  • design-automation
  • variables
  • components
  • design-systems

README badge

README badge for openai/skills/figma-use

This skill loads the Figma Plugin API ruleset and reference docs, making it a prerequisite for executing JavaScript in Figma files via the use_figma MCP tool. It covers critical constraints like async/await patterns, page switching, node ID tracking, and incremental workflow practices to avoid race conditions and atomicity failures.

Generated from the current SKILL.md.

Do I need to load this skill before every use_figma call?
Yes. You must pass `skillNames: "figma-use"` and load this skill before every `use_figma` tool call. Skipping it causes hard-to-debug failures.
Can I use figma.notify() to show messages in the plugin UI?
No. `figma.notify()` throws "not implemented". Use `return` to send data back to the agent instead.
What should I do if a use_figma call fails with an error?
Stop and read the error message carefully. `use_figma` is atomic — if a script errors, no changes are made to the file. Fix the script based on the error and retry.
How do I switch to a different page in a use_figma script?
Use `await figma.setCurrentPageAsync(page)` to switch pages and load their content. The sync setter `figma.currentPage = page` throws an error.
What should I return from a use_figma script that creates or modifies nodes?
Always return all affected node IDs in a structured object, e.g. `return { createdNodeIds: [...], mutatedNodeIds: [...] }`. This is required so subsequent calls can reference those nodes.

Generated from the current SKILL.md. These answers refresh after source changes.