agentsclimarketplace

Design extractor

Skill PatNoO/design-extractor/.claude/skills/design-extractor

An AI Agent Skill for Claude Code that automatically extracts design systems from codebases. Generates a single, ready-to-run Figma script that reconstructs tokens, components, and frames with 1:1 accuracy.

Install
npx -y skills add PatNoO/design-extractor --skill design-extractor

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Extracts the ENTIRE design system from an existing codebase (repo) and exports it to a single runnable Figma plugin script that automatically creates tokens, components, AND frames. Always use this skill when the user wants to: move their design from code to Figma/Pencil, extract design tokens from a repo, sync a codebase design to a design tool, generate a design system from existing code, reverse-engineer their app design, export colors/typography/spacing/components/pages from their app, or get their entire app design into Figma automatically. Triggers on phrases like: "design to figma", "extract design", "design tokens", "code to figma", "export design", "design system from repo", "reverse design", "get my design into figma", "components to figma", "frames to figma", "whole design to figma".

SKILL.md

20.3 KB, as published. Nobody here has run it

Design Extractor Skill

Takes the entire design from a repo and generates a single JS script that the user pastes into the Figma Console. The script automatically creates tokens, components and frames.

First time on a new project? Run setup-extractor.md (in this folder) to calibrate the skill for your framework and styling approach before generating the script.

End result for the user

1. Claude Code runs the skill against the repo
2. Generates a single figma-import.js script
3. User opens Figma
4. Menu → Plugins → Development → Open Console
5. Paste the script → Enter
6. The entire design is created automatically in Figma

One paste. One Enter. Done.


What the skill does

Given a repo, you will:

  1. Analyze the codebase and identify all design-related information
  2. Extract tokens, components and page layouts
  3. Generate a single combined Figma plugin script
  4. Deliver a ready-to-run script with instructions

Step 1: Identify framework and design approach

Look at the file structure and determine:

Signal in codeFramework
tailwind.config.js/tsTailwind CSS
*.module.css, styles/CSS Modules
styled-components, emotionCSS-in-JS
tokens.js, theme.js, design-tokens.*Explicit tokens
*.swift, Color+Extensions.swiftSwiftUI
styles.ts with export objectReact Native
variables.css, :root { --CSS Custom Properties
res/values/colors.xml, res/values/dimens.xmlKotlin/Android (XML Views)
*.kt + res/layout/activity_*.xmlKotlin/Android (XML Views)

Always read these files if they exist:

  • tailwind.config.*
  • tokens.* / theme.* / design-tokens.*
  • styles/globals.css or app/globals.css
  • src/styles/variables.*
  • Any files named colors, typography, spacing
  • res/values/colors.xml, res/values/dimens.xml, res/values/styles.xml (Android)

Step 2: Extract design tokens

Priority order (highest → lowest):

  1. Explicit token file (theme.js, tokens.json) → read directly
  2. Tailwind config → extract colors, fontFamily, fontSize, spacing, borderRadius
  3. CSS Custom Properties (:root { --color-primary: ... }) → parse variables
  4. Android XML resources → parse res/values/colors.xml, dimens.xml, styles.xml
  5. CSS-in-JS theme object → extract theme structure
  6. Hardcoded values in components → collect unique values

Android XML token extraction

When res/values/colors.xml is present:

  • Parse every <color name="..."> tag → token name + hex value
  • Token name: strip color_ prefix, camelCase the rest (e.g. color_text_primarytextPrimary)

When res/values/dimens.xml is present:

  • spacing_* → spacing tokens (strip dp suffix, treat as px)
  • radius_* → border radius tokens
  • font_size_* → typography size tokens (strip sp suffix, treat as px)

When res/values/styles.xml is present:

  • Resolve TextAppearance.* styles: read android:textSize and android:fontFamily / weight
  • Font weight: sans-serif = 400, sans-serif-medium = 500, sans-serif-bold = 700
  • Resolve @dimen/ and @color/ references inside style items using the values already extracted

What to look for:

COLORS:
- Primary, secondary, accent, background, surface, text, border, error, success, warning

TYPOGRAPHY:
- fontFamily (all unique fonts)
- fontSize (all sizes with semantic names if possible: xs, sm, base, lg, xl, 2xl...)
- fontWeight, lineHeight, letterSpacing

SPACING:
- All spacing values, mapped to a scale (4, 8, 12, 16, 24, 32, 48, 64...)

BORDER:
- borderRadius (none, sm, md, lg, full), borderWidth

SHADOWS:
- All box-shadow values

BREAKPOINTS:
- sm, md, lg, xl, 2xl (or equivalent)

Step 3: Extract components

Scan component files (.tsx, .jsx, .vue, .swift, .html, res/layout/*.xml) and document:

For each component: name, variants, props that affect appearance, which tokens it uses.

Android XML component patterns

When scanning res/layout/component_*.xml:

XML elementFigma component
MaterialButtonButton — read android:backgroundTint (fill), app:cornerRadius (radius), android:textColor (label color)
MaterialCardViewCard — read app:cardBackgroundColor, app:cardCornerRadius, app:strokeColor
TextViewText node — resolve android:textAppearance via styles.xml for size + weight
MaterialToolbarNav bar — read android:background, android:height

Resolve @color/name and @dimen/name references using the extracted token maps before setting values. For android:backgroundTint="@color/color_primary" → use tokens.colors.primary.

Button variants: if only one layout file exists, infer secondary (transparent + stroke) and ghost (transparent, no stroke) from Material Design convention — note this as inferred in design-system-summary.md.

Read the reference file for full guide: → references/component-patterns.md


Step 4: Extract frames (pages/views)

Identify all routes and analyze their layouts.

  • Next.js App Router: scan app/**/page.tsx
  • Next.js Pages: scan pages/**/*.tsx
  • HTML: scan all .html files
  • SwiftUI: scan all *View.swift files
  • React Router: read App.tsx / router.tsx
  • Kotlin/Android: scan *Activity.kt files — each Activity = one screen. Read its paired res/layout/activity_*.xml for layout structure. Mobile frame size: 390×844px.

Read the reference file for full guide: → references/frame-generator.md


Step 5: Generate the combined script

ALWAYS generate a single combined script — not three separate ones.

Structure for figma-import.js:

// === DESIGN IMPORT – [Project name] ===
// Generated by Claude Code [date]
// Run in: Figma → Plugins → Development → Open Console

const tokens = { colors: {...}, typography: {...}, spacing: {...} };
function solidColor(hex) { ... }

async function loadFonts() {
  // Collect every unique family+style from tokens.typography, then load them all
  const toLoad = new Set();
  for (const t of Object.values(tokens.typography)) {
    toLoad.add(JSON.stringify({ family: t.family, style: weightToStyle(t.weight) }));
  }
  // Always include fallback weights so helper text nodes never fail
  for (const family of [...new Set(Object.values(tokens.typography).map(t => t.family))]) {
    for (const style of ["Regular", "Medium", "Semi Bold", "Bold"]) {
      toLoad.add(JSON.stringify({ family, style }));
    }
  }
  for (const entry of toLoad) {
    try { await figma.loadFontAsync(JSON.parse(entry)); } catch {}
  }
}

async function createTokenStyles() { ... }

const components = [ ... ];
async function createComponents() { ... }

const pages = [ ... ];
async function buildFrames() { ... }

// Top-level await — NO async IIFE (crashes Figma console before fonts load)
try {
  console.log("⏳ Starting design import...");
  await loadFonts();
  await createTokenStyles();
  await createComponents();
  await buildFrames();
  console.log("🎉 SUCCESS: Design imported!");
} catch (err) {
  console.error("❌ ERROR:", err);
}

See full templates in: → references/figma-plugin-template.mdreferences/frame-generator.md


Step 6: Deliver

Always deliver:

  1. figma-import.js — the combined script, ready to run
  2. design-system-summary.md — what was found and any gaps
  3. Three-line instructions on how to run it in Figma

Step 7: Self-validate before delivering

After generating figma-import.js, run the validator:

bash .claude/skills/design-extractor/scripts/validate.sh figma-import.js

If any checks fail: fix the script, re-run the validator. Do not deliver until exit code is 0.

Use the checklist below as a fix guide when the validator reports a failure:

Rule violations (instant fail — fix before delivering)

  • Rule 1 – Page count: Does the script create exactly 2 pages (🧩 Components and 📐 Frames)? Search output for figma.createPage(). Count must be exactly 2.
  • Rule 2 – No placeholders: Does every fills value come from tokens.colors.*? Search for hardcoded hex strings not assigned to a token first.
  • Rule 3 – Token connections: Does every frame section reference tokens.colors.* rather than a literal hex?
  • Rule 4 – Mobile + desktop frames: For each entry in pages[], is there both a 390px mobile and a 1440px desktop frame?
  • Rule 5 – Font mapping: Does the script contain any of: "SF Pro", "-apple-system", "BlinkMacSystemFont", "system-ui", "system"? If yes, replace with "Inter".
  • Rule 6 – lineHeight unit: Search for unit: "MULTIPLIER". Must return zero matches. All lineHeight values must use "PIXELS", "PERCENT", or "AUTO".
  • Rule 7 – Top-level await: Search for (async () => { or (async() => {. Must return zero matches.
  • Rule 8 – setCurrentPageAsync: Search for figma.currentPage = (assignment, not .name =). Must return zero matches.
  • Rule 9 – No figma.notify / figma.closePlugin: Search for figma.notify( and figma.closePlugin(. Must both return zero matches.
  • Rule 10 – String quoting: Any string value sourced from extracted repo data (page titles, component names, button text) uses single-quote outer delimiter.

Structure checks

  • runPhase helper is present and all four phases use it
  • Each component loop and each frame loop has its own per-item try-catch
  • Progress console.log messages present at start of each phase ([1/4], [2/4], [3/4], [4/4])

If any check fails: fix the generated script, then re-run the checklist from the top. Only deliver once all boxes can be checked.


Framework compatibility

FrameworkTokensComponentsFrames
Next.js (Tailwind)✅ Automatic✅ Automatic✅ Automatic
React + CSS Modules✅ Good✅ Good✅ Good
HTML/CSS✅ CSS vars✅ Manual scan✅ Per .html file
SwiftUI⚠️ Color extensions✅ Views✅ Scenes
Vue✅ Automatic✅ Automatic✅ Automatic
Kotlin/Android (XML Views)✅ colors.xml + dimens.xml✅ Layout XML✅ Per Activity
C++ / Qt⚠️ Manual⚠️ .ui files⚠️ Widget tree

Claude Code adapts the extraction per framework automatically.


Key principles

  • Never destructive — read only, never modify the repo
  • Assume gracefully — if unsure of a semantic name, use the technical value
  • Flag gaps — clearly state what could not be extracted automatically
  • Keep it portable — output must work without knowing the specific repo

CRITICAL RULES — Always follow these

These are hard rules learned from real usage. Never violate them.

Rule 1: NEVER create multiple Figma pages

Figma's free plan only allows 3 pages. Creating one page per route will immediately hit this limit.

ALWAYS put everything on maximum 2 pages:

Page 1: "🧩 Components"  → all components side by side
Page 2: "📐 Frames"      → ALL page frames side by side on one canvas

Frames go next to each other horizontally with 80px gap — never on separate Figma pages.

// WRONG ❌ — creates a new Figma page per route
const figmaPage = figma.createPage();

// CORRECT ✅ — all frames on one page, positioned with xOffset
let xOffset = 0;
for (const pageData of pages) {
  const frame = figma.createFrame();
  frame.x = xOffset;
  frame.y = 0;
  xOffset += (frame.width || 390) + 80;
  figma.currentPage.appendChild(frame);
}

Rule 2: NEVER use placeholder boxes for frame content

Frames must use actual colors, spacing and structure from the extracted design — not labeled placeholder rectangles.

// WRONG ❌
const box = figma.createFrame();
box.fills = [solidColor("#EFF6FF")]; // generic placeholder color
// adds text "Search Bar" as a label

// CORRECT ✅
const heroSection = figma.createFrame();
heroSection.fills = [solidColor(tokens.colors["primary"])]; // actual brand color
heroSection.layoutMode = "VERTICAL";
heroSection.paddingLeft = heroSection.paddingRight = 24;
heroSection.paddingTop = heroSection.paddingBottom = 32;
heroSection.itemSpacing = 16;
// real text nodes with actual typography tokens

Rule 3: Always connect frames to extracted tokens

Every color, font and spacing value in a frame must come from the extracted tokens — never use hardcoded defaults like #3B82F6 if the repo has a different primary color.

Rule 4: Generate mobile-first frames

For each route generate:

  • Mobile frame (390px) — always first, primary for app projects
  • Desktop frame (1440px) — placed 80px to the right of mobile

Rule 5: Map system fonts to Figma-loadable fonts

Figma cannot load OS-level system fonts. Any system font reference found in the repo must be mapped to the nearest Figma-available equivalent before generating the script.

Source fontMap to
SF Pro, SF Pro Display, SF Pro Text"Inter"
-apple-system, BlinkMacSystemFont"Inter"
.font(.system(...)) (SwiftUI)"Inter"
Roboto"Roboto" (available in Figma)
system-ui"Inter"
Android: sans-serif / Roboto"Roboto"

Add a comment in the generated script noting the mapping so users know why the font differs from their app:

// Note: SF Pro (iOS system font) mapped to Inter — Figma cannot load device fonts

Rule 6: lineHeight must use PIXELS, PERCENT or AUTO — never MULTIPLIER

Figma does not accept MULTIPLIER as a lineHeight unit. Always use one of the three valid formats:

// WRONG ❌
style.lineHeight = { unit: "MULTIPLIER", value: 1.5 };

// CORRECT ✅ — option 1: pixels
style.lineHeight = { unit: "PIXELS", value: 24 };

// CORRECT ✅ — option 2: percent
style.lineHeight = { unit: "PERCENT", value: 150 };

// CORRECT ✅ — option 3: auto
style.lineHeight = { unit: "AUTO" };

If the repo defines lineHeight as a multiplier (e.g. 1.5), convert to percent by multiplying by 100:

// lineHeight: 1.5 → { unit: "PERCENT", value: 150 }
const lineHeightPercent = multiplierValue * 100;
style.lineHeight = { unit: "PERCENT", value: lineHeightPercent };

⚙️ Figma Script Generation Rules for Developer Console

When generating JavaScript meant to run directly in the Figma Developer Console, follow these rules to prevent runtime crashes and deprecation warnings.

Rule 6: NEVER use async IIFEs — use top-level await

The Figma console supports top-level await natively. Wrapping code in (async () => { ... })() causes the plugin environment to close before async operations finish, crashing the script.

// WRONG ❌
(async () => {
  await loadFonts();
  await createComponents();
})();

// CORRECT ✅
try {
  await loadFonts();
  await createComponents();
  console.log("✅ Done");
} catch (err) {
  console.error("❌ ERROR:", err);
}

Rule 7: NEVER use figma.currentPage = — use setCurrentPageAsync()

The synchronous setter is deprecated and generates console warnings. Always use the async version:

// WRONG ❌
figma.currentPage = page;

// CORRECT ✅
await figma.setCurrentPageAsync(page);

Rule 8: Use console.log / console.error — NOT figma.notify() or figma.closePlugin()

figma.notify() relies on async timers that can crash when the console environment shuts down. figma.closePlugin() terminates the environment mid-run when called from the console.

This applies at EVERY point in the script — including the very last line. Do not add figma.notify() after the try/catch block as a "success toast".

// WRONG ❌ — anywhere in the script, including the final line
figma.notify("🎉 Done!", { timeout: 5000 });
figma.closePlugin();

// CORRECT ✅
console.log("🎉 SUCCESS: Design imported!");

Rule 9: Use single-quoted outer delimiters when string content may contain double quotes

If generated string content (labels, titles, names from the repo) could contain double-quote characters, use single quotes as the outer delimiter. A double quote inside a double-quoted JS string will break the parser with "missing ) after argument list".

// WRONG ❌ — breaks if the string contains "
t.characters = "Which actor plays the main character in "The Dark Knight"?";

// CORRECT ✅ — safe regardless of content
t.characters = 'Which actor plays the main character in "The Dark Knight"?';

Apply this whenever the string value comes from extracted repo data — component names, page titles, label text, anything that wasn't written by hand.


Correct main block template

Every generated script must follow this structure:

// === DESIGN IMPORT – [Project name] ===
// Generated by Claude Code [date]
// Run in: Figma → Plugins → Development → Open Console

const tokens = { colors: {...}, typography: {...}, spacing: {...} };

async function loadFonts() { ... }
async function createTokenStyles() { ... }
async function createComponents() { ... }
async function buildFrames() { ... }

// Top-level await — no IIFE wrapper
try {
  console.log("⏳ Starting design import...");
  await loadFonts();
  await createTokenStyles();
  await createComponents();
  await buildFrames();
  console.log("🎉 SUCCESS: Design imported!");
} catch (err) {
  console.error("❌ ERROR:", err);
}

Local Project Formatting

<!-- This section is auto-generated by setup-extractor.md for each project. Run `setup-extractor.md` to populate this with your project's specific rules. DO NOT manually edit — re-run the audit workflow to regenerate. -->

Not yet calibrated for this project.

To calibrate this skill for your specific framework and styling approach, run the setup workflow:

Open: .claude/skills/design-extractor/setup-extractor.md
Follow the 5-step audit workflow.
This section will be replaced with your project's rules.

What will be detected and added here

  • Framework (Next.js, Vue, Svelte, React, SwiftUI, etc.)
  • Styling approach (Tailwind, CSS Modules, Styled Components, CSS vars, etc.)
  • Component folder structure (where your components live)
  • Code generation rules (naming conventions, file format, output structure)

SwiftUI Project Notes (learned from SceneIt)

When extracting from a SwiftUI project:

  • Colors: Read Color+Extensions.swift or Assets.xcassets color set JSON files. Dark-mode variants are usually the primary appearance.
  • Flat token object: Use a flat const colors = { key: "#hex" } rather than a nested tokens.colors object — the script helper functions (sc, hGrad, vGrad) reference colors directly.
  • Typography: SwiftUI .font(.system(size:weight:)) maps to Inter in Figma. Extract all unique sizes and weights into a semantic token table.
  • Spacing constants: Look for enum Spacing or struct Spacing with static let values.
  • Corner radius: Look for enum CornerRadius or extension on CGFloat.
  • Views → Frames: Each *View.swift file is one frame. Map ZStack/VStack/HStack to Figma frame layout direction.
  • Custom shapes (Shape protocol): Cannot be reproduced exactly — approximate with nearest available Figma shape and note the limitation in design-system-summary.md.
  • Gradient helpers: Always generate hGrad(c1, c2) and vGrad(c1, c2) helpers in the script when the app uses gradients.
  • txt() helper: Always generate an async txt(chars, size, weight, hex, alpha) helper to avoid repeating font/fill setup on every text node.

Gives 0 of the 12 instructions most design systems skills give

Counted across 528 of the 534 authors here whose files we hold, read 2026-08-06

  • create a custom theme if neededin 54 of 528, across 10 files
  • read the corresponding theme filein 54 of 528, across 10 files
  • ask which theme to applyin 53 of 528, across 9 files
  • show the theme showcasein 53 of 528, across 9 files
  • maintain visual identity across all slidesin 50 of 528, across 6 files
  • apply the specified colors and fontsin 47 of 528, across 3 files
  • get explicit confirmationin 45 of 528, across 1 file
  • Generate a design system before codingin 19 of 528, across 6 files
  • Maintain at least 4.5:1 color contrast ratioin 19 of 528, across 8 files
  • Describe component shapes, colors, shadows, and interaction statesin 18 of 528, across 4 files
  • Check Python installation and install if missingin 17 of 528, across 4 files
  • Default to html-tailwind if stack is unspecifiedin 17 of 528, across 4 files

Said here and by no other author read

  • extract design tokens, components, and page layouts
  • generate a single combined Figma plugin script
  • delivered a ready-to-run script with instructions
  • create exactly two Figma pages
  • use actual extracted colors, spacing, and structure in frames
  • replace system fonts with Inter in the script

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.