figma-design-md-plugin

Figma plugin that extracts local styles and generates DESIGN.md and SKILL.md files for AI-driven design system implementation

Skill file

Preview skill file
---
name: figma-design-md-plugin
description: Figma plugin that extracts local styles and generates DESIGN.md and SKILL.md files for AI-driven design system implementation
triggers:
  - "extract design tokens from Figma"
  - "generate DESIGN.md from Figma file"
  - "create design system documentation from Figma"
  - "build SKILL.md for AI coding agents"
  - "export Figma styles to markdown"
  - "convert Figma variables to design tokens"
  - "setup TypeUI design guidelines"
  - "auto-generate design system blueprint"
---

# Figma Design.md Plugin Skill

> Skill by [ara.so](https://ara.so) — Design Skills collection.

## Overview

The **design-md-figma** plugin extracts local styles, variables, and component families from Figma files and generates standardized `DESIGN.md` and `SKILL.md` files. These outputs provide AI coding agents with design-system blueprints for building consistent interfaces.

The plugin reads:
- Local color/typography/spacing/radius/motion styles
- Figma variable collections
- Effect styles (shadows, blur)
- Grid styles
- Component families

It generates TypeUI-format markdown that AI agents can use to implement design-consistent code.

## Installation & Setup

### Development Setup

```bash
# Clone the repository
git clone https://github.com/bergside/design-md-figma.git
cd design-md-figma

# Install dependencies
npm install

# Build the plugin
npm run build
```

### Load in Figma Desktop

1. Open Figma Desktop
2. Navigate to **Plugins → Development → Import plugin from manifest...**
3. Select the `manifest.json` file from your cloned directory
4. The plugin appears in **Plugins → Development → Design MD Skill Generator**

### Watch Mode for Development

```bash
# Auto-rebuild on file changes
npm run watch
```

## Plugin Architecture

### File Structure

```
design-md-figma/
├── src/
│   ├── code.ts          # Main plugin logic (runs in Figma sandbox)
│   ├── ui.html          # Plugin UI (runs in iframe)
│   └── types.ts         # TypeScript type definitions
├── manifest.json        # Figma plugin configuration
└── package.json
```

### Key TypeScript Interfaces

```typescript
interface ExtractedData {
  source: {
    fileName: string;
    pageNames: string[];
    timestamp: string;
  };
  variableCollections: VariableCollection[];
  colorTokens: ColorToken[];
  typographyTokens: TypographyToken[];
  spacingTokens: SpacingToken[];
  radiusTokens: RadiusToken[];
  effectStyles: EffectStyle[];
  gridStyles: GridStyle[];
  componentFamilies: string[];
}

interface ColorToken {
  name: string;
  value: string; // hex, rgb(), or variable reference
  type: 'paint-style' | 'color-variable';
}

interface TypographyToken {
  name: string;
  fontFamily: string;
  fontSize: number;
  fontWeight: number;
  lineHeight: string;
  letterSpacing: string;
}
```

## Core Plugin Functions

### Extracting Design Tokens

The plugin automatically extracts tokens when run:

```typescript
// Main extraction flow (src/code.ts)
async function extractDesignTokens(): Promise<ExtractedData> {
  const data: ExtractedData = {
    source: {
      fileName: figma.root.name,
      pageNames: figma.root.children.map(page => page.name),
      timestamp: new Date().toISOString()
    },
    variableCollections: [],
    colorTokens: [],
    typographyTokens: [],
    spacingTokens: [],
    radiusTokens: [],
    effectStyles: [],
    gridStyles: [],
    componentFamilies: []
  };

  // Extract local paint styles
  const paintStyles = figma.getLocalPaintStyles();
  paintStyles.forEach(style => {
    const paint = style.paints[0];
    if (paint.type === 'SOLID') {
      data.colorTokens.push({
        name: style.name,
        value: rgbToHex(paint.color),
        type: 'paint-style'
      });
    }
  });

  // Extract local text styles
  const textStyles = figma.getLocalTextStyles();
  textStyles.forEach(style => {
    data.typographyTokens.push({
      name: style.name,
      fontFamily: style.fontName.family,
      fontSize: style.fontSize,
      fontWeight: style.fontName.style,
      lineHeight: formatLineHeight(style.lineHeight),
      letterSpacing: formatLetterSpacing(style.letterSpacing)
    });
  });

  // Extract variable collections
  const collections = figma.variables.getLocalVariableCollections();
  collections.forEach(collection => {
    data.variableCollections.push({
      id: collection.id,
      name: collection.name,
      modes: collection.modes.map(m => m.name)
    });
  });

  return data;
}
```

### Generating DESIGN.md

```typescript
function generateDesignMD(data: ExtractedData): string {
  let markdown = `# DESIGN.md\n\n`;
  
  markdown += `## Source\n\n`;
  markdown += `- **File**: ${data.source.fileName}\n`;
  markdown += `- **Pages**: ${data.source.pageNames.join(', ')}\n`;
  markdown += `- **Extracted**: ${data.source.timestamp}\n\n`;

  markdown += `## Color Tokens\n\n`;
  markdown += `| Name | Value | Type |\n`;
  markdown += `| --- | --- | --- |\n`;
  data.colorTokens.forEach(token => {
    markdown += `| ${token.name} | ${token.value} | ${token.type} |\n`;
  });
  markdown += `\n`;

  markdown += `## Typography Tokens\n\n`;
  markdown += `| Name | Family | Size | Weight | Line Height | Letter Spacing |\n`;
  markdown += `| --- | --- | --- | --- | --- | --- |\n`;
  data.typographyTokens.forEach(token => {
    markdown += `| ${token.name} | ${token.fontFamily} | ${token.fontSize}px | ${token.fontWeight} | ${token.lineHeight} | ${token.letterSpacing} |\n`;
  });

  return markdown;
}
```

### Generating SKILL.md

```typescript
function generateSkillMD(data: ExtractedData): string {
  let markdown = `# SKILL.md\n\n`;
  
  markdown += `## Mission\n\n`;
  markdown += `Build interfaces that strictly adhere to the design system extracted from **${data.source.fileName}**.\n\n`;

  markdown += `## Style Foundations\n\n`;
  markdown += `### Colors\n\n`;
  data.colorTokens.forEach(token => {
    markdown += `- **${token.name}**: \`${token.value}\`\n`;
  });
  markdown += `\n`;

  markdown += `### Typography\n\n`;
  data.typographyTokens.forEach(token => {
    markdown += `- **${token.name}**: ${token.fontFamily} ${token.fontSize}px/${token.lineHeight}, weight ${token.fontWeight}\n`;
  });

  markdown += `\n## Rules: Do\n\n`;
  markdown += `- Use extracted color tokens exactly as defined\n`;
  markdown += `- Apply typography scales from text styles\n`;
  markdown += `- Reference component families for consistency\n`;
  markdown += `- Follow spacing/radius token values\n\n`;

  markdown += `## Rules: Don't\n\n`;
  markdown += `- Never hardcode colors outside extracted palette\n`;
  markdown += `- Don't create arbitrary font sizes\n`;
  markdown += `- Avoid inconsistent spacing values\n`;

  return markdown;
}
```

## Message Passing (UI ↔ Plugin)

The plugin uses Figma's message API:

```typescript
// In code.ts (plugin sandbox)
figma.ui.onmessage = async (msg) => {
  if (msg.type === 'extract') {
    const data = await extractDesignTokens();
    figma.ui.postMessage({
      type: 'extraction-complete',
      data
    });
  }
  
  if (msg.type === 'generate-design-md') {
    const markdown = generateDesignMD(msg.data);
    figma.ui.postMessage({
      type: 'markdown-ready',
      markdown,
      fileType: 'DESIGN.md'
    });
  }
};

// In ui.html (iframe)
window.onmessage = (event) => {
  const msg = event.data.pluginMessage;
  if (msg.type === 'extraction-complete') {
    displayExtractedData(msg.data);
  }
  if (msg.type === 'markdown-ready') {
    updateEditor(msg.markdown);
    enableDownload(msg.markdown, msg.fileType);
  }
};
```

## Common Usage Patterns

### Extract and Download DESIGN.md

```typescript
// User opens plugin in Figma
// Plugin auto-extracts on load
figma.showUI(__html__, { width: 600, height: 800 });

const data = await extractDesignTokens();
figma.ui.postMessage({ type: 'extraction-complete', data });

// User clicks "Generate DESIGN.md"
// UI sends message to plugin
parent.postMessage({
  pluginMessage: { type: 'generate-design-md', data }
}, '*');

// Plugin generates markdown and sends back
const markdown = generateDesignMD(data);
figma.ui.postMessage({ 
  type: 'markdown-ready', 
  markdown, 
  fileType: 'DESIGN.md' 
});

// UI enables download button
function downloadFile(content: string, filename: string) {
  const blob = new Blob([content], { type: 'text/markdown' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
}
```

### Refresh Token Extraction

```typescript
// User modifies Figma file (adds new color style)
// User clicks "Refresh" in plugin UI
parent.postMessage({
  pluginMessage: { type: 'refresh' }
}, '*');

// Plugin re-runs extraction
const updatedData = await extractDesignTokens();
figma.ui.postMessage({ 
  type: 'extraction-complete', 
  data: updatedData 
});
```

### Toggle Between DESIGN.md and SKILL.md

```typescript
// UI state management
let currentView: 'design' | 'skill' = 'design';

function toggleView() {
  currentView = currentView === 'design' ? 'skill' : 'design';
  
  parent.postMessage({
    pluginMessage: { 
      type: currentView === 'design' ? 'generate-design-md' : 'generate-skill-md',
      data: extractedData
    }
  }, '*');
}
```

## Configuration

### manifest.json

```json
{
  "name": "Design MD Skill Generator",
  "id": "1612814320994608244",
  "api": "1.0.0",
  "main": "dist/code.js",
  "ui": "dist/ui.html",
  "editorType": ["figma"],
  "networkAccess": {
    "allowedDomains": ["none"]
  }
}
```

### TypeScript Configuration

```json
{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "lib": ["ES2015"],
    "outDir": "./dist",
    "strict": true,
    "typeRoots": ["./node_modules/@types", "./node_modules/@figma"]
  }
}
```

## Helper Utilities

### Color Conversion

```typescript
function rgbToHex(color: RGB): string {
  const r = Math.round(color.r * 255).toString(16).padStart(2, '0');
  const g = Math.round(color.g * 255).toString(16).padStart(2, '0');
  const b = Math.round(color.b * 255).toString(16).padStart(2, '0');
  return `#${r}${g}${b}`;
}

function rgbaToString(color: RGBA): string {
  const r = Math.round(color.r * 255);
  const g = Math.round(color.g * 255);
  const b = Math.round(color.b * 255);
  return `rgba(${r}, ${g}, ${b}, ${color.a})`;
}
```

### Typography Formatting

```typescript
function formatLineHeight(lineHeight: LineHeight): string {
  if (lineHeight.unit === 'PIXELS') {
    return `${lineHeight.value}px`;
  }
  if (lineHeight.unit === 'PERCENT') {
    return `${lineHeight.value}%`;
  }
  return 'auto';
}

function formatLetterSpacing(spacing: LetterSpacing): string {
  if (spacing.unit === 'PIXELS') {
    return `${spacing.value}px`;
  }
  if (spacing.unit === 'PERCENT') {
    return `${spacing.value}%`;
  }
  return 'normal';
}
```

### Component Family Detection

```typescript
function extractComponentFamilies(): string[] {
  const families = new Set<string>();
  
  const components = figma.root.findAll(node => 
    node.type === 'COMPONENT_SET'
  ) as ComponentSetNode[];
  
  components.forEach(componentSet => {
    // Component set name format: "Button"
    families.add(componentSet.name);
  });
  
  return Array.from(families).sort();
}
```

## Troubleshooting

### Plugin Won't Load

**Issue**: Plugin doesn't appear in Figma menu

**Solution**:
```bash
# Ensure build completed successfully
npm run build

# Check dist/ folder exists with code.js and ui.html
ls -la dist/

# Verify manifest.json points to correct paths
cat manifest.json
```

### No Styles Extracted

**Issue**: Generated markdown shows empty token lists

**Solution**:
- Ensure file has **local styles** (not from external libraries)
- Check Figma file has published color/text styles
- Run **Refresh** after adding new styles

```typescript
// Debug: Log style counts
console.log('Paint styles:', figma.getLocalPaintStyles().length);
console.log('Text styles:', figma.getLocalTextStyles().length);
console.log('Effect styles:', figma.getLocalEffectStyles().length);
```

### Variable Collections Not Found

**Issue**: Variable collections section empty in DESIGN.md

**Solution**:
```typescript
// Ensure using Figma API 1.0.0+ (supports variables)
const collections = figma.variables.getLocalVariableCollections();
if (collections.length === 0) {
  console.warn('No local variable collections found');
}

// Check file actually has variables defined
// Variables → View local variables in Figma
```

### TypeScript Errors

**Issue**: Build fails with type errors

**Solution**:
```bash
# Install Figma type definitions
npm install --save-dev @figma/plugin-typings

# Run type check separately
npm run typecheck

# Common fix: ensure tsconfig includes Figma types
# tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./node_modules/@figma"]
  }
}
```

### Download Not Working

**Issue**: Download button doesn't trigger file save

**Solution**:
```typescript
// Ensure blob creation and download logic is correct
function downloadMarkdown(content: string, filename: string) {
  const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  document.body.appendChild(link); // Required for Firefox
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url); // Clean up
}
```

## Integration with AI Agents

### Using Generated DESIGN.md with Claude Code

```bash
# Add to project root
cp ~/Downloads/DESIGN.md ./DESIGN.md

# Reference in .claude/instructions
echo "Follow design tokens and guidelines in DESIGN.md" >> .claude/instructions
```

### Using SKILL.md with Cursor

```bash
# Add to .cursor/ directory
mkdir -p .cursor
cp ~/Downloads/SKILL.md .cursor/design-system.md

# Cursor automatically reads skills from .cursor/
```

### Programmatic Usage

```typescript
import fs from 'fs';

// Read generated DESIGN.md
const designMD = fs.readFileSync('./DESIGN.md', 'utf-8');

// Parse color tokens (example)
const colorSection = designMD.match(/## Color Tokens\n\n([\s\S]*?)\n\n##/);
if (colorSection) {
  const rows = colorSection[1].split('\n').slice(2); // Skip header
  const colors = rows.map(row => {
    const [name, value, type] = row.split('|').map(s => s.trim());
    return { name, value, type };
  });
  console.log(colors);
}
```

## Best Practices

1. **Run extraction on design-complete files** — Extract after design system is stable
2. **Use local styles, not library styles** — Plugin only reads local file styles
3. **Name styles consistently** — Use hierarchical naming: `color/primary/500`, `text/heading/h1`
4. **Leverage variable collections** — Map design tokens to Figma variables for better extraction
5. **Refresh after changes** — Re-run extraction when styles/variables updated
6. **Commit generated files to repo** — Keep DESIGN.md and SKILL.md in version control alongside code

## TypeUI Format Compliance

Generated files follow [TypeUI DESIGN.md specification](https://www.typeui.sh/design-md):

- Structured sections (Source, Tokens, Styles, Components)
- Markdown tables for token lists
- ISO 8601 timestamps
- Hex color format for paint styles
- Pixel-based sizing units
- Component family enumeration

## Resources

- **Plugin homepage**: https://www.figma.com/community/plugin/1612814320994608244/design-md-skills
- **TypeUI DESIGN.md format**: https://www.typeui.sh/design-md
- **Curated design skills**: https://www.typeui.sh/design-skills
- **Figma Plugin API**: https://www.figma.com/plugin-docs/api/api-overview/

Source

Creator's repository · aradotso/design-skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk