design-md-chrome-extractor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

design-md-chrome-extractor

design-md-chrome-extractor

Skill by ara.so — Design Skills collection.
Skill by ara.so — Design Skills collection.

Overview

概述

The TypeUI DESIGN.md Extractor is a Chrome extension that analyzes any website and extracts its design system (typography, colors, spacing, radius, shadows, motion) to generate standardized
DESIGN.md
or
SKILL.md
files. These files follow the open-source TypeUI format and can be used with AI coding tools like Claude Code, Google Stitch, Codex, and Cursor to replicate design systems.
Key capabilities:
  • Auto-extract design tokens from any webpage
  • Generate DESIGN.md (design system documentation)
  • Generate SKILL.md (AI agent-ready skills)
  • Download extracted files for immediate use
  • Refresh extraction on page state changes
TypeUI DESIGN.md提取器是一款Chrome扩展程序,可分析任意网站并提取其设计系统(排版、颜色、间距、圆角、阴影、动效),生成标准化的
DESIGN.md
SKILL.md
文件。这些文件遵循开源的TypeUI格式,可与Claude Code、Google Stitch、Codex和Cursor等AI编码工具配合使用,以复刻设计系统。
核心功能:
  • 自动从任意网页提取设计令牌
  • 生成DESIGN.md(设计系统文档)
  • 生成SKILL.md(适配AI Agent的技能文件)
  • 下载提取后的文件以便立即使用
  • 页面状态变化时重新提取

Installation

安装

From Chrome Web Store

从Chrome应用商店安装

Local Development Installation

本地开发安装

  1. Clone the repository:
bash
git clone https://github.com/bergside/design-md-chrome.git
cd design-md-chrome
  1. Load in Chrome:
    • Open
      chrome://extensions
    • Enable Developer mode (toggle in top-right)
    • Click Load unpacked
    • Select the
      design-md-chrome
      project folder
  2. Verify installation:
    • Extension icon should appear in Chrome toolbar
    • Pin it for easy access
  1. 克隆仓库:
bash
git clone https://github.com/bergside/design-md-chrome.git
cd design-md-chrome
  1. 在Chrome中加载扩展:
    • 打开
      chrome://extensions
    • 启用开发者模式(右上角开关)
    • 点击加载已解压的扩展程序
    • 选择
      design-md-chrome
      项目文件夹
  2. 验证安装:
    • 扩展图标应出现在Chrome工具栏中
    • 固定图标以便快速访问

Usage Patterns

使用模式

Basic Extraction Workflow

基础提取流程

  1. Navigate to target website (e.g.,
    https://stripe.com
    )
  2. Click the extension icon in toolbar
  3. Click "Auto-extract" button
  4. Review extracted design tokens in popup
  5. Generate DESIGN.md or SKILL.md
  6. Download the file
  1. 导航至目标网站(例如:
    https://stripe.com
  2. 点击工具栏中的扩展图标
  3. 点击“自动提取”按钮
  4. 查看弹出窗口中提取的设计令牌
  5. 生成DESIGN.md或SKILL.md文件
  6. 下载文件

Programmatic Extraction (Content Script)

程序化提取(内容脚本)

The extension injects
content.js
which performs extraction:
javascript
// Core extraction happens via message passing
chrome.runtime.sendMessage({
  action: 'extract',
  url: window.location.href
}, (response) => {
  console.log('Extracted tokens:', response.tokens);
});
扩展会注入
content.js
来执行提取操作:
javascript
// 核心提取通过消息传递完成
chrome.runtime.sendMessage({
  action: 'extract',
  url: window.location.href
}, (response) => {
  console.log('Extracted tokens:', response.tokens);
});

Style Token Structure

样式令牌结构

Extracted tokens follow this structure:
javascript
{
  typography: {
    fontFamilies: ['Inter', 'system-ui', 'sans-serif'],
    fontSizes: ['12px', '14px', '16px', '20px', '24px', '32px'],
    fontWeights: ['400', '500', '600', '700'],
    lineHeights: ['1.2', '1.5', '1.6']
  },
  colors: {
    primary: ['#635BFF', '#0A2540'],
    neutral: ['#FFFFFF', '#F6F9FC', '#E3E8EE'],
    semantic: {
      success: '#00D924',
      error: '#DF1B41',
      warning: '#FFC043'
    }
  },
  spacing: ['4px', '8px', '12px', '16px', '24px', '32px', '48px', '64px'],
  radius: ['0px', '4px', '8px', '12px', '16px', '9999px'],
  shadows: [
    '0 1px 3px rgba(0,0,0,0.1)',
    '0 4px 6px rgba(0,0,0,0.1)',
    '0 10px 40px rgba(0,0,0,0.15)'
  ],
  motion: {
    durations: ['150ms', '200ms', '300ms', '500ms'],
    easings: ['ease', 'ease-in-out', 'cubic-bezier(0.4,0,0.2,1)']
  }
}
提取的令牌遵循以下结构:
javascript
{
  typography: {
    fontFamilies: ['Inter', 'system-ui', 'sans-serif'],
    fontSizes: ['12px', '14px', '16px', '20px', '24px', '32px'],
    fontWeights: ['400', '500', '600', '700'],
    lineHeights: ['1.2', '1.5', '1.6']
  },
  colors: {
    primary: ['#635BFF', '#0A2540'],
    neutral: ['#FFFFFF', '#F6F9FC', '#E3E8EE'],
    semantic: {
      success: '#00D924',
      error: '#DF1B41',
      warning: '#FFC043'
    }
  },
  spacing: ['4px', '8px', '12px', '16px', '24px', '32px', '48px', '64px'],
  radius: ['0px', '4px', '8px', '12px', '16px', '9999px'],
  shadows: [
    '0 1px 3px rgba(0,0,0,0.1)',
    '0 4px 6px rgba(0,0,0,0.1)',
    '0 10px 40px rgba(0,0,0,0.15)'
  ],
  motion: {
    durations: ['150ms', '200ms', '300ms', '500ms'],
    easings: ['ease', 'ease-in-out', 'cubic-bezier(0.4,0,0.2,1)']
  }
}

Generated File Structure

生成文件结构

DESIGN.md Format

DESIGN.md格式

markdown
undefined
markdown
undefined

[Site Name] Design System

[站点名称] 设计系统

Mission

目标

Define and maintain consistent visual language for [product].
为[产品]定义并维护一致的视觉语言。

Brand

品牌

Style Foundations

样式基础

Typography

排版

  • Font families: Inter, system-ui
  • Sizes: 12px, 14px, 16px, 20px, 24px, 32px
  • Weights: 400 (regular), 500 (medium), 600 (semibold), 700 (bold)
  • Line heights: 1.2 (tight), 1.5 (base), 1.6 (relaxed)
  • 字体家族: Inter, system-ui
  • 字号: 12px, 14px, 16px, 20px, 24px, 32px
  • 字重: 400(常规), 500(中等), 600(半粗体), 700(粗体)
  • 行高: 1.2(紧凑), 1.5(基准), 1.6(宽松)

Colors

颜色

Primary
  • #635BFF
    - Brand primary
  • #0A2540
    - Dark navy
Neutral
  • #FFFFFF
    - White
  • #F6F9FC
    - Light gray
  • #E3E8EE
    - Medium gray
主色
  • #635BFF
    - 品牌主色
  • #0A2540
    - 深海军蓝
中性色
  • #FFFFFF
    - 白色
  • #F6F9FC
    - 浅灰色
  • #E3E8EE
    - 中灰色

Spacing

间距

Scale: 4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px
刻度: 4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px

Radius

圆角

Values: 0px, 4px, 8px, 12px, 16px, 9999px (pill)
取值: 0px, 4px, 8px, 12px, 16px, 9999px(胶囊形)

Accessibility

无障碍

  • WCAG 2.2 AA compliance required
  • Minimum contrast ratio 4.5:1 for text
  • Focus indicators required on all interactive elements
undefined
  • 需符合WCAG 2.2 AA标准
  • 文本最小对比度为4.5:1
  • 所有交互元素必须有焦点指示器
undefined

SKILL.md Format

SKILL.md格式

Similar structure but optimized for AI agent consumption with additional sections:
  • Component Rule Expectations: State, interaction, accessibility requirements
  • Quality Gates: Testable validation criteria
  • Guideline Authoring Workflow: Step-by-step implementation process
结构与DESIGN.md类似,但针对AI Agent的使用进行了优化,增加了以下章节:
  • 组件规则预期: 状态、交互、无障碍要求
  • 质量门: 可测试的验证标准
  • 指南编写流程: 分步实现过程

Extension Architecture

扩展架构

Manifest (manifest.json)

清单文件(manifest.json)

json
{
  "manifest_version": 3,
  "name": "DESIGN.MD Style Extractor",
  "version": "1.0.0",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"]
  }]
}
json
{
  "manifest_version": 3,
  "name": "DESIGN.MD Style Extractor",
  "version": "1.0.0",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"]
  }]
}

Message Passing Pattern

消息传递模式

Popup → Content Script:
javascript
// popup.js
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  chrome.tabs.sendMessage(tabs[0].id, {
    action: 'extractStyles'
  }, (response) => {
    displayTokens(response.tokens);
  });
});
Content Script → Background:
javascript
// content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'extractStyles') {
    const tokens = extractDesignTokens();
    sendResponse({tokens});
  }
});
弹出窗口 → 内容脚本:
javascript
// popup.js
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  chrome.tabs.sendMessage(tabs[0].id, {
    action: 'extractStyles'
  }, (response) => {
    displayTokens(response.tokens);
  });
});
内容脚本 → 后台:
javascript
// content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'extractStyles') {
    const tokens = extractDesignTokens();
    sendResponse({tokens});
  }
});

API Reference

API参考

Extension Actions

扩展操作

ActionDescriptionResponse
extractStyles
Scan page for design tokens
{tokens: Object}
generateDesignMD
Create DESIGN.md content
{markdown: String}
generateSkillMD
Create SKILL.md content
{markdown: String}
refresh
Re-run extraction
{tokens: Object}
download
Trigger file download
{success: Boolean}
操作描述响应
extractStyles
扫描页面提取设计令牌
{tokens: Object}
generateDesignMD
创建DESIGN.md内容
{markdown: String}
generateSkillMD
创建SKILL.md内容
{markdown: String}
refresh
重新执行提取
{tokens: Object}
download
触发文件下载
{success: Boolean}

Core Extraction Functions

核心提取函数

javascript
// Extract all computed styles from page
function extractDesignTokens() {
  const allElements = document.querySelectorAll('*');
  const tokens = {
    typography: new Set(),
    colors: new Set(),
    spacing: new Set(),
    radius: new Set(),
    shadows: new Set()
  };
  
  allElements.forEach(el => {
    const computed = window.getComputedStyle(el);
    
    // Extract typography
    tokens.typography.add(computed.fontFamily);
    tokens.typography.add(computed.fontSize);
    tokens.typography.add(computed.fontWeight);
    
    // Extract colors
    if (computed.color !== 'rgba(0, 0, 0, 0)') {
      tokens.colors.add(computed.color);
    }
    if (computed.backgroundColor !== 'rgba(0, 0, 0, 0)') {
      tokens.colors.add(computed.backgroundColor);
    }
    
    // Extract spacing
    ['margin', 'padding'].forEach(prop => {
      ['Top', 'Right', 'Bottom', 'Left'].forEach(side => {
        const value = computed[prop + side];
        if (value !== '0px') tokens.spacing.add(value);
      });
    });
    
    // Extract border radius
    if (computed.borderRadius !== '0px') {
      tokens.radius.add(computed.borderRadius);
    }
    
    // Extract box shadows
    if (computed.boxShadow !== 'none') {
      tokens.shadows.add(computed.boxShadow);
    }
  });
  
  return normalizeTokens(tokens);
}

// Normalize and deduplicate tokens
function normalizeTokens(rawTokens) {
  return {
    typography: {
      fontFamilies: Array.from(rawTokens.typography).filter(isFontFamily),
      fontSizes: Array.from(rawTokens.typography).filter(isFontSize).sort(),
      fontWeights: Array.from(rawTokens.typography).filter(isFontWeight).sort()
    },
    colors: Array.from(rawTokens.colors).map(normalizeColor),
    spacing: Array.from(rawTokens.spacing).sort(numericalSort),
    radius: Array.from(rawTokens.radius).sort(numericalSort),
    shadows: Array.from(rawTokens.shadows)
  };
}
javascript
// 提取页面所有计算样式
function extractDesignTokens() {
  const allElements = document.querySelectorAll('*');
  const tokens = {
    typography: new Set(),
    colors: new Set(),
    spacing: new Set(),
    radius: new Set(),
    shadows: new Set()
  };
  
  allElements.forEach(el => {
    const computed = window.getComputedStyle(el);
    
    // 提取排版信息
    tokens.typography.add(computed.fontFamily);
    tokens.typography.add(computed.fontSize);
    tokens.typography.add(computed.fontWeight);
    
    // 提取颜色
    if (computed.color !== 'rgba(0, 0, 0, 0)') {
      tokens.colors.add(computed.color);
    }
    if (computed.backgroundColor !== 'rgba(0, 0, 0, 0)') {
      tokens.colors.add(computed.backgroundColor);
    }
    
    // 提取间距
    ['margin', 'padding'].forEach(prop => {
      ['Top', 'Right', 'Bottom', 'Left'].forEach(side => {
        const value = computed[prop + side];
        if (value !== '0px') tokens.spacing.add(value);
      });
    });
    
    // 提取圆角
    if (computed.borderRadius !== '0px') {
      tokens.radius.add(computed.borderRadius);
    }
    
    // 提取阴影
    if (computed.boxShadow !== 'none') {
      tokens.shadows.add(computed.boxShadow);
    }
  });
  
  return normalizeTokens(tokens);
}

// 标准化并去重令牌
function normalizeTokens(rawTokens) {
  return {
    typography: {
      fontFamilies: Array.from(rawTokens.typography).filter(isFontFamily),
      fontSizes: Array.from(rawTokens.typography).filter(isFontSize).sort(),
      fontWeights: Array.from(rawTokens.typography).filter(isFontWeight).sort()
    },
    colors: Array.from(rawTokens.colors).map(normalizeColor),
    spacing: Array.from(rawTokens.spacing).sort(numericalSort),
    radius: Array.from(rawTokens.radius).sort(numericalSort),
    shadows: Array.from(rawTokens.shadows)
  };
}

Testing

测试

Run the test suite:
bash
node tests/run-tests.mjs
Test coverage includes:
  • Token extraction accuracy
  • Markdown generation validity
  • Color normalization (rgb → hex)
  • Spacing scale deduplication
运行测试套件:
bash
node tests/run-tests.mjs
测试覆盖范围包括:
  • 令牌提取准确性
  • Markdown生成有效性
  • 颜色标准化(rgb → hex)
  • 间距刻度去重

Common Patterns

常见模式

Extract and Save Workflow

提取并保存流程

javascript
async function extractAndSave(format = 'DESIGN') {
  // 1. Extract tokens
  const tokens = await extractDesignTokens();
  
  // 2. Generate markdown
  const markdown = format === 'DESIGN' 
    ? generateDesignMD(tokens)
    : generateSkillMD(tokens);
  
  // 3. Download file
  const blob = new Blob([markdown], {type: 'text/markdown'});
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${format}.md`;
  a.click();
}
javascript
async function extractAndSave(format = 'DESIGN') {
  // 1. 提取令牌
  const tokens = await extractDesignTokens();
  
  // 2. 生成Markdown
  const markdown = format === 'DESIGN' 
    ? generateDesignMD(tokens)
    : generateSkillMD(tokens);
  
  // 3. 下载文件
  const blob = new Blob([markdown], {type: 'text/markdown'});
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${format}.md`;
  a.click();
}

Custom Token Filtering

自定义令牌过滤

javascript
// Filter extracted colors by frequency
function filterFrequentColors(colors, minOccurrences = 5) {
  const colorMap = new Map();
  
  colors.forEach(color => {
    const normalized = normalizeColor(color);
    colorMap.set(normalized, (colorMap.get(normalized) || 0) + 1);
  });
  
  return Array.from(colorMap.entries())
    .filter(([_, count]) => count >= minOccurrences)
    .map(([color, _]) => color);
}
javascript
// 按频率过滤提取的颜色
function filterFrequentColors(colors, minOccurrences = 5) {
  const colorMap = new Map();
  
  colors.forEach(color => {
    const normalized = normalizeColor(color);
    colorMap.set(normalized, (colorMap.get(normalized) || 0) + 1);
  });
  
  return Array.from(colorMap.entries())
    .filter(([_, count]) => count >= minOccurrences)
    .map(([color, _]) => color);
}

Merge Multiple Extractions

合并多次提取结果

javascript
// Combine tokens from multiple pages
function mergeTokens(tokensArray) {
  return {
    typography: {
      fontFamilies: [...new Set(tokensArray.flatMap(t => t.typography.fontFamilies))],
      fontSizes: [...new Set(tokensArray.flatMap(t => t.typography.fontSizes))].sort(),
      fontWeights: [...new Set(tokensArray.flatMap(t => t.typography.fontWeights))].sort()
    },
    colors: [...new Set(tokensArray.flatMap(t => t.colors))],
    spacing: [...new Set(tokensArray.flatMap(t => t.spacing))].sort(numericalSort),
    radius: [...new Set(tokensArray.flatMap(t => t.radius))].sort(numericalSort),
    shadows: [...new Set(tokensArray.flatMap(t => t.shadows))]
  };
}
javascript
// 合并多个页面的令牌
function mergeTokens(tokensArray) {
  return {
    typography: {
      fontFamilies: [...new Set(tokensArray.flatMap(t => t.typography.fontFamilies))],
      fontSizes: [...new Set(tokensArray.flatMap(t => t.typography.fontSizes))].sort(),
      fontWeights: [...new Set(tokensArray.flatMap(t => t.typography.fontWeights))].sort()
    },
    colors: [...new Set(tokensArray.flatMap(t => t.colors))],
    spacing: [...new Set(tokensArray.flatMap(t => t.spacing))].sort(numericalSort),
    radius: [...new Set(tokensArray.flatMap(t => t.radius))].sort(numericalSort),
    shadows: [...new Set(tokensArray.flatMap(t => t.shadows))]
  };
}

Troubleshooting

故障排除

Extension Not Detecting Styles

扩展无法检测到样式

Issue: Extracted tokens are empty or incomplete.
Solutions:
  • Wait for page to fully load (including dynamic content)
  • Refresh extraction after SPA navigation
  • Check if styles are in Shadow DOM (requires additional traversal)
  • Verify content script has injected:
    chrome://extensions
    → Inspect views
问题:提取的令牌为空或不完整。
解决方案
  • 等待页面完全加载(包括动态内容)
  • SPA导航后重新提取
  • 检查样式是否在Shadow DOM中(需要额外遍历)
  • 验证内容脚本已注入:
    chrome://extensions
    → 检查视图

Download Not Working

下载功能失效

Issue: Generated file doesn't download.
Solutions:
javascript
// Ensure proper blob MIME type
const blob = new Blob([content], {type: 'text/markdown;charset=utf-8'});

// Add fallback for different browsers
if (navigator.msSaveBlob) {
  navigator.msSaveBlob(blob, filename);
} else {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}
问题:生成的文件无法下载。
解决方案
javascript
// 确保Blob的MIME类型正确
const blob = new Blob([content], {type: 'text/markdown;charset=utf-8'});

// 为不同浏览器添加兼容方案
if (navigator.msSaveBlob) {
  navigator.msSaveBlob(blob, filename);
} else {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

Duplicate Tokens

令牌重复

Issue: Multiple similar values extracted (e.g.,
16px
and
16.5px
).
Solution:
javascript
// Round to nearest whole pixel
function roundPixels(value) {
  const num = parseFloat(value);
  return Math.round(num) + 'px';
}

// Apply tolerance-based deduplication
function deduplicateWithTolerance(values, tolerance = 1) {
  return values.reduce((acc, val) => {
    const num = parseFloat(val);
    if (!acc.some(existing => Math.abs(parseFloat(existing) - num) < tolerance)) {
      acc.push(val);
    }
    return acc;
  }, []);
}
问题:提取到多个相似值(例如:
16px
16.5px
)。
解决方案
javascript
// 四舍五入到最接近的整数像素
function roundPixels(value) {
  const num = parseFloat(value);
  return Math.round(num) + 'px';
}

// 基于容差的去重
function deduplicateWithTolerance(values, tolerance = 1) {
  return values.reduce((acc, val) => {
    const num = parseFloat(val);
    if (!acc.some(existing => Math.abs(parseFloat(existing) - num) < tolerance)) {
      acc.push(val);
    }
    return acc;
  }, []);
}

Cross-Origin Restrictions

跨域限制

Issue: Cannot extract from iframe with different origin.
Solution: Extension permissions already include
<all_urls>
, but iframes require explicit traversal:
javascript
function extractFromIframes() {
  const iframes = document.querySelectorAll('iframe');
  iframes.forEach(iframe => {
    try {
      const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
      // Extract from iframe document
      extractDesignTokens(iframeDoc);
    } catch (e) {
      console.warn('Cannot access iframe:', e);
    }
  });
}
问题:无法提取不同源iframe中的样式。
解决方案:扩展权限已包含
<all_urls>
,但iframe需要显式遍历:
javascript
function extractFromIframes() {
  const iframes = document.querySelectorAll('iframe');
  iframes.forEach(iframe => {
    try {
      const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
      // 从iframe文档提取
      extractDesignTokens(iframeDoc);
    } catch (e) {
      console.warn('无法访问iframe:', e);
    }
  });
}

Integration with AI Coding Tools

与AI编码工具集成

Using Extracted DESIGN.md with Claude Code

将提取的DESIGN.md与Claude Code配合使用

bash
undefined
bash
undefined

Add to project context

添加到项目上下文

cp DESIGN.md .claude/
cp DESIGN.md .claude/

Reference in prompts

在提示词中引用

"Build a button component following DESIGN.md spacing and color tokens"
undefined
"按照DESIGN.md中的间距和颜色令牌构建一个按钮组件"
undefined

Using with Cursor

与Cursor配合使用

bash
undefined
bash
undefined

Add to .cursorrules

添加到.cursorrules

cp DESIGN.md .cursorrules/design-system.md
cp DESIGN.md .cursorrules/design-system.md

Reference in code

在代码中引用

// Uses spacing-4 (16px) from DESIGN.md
<div className="p-4"> ```
// 使用DESIGN.md中的spacing-4(16px)
<div className="p-4"> ```

Using with Google Stitch

与Google Stitch配合使用

Upload
SKILL.md
as a custom skill to enable design-system-aware code generation.
上传
SKILL.md
作为自定义技能,启用支持设计系统的代码生成。

Resources

资源