agentleFS
dylantarredylantarre/design-system-skills/skills/tools/figma/SKILL.md

figma

Skill50 starsChanged 11 months agoMITView on GitHub

Syncs design tokens between code and Figma using Variables API or Tokens Studio plugin. Use when establishing Figma-to-code workflows, exporting Figma tokens, or setting up design-development handoff.

  • Reads credentials
  • Sends data out

52 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use analytics-metrics, aws-account-management and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • hoodiniSkill

    analytics-metrics

    Build data visualization and analytics dashboards. Use when creating charts, KPI displays, metrics dashboards, or data visualization components. Triggers on analytics, dashboard, charts, metrics, KPI, data visualization, Recharts.

    Also in 3 projects that use this

  • hoodiniSkill

    aws-account-management

    Manage AWS accounts, organizations, IAM, and billing. Use when setting up AWS Organizations, managing IAM policies, controlling costs, or implementing multi-account strategies. Triggers on AWS Organizations, AWS IAM, AWS billing, Cost Explorer, SCPs, multi-account, AWS SSO, Identity Center.

    Also in 3 projects that use this

  • LangChainSkill

    slideshow

    Author a HyperFrames slideshow — a presentation, pitch deck, or interactive deck with discrete slides, fragment reveals, branching, hotspot navigation, and built-in presenter mode with speaker notes; also converts an existing page into a deck. Output is a navigable deck, not a rendered MP4. If the user didn't explicitly ask for a slideshow, confirm before authoring. Unclear → /hyperframes.

    Also in 8 projects that use this

  • jaron-goh-centrSkill

    talking-head-recut

    Package an existing talking-head / interview / podcast video with timed, designed GRAPHIC OVERLAY cards — kinetic titles, lower-thirds, data callouts, quotes, side panels, picture-in-picture — synced to the transcript, on a 16:9 / 9:16 / 4:5 canvas of your choice; the clip plays untouched underneath. Trigger on "graphic overlays", "on-screen graphics", "package / dress up my video". Not plain subtitles (/embedded-captions). Unclear → /hyperframes.

    Also in 6 projects that use this

  • LangChainSkill

    faceless-explainer

    Turn arbitrary text — an article, notes, a topic, a brief — into a faceless explainer video: there is no site or footage to capture, so the visuals are invented per scene (typography, abstract graphics, diagrams, data-viz). Use for topic explainers, concept breakdowns, how-tos, listicles. Not a video built from a website (/product-launch-video — promo or tour). Unclear → /hyperframes.

    Also in 6 projects that use this

  • ElyzeSolutionsSkill

    captions-overlay

    Overlay doctrine for the embedded-captions workflow — the caption MODEL (drop / rail / embed) and the rule that captions are an OVERLAY composited on top of the film, never a reserved bottom band you shift content up to avoid. Load when adding captions/subtitles to a talking-head or launch video, when deciding whether a phrase should be dropped, ride the verbatim rail, or be promoted to a scarce embedded climax, when laying out a composition that will carry captions (do NOT reserve a keep-out band), or when centering a composition on the true frame center under captions. Quotes the rail+embed model from embedded-captions and constraint #13 (captions overlay, keep-out band retired) from the product-launch-video scene agent. Applies ON TOP of embedded-captions.

    Also in 3 projects that use this

  • +4 more from hoodini

The file

We split this skill into the rules it gives an agent

2 rules. 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 44 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Semantic tokens: Use aliases for theme-aware values

    line 481
  2. Distinctive:

    Documentation: Keep a mapping document for designers

    line 482

“Common” is measured: kept by at least 6 in 10 of the 44 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Figma Token Integration

Overview

Sync design tokens between code and Figma using Figma Variables, the Tokens Studio plugin, or Style Dictionary. Establish a single source of truth workflow for design-to-code handoff.

When to Use

  • Setting up Figma Variables from code tokens
  • Exporting Figma styles to code tokens
  • Establishing a design token workflow
  • Syncing colors, typography, and spacing with Figma

Quick Reference: Figma Token Types

Figma TypeMaps ToExample
Color Variable--color-*--color-primary-500
Number Variable--spacing-*, --radius-*--spacing-md
String VariableFont family, mode names--font-sans
Boolean VariableFeature flagsTheme mode toggles

The Process

  1. Choose workflow direction:
    • Code → Figma (tokens are source of truth in code)
    • Figma → Code (designers own tokens in Figma)
    • Bidirectional (sync both ways)
  2. Select tooling: Native Variables, Tokens Studio, or custom
  3. Define token structure: Categories, naming, modes
  4. Set up sync mechanism: Plugin, API, or manual
  5. Document workflow: Who updates what, when to sync

Workflow Options

ApproachBest ForTools
Figma Variables (native)Simple setups, color/number tokensFigma UI, REST API
Tokens StudioComplex tokens, multi-fileTokens Studio plugin + GitHub
Style DictionaryEnterprise, CI/CD integrationStyle Dictionary + Figma API
Manual syncSmall teams, infrequent changesCopy/paste with conventions

Figma Variables (Native)

Creating Variables from Code Tokens

Token structure for Figma:

{
  "color": {
    "primary": {
      "50": { "value": "#eff6ff", "type": "color" },
      "100": { "value": "#dbeafe", "type": "color" },
      "500": { "value": "#3b82f6", "type": "color" },
      "900": { "value": "#1e3a8a", "type": "color" }
    },
    "gray": {
      "50": { "value": "#f9fafb", "type": "color" },
      "900": { "value": "#111827", "type": "color" }
    }
  },
  "spacing": {
    "xs": { "value": 4, "type": "number" },
    "sm": { "value": 8, "type": "number" },
    "md": { "value": 16, "type": "number" },
    "lg": { "value": 24, "type": "number" }
  },
  "radius": {
    "sm": { "value": 4, "type": "number" },
    "md": { "value": 8, "type": "number" },
    "lg": { "value": 16, "type": "number" },
    "full": { "value": 9999, "type": "number" }
  }
}

Figma REST API Example

Create variables via API:

import Anthropic from '@anthropic-ai/sdk'; // Example using Figma API

interface FigmaVariable {
  name: string;
  resolvedType: 'COLOR' | 'FLOAT' | 'STRING' | 'BOOLEAN';
  valuesByMode: Record<string, any>;
}

async function createFigmaVariables(
  fileKey: string,
  collectionName: string,
  tokens: Record<string, any>
): Promise<void> {
  const accessToken = process.env.FIGMA_ACCESS_TOKEN;

  // Step 1: Create variable collection
  const collectionResponse = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/variables`,
    {
      method: 'POST',
      headers: {
        'X-Figma-Token': accessToken,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        variableCollections: [
          {
            action: 'CREATE',
            name: collectionName,
            initialModeId: 'light',
          },
        ],
      }),
    }
  );

  const { variableCollections } = await collectionResponse.json();
  const collectionId = variableCollections[0].id;

  // Step 2: Create variables
  const variables: FigmaVariable[] = [];

  for (const [category, values] of Object.entries(tokens)) {
    for (const [name, token] of Object.entries(values as Record<string, any>)) {
      variables.push({
        name: `${category}/${name}`,
        resolvedType: token.type === 'color' ? 'COLOR' : 'FLOAT',
        valuesByMode: {
          light: parseValue(token.value, token.type),
        },
      });
    }
  }

  await fetch(`https://api.figma.com/v1/files/${fileKey}/variables`, {
    method: 'POST',
    headers: {
      'X-Figma-Token': accessToken,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      variables: variables.map((v) => ({
        action: 'CREATE',
        variableCollectionId: collectionId,
        ...v,
      })),
    }),
  });
}

function parseValue(value: string | number, type: string): any {
  if (type === 'color') {
    // Convert hex to Figma RGBA
    const hex = value as string;
    const r = parseInt(hex.slice(1, 3), 16) / 255;
    const g = parseInt(hex.slice(3, 5), 16) / 255;
    const b = parseInt(hex.slice(5, 7), 16) / 255;
    return { r, g, b, a: 1 };
  }
  return value;
}

Variable Modes (Theming)

Light/dark mode setup:

{
  "variableCollections": [
    {
      "name": "Semantic Colors",
      "modes": [
        { "name": "Light", "modeId": "light" },
        { "name": "Dark", "modeId": "dark" }
      ]
    }
  ],
  "variables": [
    {
      "name": "background/default",
      "valuesByMode": {
        "light": { "type": "VARIABLE_ALIAS", "id": "color/gray/50" },
        "dark": { "type": "VARIABLE_ALIAS", "id": "color/gray/900" }
      }
    },
    {
      "name": "text/primary",
      "valuesByMode": {
        "light": { "type": "VARIABLE_ALIAS", "id": "color/gray/900" },
        "dark": { "type": "VARIABLE_ALIAS", "id": "color/gray/50" }
      }
    }
  ]
}

Tokens Studio Integration

Token File Structure

tokens.json (Tokens Studio format):

{
  "global": {
    "colors": {
      "primary": {
        "50": { "value": "#eff6ff", "type": "color" },
        "500": { "value": "#3b82f6", "type": "color" },
        "900": { "value": "#1e3a8a", "type": "color" }
      },
      "gray": {
        "50": { "value": "#f9fafb", "type": "color" },
        "900": { "value": "#111827", "type": "color" }
      }
    },
    "spacing": {
      "xs": { "value": "4", "type": "spacing" },
      "sm": { "value": "8", "type": "spacing" },
      "md": { "value": "16", "type": "spacing" },
      "lg": { "value": "24", "type": "spacing" }
    },
    "borderRadius": {
      "sm": { "value": "4", "type": "borderRadius" },
      "md": { "value": "8", "type": "borderRadius" },
      "lg": { "value": "16", "type": "borderRadius" }
    },
    "fontFamilies": {
      "sans": { "value": "Inter", "type": "fontFamilies" },
      "mono": { "value": "JetBrains Mono", "type": "fontFamilies" }
    },
    "fontSizes": {
      "xs": { "value": "12", "type": "fontSizes" },
      "sm": { "value": "14", "type": "fontSizes" },
      "base": { "value": "16", "type": "fontSizes" },
      "lg": { "value": "18", "type": "fontSizes" }
    },
    "lineHeights": {
      "tight": { "value": "1.25", "type": "lineHeights" },
      "normal": { "value": "1.5", "type": "lineHeights" },
      "relaxed": { "value": "1.75", "type": "lineHeights" }
    }
  },
  "light": {
    "bg": {
      "default": { "value": "{global.colors.gray.50}", "type": "color" },
      "subtle": { "value": "{global.colors.gray.100}", "type": "color" }
    },
    "text": {
      "primary": { "value": "{global.colors.gray.900}", "type": "color" },
      "secondary": { "value": "{global.colors.gray.600}", "type": "color" }
    }
  },
  "dark": {
    "bg": {
      "default": { "value": "{global.colors.gray.900}", "type": "color" },
      "subtle": { "value": "{global.colors.gray.800}", "type": "color" }
    },
    "text": {
      "primary": { "value": "{global.colors.gray.50}", "type": "color" },
      "secondary": { "value": "{global.colors.gray.400}", "type": "color" }
    }
  }
}

Tokens Studio → Code Transform

Style Dictionary config (sd.config.js):

module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'dist/css/',
      files: [
        {
          destination: 'variables.css',
          format: 'css/variables',
          filter: (token) => token.filePath.includes('global'),
        },
        {
          destination: 'light.css',
          format: 'css/variables',
          filter: (token) => token.filePath.includes('light'),
          options: {
            selector: ':root, [data-theme="light"]',
          },
        },
        {
          destination: 'dark.css',
          format: 'css/variables',
          filter: (token) => token.filePath.includes('dark'),
          options: {
            selector: '[data-theme="dark"]',
          },
        },
      ],
    },
    tailwind: {
      transformGroup: 'js',
      buildPath: 'dist/',
      files: [
        {
          destination: 'tailwind.tokens.js',
          format: 'javascript/module',
        },
      ],
    },
  },
};

Figma Plugin Development

Plugin for Token Sync

manifest.json:

{
  "name": "Token Sync",
  "id": "token-sync-plugin",
  "api": "1.0.0",
  "main": "code.js",
  "ui": "ui.html",
  "editorType": ["figma"],
  "networkAccess": {
    "allowedDomains": ["api.github.com"]
  }
}

code.ts (plugin logic):

figma.showUI(__html__, { width: 400, height: 500 });

figma.ui.onmessage = async (msg) => {
  if (msg.type === 'import-tokens') {
    await importTokens(msg.tokens);
  } else if (msg.type === 'export-tokens') {
    const tokens = await exportTokens();
    figma.ui.postMessage({ type: 'tokens-exported', tokens });
  }
};

async function importTokens(tokens: Record<string, any>): Promise<void> {
  // Get or create collection
  const collections = await figma.variables.getLocalVariableCollectionsAsync();
  let collection = collections.find((c) => c.name === 'Design Tokens');

  if (!collection) {
    collection = figma.variables.createVariableCollection('Design Tokens');
  }

  const modeId = collection.modes[0].modeId;

  // Import each token
  for (const [path, token] of flattenTokens(tokens)) {
    const existing = await figma.variables.getVariableByIdAsync(
      `VariableID:${path.replace(/\//g, ':')}`
    );

    if (existing) {
      existing.setValueForMode(modeId, parseTokenValue(token));
    } else {
      const variable = figma.variables.createVariable(
        path,
        collection,
        getVariableType(token.type)
      );
      variable.setValueForMode(modeId, parseTokenValue(token));
    }
  }

  figma.notify(`Imported ${Object.keys(tokens).length} tokens`);
}

async function exportTokens(): Promise<Record<string, any>> {
  const tokens: Record<string, any> = {};
  const collections = await figma.variables.getLocalVariableCollectionsAsync();

  for (const collection of collections) {
    for (const variableId of collection.variableIds) {
      const variable = await figma.variables.getVariableByIdAsync(variableId);
      if (!variable) continue;

      const path = variable.name.split('/');
      let current = tokens;

      for (let i = 0; i < path.length - 1; i++) {
        current[path[i]] = current[path[i]] || {};
        current = current[path[i]];
      }

      const modeId = collection.modes[0].modeId;
      current[path[path.length - 1]] = {
        value: formatValue(variable.valuesByMode[modeId], variable.resolvedType),
        type: mapVariableType(variable.resolvedType),
      };
    }
  }

  return tokens;
}

function* flattenTokens(
  obj: Record<string, any>,
  path: string[] = []
): Generator<[string, any]> {
  for (const [key, value] of Object.entries(obj)) {
    if (value.value !== undefined) {
      yield [path.concat(key).join('/'), value];
    } else {
      yield* flattenTokens(value, path.concat(key));
    }
  }
}

function getVariableType(type: string): VariableResolvedDataType {
  switch (type) {
    case 'color':
      return 'COLOR';
    case 'spacing':
    case 'borderRadius':
    case 'fontSizes':
      return 'FLOAT';
    default:
      return 'STRING';
  }
}

function parseTokenValue(token: { value: string; type: string }): VariableValue {
  if (token.type === 'color') {
    return hexToRgba(token.value);
  }
  if (['spacing', 'borderRadius', 'fontSizes'].includes(token.type)) {
    return parseFloat(token.value);
  }
  return token.value;
}

function hexToRgba(hex: string): RGBA {
  const r = parseInt(hex.slice(1, 3), 16) / 255;
  const g = parseInt(hex.slice(3, 5), 16) / 255;
  const b = parseInt(hex.slice(5, 7), 16) / 255;
  return { r, g, b, a: 1 };
}

Naming Conventions

CategoryFigma ConventionCode Convention
Colorscolor/primary/500--color-primary-500
Spacingspacing/md--spacing-md
Radiusradius/lg--radius-lg
Typographytext/body/regular--text-body
Shadowsshadow/md--shadow-md
Semanticbg/default--color-bg-default

Workflow Best Practices

  1. Single source of truth: Decide if code or Figma owns tokens
  2. Version control: Store token files in Git
  3. CI/CD integration: Auto-sync on merge to main
  4. Semantic tokens: Use aliases for theme-aware values
  5. Documentation: Keep a mapping document for designers
  6. Validation: Lint tokens before sync

Common Pitfalls

IssueSolution
Color format mismatchNormalize to hex or RGB before sync
Spacing unitsUse unitless in Figma, add px/rem in code
Missing tokensValidate token coverage before sync
Mode driftSync all modes together, not separately
Name collisionsEstablish naming convention upfront

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.