figma-design-md-plugin
Figma plugin that extracts local styles and generates DESIGN.md and SKILL.md files for AI-driven design system implementation
How do I install this agent skill?
npx skills add https://github.com/reason-machines/design-skills --skill figma-design-md-pluginIs this agent skill safe to install?
- Gen Agent Trust Hubwarn
The skill instructs the agent to clone and install a Figma plugin from an external repository, which involves running code from an unverified source. Additionally, the plugin's data extraction logic is vulnerable to indirect prompt injection because it does not sanitize style or variable names taken from Figma files before including them in generated AI instructions.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Figma Design.md Plugin Skill
Skill by 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
# 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
- Open Figma Desktop
- Navigate to Plugins → Development → Import plugin from manifest...
- Select the
manifest.jsonfile from your cloned directory - The plugin appears in Plugins → Development → Design MD Skill Generator
Watch Mode for Development
# 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
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:
// 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
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
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:
// 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
// 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
// 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
// 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
{
"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
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"lib": ["ES2015"],
"outDir": "./dist",
"strict": true,
"typeRoots": ["./node_modules/@types", "./node_modules/@figma"]
}
}
Helper Utilities
Color Conversion
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
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
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:
# 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
// 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:
// 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:
# 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:
// 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
# 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
# Add to .cursor/ directory
mkdir -p .cursor
cp ~/Downloads/SKILL.md .cursor/design-system.md
# Cursor automatically reads skills from .cursor/
Programmatic Usage
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
- Run extraction on design-complete files — Extract after design system is stable
- Use local styles, not library styles — Plugin only reads local file styles
- Name styles consistently — Use hierarchical naming:
color/primary/500,text/heading/h1 - Leverage variable collections — Map design tokens to Figma variables for better extraction
- Refresh after changes — Re-run extraction when styles/variables updated
- 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:
- 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/
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/reason-machines/design-skills/figma-design-md-plugin">View figma-design-md-plugin on skillZs</a>