
figma-capture-extension
Chrome extension that captures webpages into Figma's clipboard format with font fixes and DOM cleanup
- Installs
- 122
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/reason-machines/design-skills --skill figma-capture-extension- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads figma-capture-extension automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Figma Capture Extension
Skill by ara.so — Design Skills collection
This Chrome extension captures any webpage into Figma's clipboard format, adding post-processing to fix fonts (especially CJK), clean up DOM structure, and remove empty elements before pasting into Figma.
What It Does
- CJK Font Correction: Detects Chinese/Japanese/Korean text and remaps to
PingFang SC/Noto Serif SC - Font Mapping: Remaps unavailable fonts via configurable
font-map.json - Default Font Fallback: Assigns
Noto Sans SCto elements without explicit fonts - DOM Flattening: Removes wrapper elements that don't contribute visually
- Empty Frame Cleanup: Strips zero-size elements and childless containers
- Event Isolation: Prevents toolbar clicks from affecting host page behavior
Installation
Step 1: Download Figma's Capture Script
make
This downloads capture.js from Figma's public endpoint. The Makefile does:
capture.js:
curl -o capture.js https://www.figma.com/community/plugin/1159123024924461424/capture.js
Step 2: Configure Font Mapping
cp font-map.example.json font-map.json
Example font-map.json structure:
{
"Arial": "Inter",
"Helvetica": "Inter",
"SF Pro Display": "Google Sans Flex",
"Roboto": "Noto Sans SC",
"system-ui": "Inter"
}
Step 3: Load Extension in Chrome
- Navigate to
chrome://extensions - Enable Developer mode (top right toggle)
- Click Load unpacked
- Select the
figma-capturedirectory
Usage
Basic Capture Workflow
- Navigate to the webpage you want to capture
- Click the extension icon or press
Alt+Shift+F - Use the toolbar to:
- Capture entire page
- Select specific element by clicking
- Switch to Figma
- Press
Ctrl+V(Windows) orCmd+V(Mac)
Keyboard Shortcut
Default: Alt+Shift+F
To customize, go to chrome://extensions/shortcuts
Configuration Files
manifest.json
Key configuration sections:
{
"manifest_version": 3,
"name": "Figma Capture",
"version": "1.0.0",
"permissions": [
"activeTab",
"clipboardWrite"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_start"
}
],
"action": {
"default_icon": "icon.png"
},
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Alt+Shift+F"
}
}
}
}
Font Mapping Configuration
Edit font-map.json to add custom font substitutions:
{
"SegoeUI": "Inter",
"San Francisco": "Google Sans Flex",
"PingFang": "PingFang SC",
"Microsoft YaHei": "Noto Sans SC",
"Hiragino Sans": "Noto Sans JP"
}
Font mapping logic (applied in order):
- CJK detection →
PingFang SC/Noto Serif SC - Custom
font-map.jsonmappings - Icon font detection → preserve original
- No font specified →
Noto Sans SC
Architecture
Component Flow
User clicks extension icon
↓
background.js activates content script
↓
content.js injects capture.js
↓
capture.js serializes DOM → clipboard payload
↓
Clipboard interceptor transforms payload:
- Font correction
- DOM cleanup
- Wrapper flattening
↓
Modified payload written to clipboard
↓
User pastes into Figma
Key Files
- background.js: Service worker, patches
attachShadowfor event isolation - content.js: Injected script, intercepts clipboard API
- capture.js: Figma's official serializer (downloaded, not in repo)
- font-map.json: User-configurable font substitutions
Code Examples
Clipboard Interception Pattern
// Override clipboard write to transform Figma payload
const originalWrite = navigator.clipboard.write;
navigator.clipboard.write = async function(data) {
const items = await Promise.all(data.map(async item => {
if (item.types.includes('text/html')) {
const blob = await item.getType('text/html');
const html = await blob.text();
// Transform Figma's clipboard payload
const transformed = transformFigmaPayload(html);
return new ClipboardItem({
'text/html': new Blob([transformed], { type: 'text/html' })
});
}
return item;
}));
return originalWrite.call(this, items);
};
Font Detection and Remapping
function detectAndFixFont(element, computedStyle) {
const text = element.textContent || '';
const fontFamily = computedStyle.fontFamily;
// CJK detection
const cjkRegex = /[\u4E00-\u9FFF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/;
if (cjkRegex.test(text)) {
// Check if serif
const isSerif = /serif/i.test(fontFamily);
return isSerif ? 'Noto Serif SC' : 'PingFang SC';
}
// Load font map
const fontMap = loadFontMap(); // from font-map.json
// Check for mapped font
for (const [original, replacement] of Object.entries(fontMap)) {
if (fontFamily.includes(original)) {
return replacement;
}
}
// Icon font detection (preserve)
if (/icon|symbol|awesome|material/i.test(fontFamily)) {
return fontFamily;
}
// Default fallback
return fontFamily || 'Noto Sans SC';
}
DOM Cleanup: Wrapper Flattening
function flattenWrappers(node) {
// Skip if not a wrapper candidate
if (node.children.length !== 1) return false;
const parent = node;
const child = node.children[0];
// Check if parent is just a pass-through container
const parentStyle = getComputedStyle(parent);
const childStyle = getComputedStyle(child);
const isPassThrough =
parentStyle.backgroundColor === 'transparent' &&
parentStyle.border === 'none' &&
parent.getBoundingClientRect().width === child.getBoundingClientRect().width &&
parent.getBoundingClientRect().height === child.getBoundingClientRect().height;
if (isPassThrough) {
// Promote child to parent's position
parent.replaceWith(child);
return true;
}
return false;
}
Event Isolation for Toolbar
// In background.js - save closed shadow roots
const shadowRoots = new WeakMap();
const originalAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function(init) {
const shadowRoot = originalAttachShadow.call(this, init);
if (init.mode === 'closed') {
shadowRoots.set(this, shadowRoot);
}
return shadowRoot;
};
// In content.js - intercept toolbar clicks
window.addEventListener('click', (event) => {
const toolbar = document.querySelector('.figma-capture-toolbar');
if (!toolbar) return;
const toolbarHost = toolbar.host;
const shadowRoot = shadowRoots.get(toolbarHost);
if (shadowRoot) {
event.stopPropagation();
event.preventDefault();
// Re-dispatch inside shadow DOM
const point = shadowRoot.elementFromPoint(event.clientX, event.clientY);
if (point) {
point.dispatchEvent(new MouseEvent('click', event));
}
}
}, true); // Capture phase
Common Patterns
Handling Special Elements
// Skip script/style/svg from processing
const SKIP_TAGS = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'LINK', 'META'];
function shouldProcessElement(element) {
if (SKIP_TAGS.includes(element.tagName)) {
return false;
}
// Skip hidden elements
const style = getComputedStyle(element);
if (style.display === 'none' || style.visibility === 'hidden') {
return false;
}
// Skip zero-size elements
const rect = element.getBoundingClientRect();
if (rect.width === 0 && rect.height === 0) {
return false;
}
return true;
}
Custom Font Mapping for Specific Websites
// Create site-specific font mappings
const siteSpecificMaps = {
'github.com': {
'-apple-system': 'Inter',
'BlinkMacSystemFont': 'Inter',
'Segoe UI': 'Inter'
},
'twitter.com': {
'TwitterChirp': 'Google Sans Flex',
'Helvetica Neue': 'Inter'
}
};
function getFontMap() {
const hostname = window.location.hostname;
const baseMap = JSON.parse(localStorage.getItem('font-map.json') || '{}');
const siteMap = siteSpecificMaps[hostname] || {};
return { ...baseMap, ...siteMap };
}
Troubleshooting
Fonts Not Displaying Correctly in Figma
Problem: Pasted elements show default Times or incorrect fonts
Solution: Check if fonts are installed in Figma:
// Add debug logging to see what fonts are being applied
console.log('Applied font:', appliedFont);
console.log('Original font:', originalFont);
console.log('Font map:', fontMap);
Update font-map.json to map problematic fonts to fonts you have in Figma:
{
"ProblematicFont": "Inter",
"AnotherBadFont": "Roboto"
}
Extension Not Activating
Problem: Clicking icon does nothing
Solution: Check console for errors:
- Right-click extension icon → Inspect popup
- Go to
chrome://extensions→ Find Figma Capture → Click "background page" - Check for errors in console
Verify capture.js exists:
ls -la capture.js
# If missing:
make
Clipboard Payload Not Modified
Problem: Fonts/cleanup not being applied
Solution: Verify clipboard interceptor is loaded:
// In browser console on target page:
console.log(navigator.clipboard.write.toString());
// Should show wrapped function, not native code
Reload extension:
chrome://extensions- Click reload icon for Figma Capture
- Refresh target webpage
Empty or Broken Capture
Problem: Pasting creates empty frame or errors
Solution: Some elements may be over-cleaned. Adjust cleanup logic:
// Disable wrapper flattening temporarily
const ENABLE_FLATTENING = false;
if (ENABLE_FLATTENING) {
flattenWrappers(node);
}
Check for CSP violations in console — some sites block extension scripts.
CJK Text Not Detected
Problem: Chinese/Japanese/Korean text uses wrong font
Solution: Verify regex pattern matches your text:
const text = "你好世界";
const cjkRegex = /[\u4E00-\u9FFF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/;
console.log(cjkRegex.test(text)); // Should be true
Manually set font in font-map.json:
{
"SimSun": "Noto Sans SC",
"Microsoft YaHei": "PingFang SC"
}
Advanced Configuration
Modify Default Fallback Font
Edit content.js:
const DEFAULT_FALLBACK_FONT = 'Inter'; // Change from 'Noto Sans SC'
Add Custom Cleanup Rules
function customCleanup(element) {
// Remove all data attributes
for (const attr of element.attributes) {
if (attr.name.startsWith('data-')) {
element.removeAttribute(attr.name);
}
}
// Remove specific classes
const REMOVE_CLASSES = ['ad', 'banner', 'cookie-notice'];
element.classList.remove(...REMOVE_CLASSES);
}
Debug Mode
Add to content.js:
const DEBUG = true;
function debug(...args) {
if (DEBUG) {
console.log('[Figma Capture]', ...args);
}
}
debug('Font applied:', fontFamily);
debug('Element cleaned:', element.tagName);
Limitations
- Figma Dependency: Relies on Figma's undocumented clipboard format (may break)
- Font Availability: Target fonts must be installed in Figma
- CSP Restrictions: Some sites block extension script injection
- Dynamic Content: May not capture lazy-loaded or JavaScript-rendered elements
capture.jsUpdates: Figma may change/remove the download endpoint
Resources
- Figma HTML to Design Plugin: https://www.figma.com/community/plugin/1159123024924461424
- Chrome Extension Manifest V3: https://developer.chrome.com/docs/extensions/mv3/
- Clipboard API: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API
FAQ
What does the figma-capture-extension skill do?
Chrome extension that captures webpages into Figma's clipboard format with font fixes and DOM cleanup
How do I install figma-capture-extension?
Run npx skills add https://github.com/reason-machines/design-skills --skill figma-capture-extension in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/figma-capture-extension/ (personal) or .claude/skills/figma-capture-extension/ (project).
Which AI agents can use figma-capture-extension?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is figma-capture-extension safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains figma-capture-extension?
It is published by reason-machines in reason-machines/design-skills. It has 122 recorded installs.
More from reason-machines/design-skills
awesome-claude-design
reason-machines/design-skills
Open the skill to read its SKILL.md instructions.
design-md-format
reason-machines/design-skills
Create and validate DESIGN.md files that give AI coding agents structured understanding of design systems through machine-readable tokens and human-readable rationale.
open-codesign-ai-design
reason-machines/design-skills
Use Open CoDesign to generate prototypes, slides, and PDFs from prompts with Claude, GPT, Gemini, or local models
open-design-ai-prototyping
reason-machines/design-skills
Open the skill to read its SKILL.md instructions.
diagram-design-editorial
reason-machines/design-skills
Create editorial-quality diagrams in HTML + SVG matching your brand — architecture, flowcharts, sequences, timelines, quadrants, and 9 more types.
claude-design-system-hooks
reason-machines/design-skills
AI-powered design-to-code engine that generates production-ready UI components from natural language using Claude API
Popular frontend & design skills
setup-matt-pocock-skills
mattpocock/skills
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
hyperframes-animation
heygen-com/hyperframes
All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.
prototype
mattpocock/skills
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
vercel-react-best-practices
vercel-labs/agent-skills
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.