<< All versions

Skill v1.0.0

currentAutomated scan96/100
agnt-gg/agnt/figma-to-coded-html
──Details
PublishedOctober 30, 2026 at 06:58 AM
Content Hashsha256:b23c41ba050d6917...
Git SHAbb21bf1fd9df
──Files
Files (1 file, 12.6 KB)
SKILL.md12.6 KBactive
SKILL.md · 262 lines · 12.6 KB

name: figma-to-coded-html description: Convert Figma designs into self-contained, shareable single-file HTML with all UI elements coded in HTML/CSS/SVG and only actual photographs embedded as inline base64. Use this skill whenever the user asks to convert a Figma file/URL to HTML, export Figma designs as code, create HTML from Figma, turn a Figma mockup into a webpage, build a coded version of a Figma design, make a shareable HTML from Figma, or mentions Figma-to-HTML, Figma export, Figma MCP, Figma conversion, or wants a portable single-file HTML version of any Figma design. Also trigger when the user pastes a Figma URL (figma.com/design/... or figma.com/file/...) and asks to make it into HTML, code it, export it, or convert it. This skill encodes the exact proven pipeline from the June 2025 YOCCI conversion — Figma MCP authentication, file structure extraction, vision-based photo/UI classification, image fill downloading, base64 embedding, and fully-coded responsive HTML output. version: 1.0.0


Figma → Coded HTML Conversion Pipeline

Convert any Figma design into a self-contained, shareable single-file HTML where all UI is coded and only real photographs are embedded as base64.

Core Principles

  1. Code everything you CAN — charts, tables, cards, navbars, buttons, progress rings, icons = HTML/CSS/SVG. Never screenshot UI and embed as an image.
  2. Only real photos as images — model shots, product photos, background imagery, artwork — things that literally cannot be CSS.
  3. Base64 inline — photos embedded as data:image/jpeg;base64,... so the file is fully portable. Zero external dependencies except Google Fonts CDN.
  4. Single file output — save to workspace AND return in a fenced code block in chat.
  5. Don't ask, just build — do the work thoroughly, report results when done.

Pipeline Steps

Step 1: Authenticate the Figma MCP

The MCP server and AGNT auth vault store tokens separately, so they have to be synced.

Do NOT ask a tool to hand you the token. A tool result is sent verbatim to the model provider, stored in the conversations table, and rendered in the chat UI — so a token that passes through one is disclosed three times over. agnt_auth no longer has any operation that returns a credential value, for exactly this reason.

Sync it inside the Node process instead, where the value never enters the transcript:

js
// execute_javascript_code
const path = require('path');
const fs = require('fs');
const os = require('os');
const authPath = path.join(process.env.AGNT_APP_PATH || process.cwd(), 'backend/src/services/auth/AuthManager.js');
const AuthManager = (await import('file://' + authPath.replace(/\\/g, '/'))).default;
const token = await AuthManager.getValidAccessToken(process.env.AGNT_USER_ID, 'figma');
if (!token) {
console.log(JSON.stringify({ ok: false, reason: 'no_figma_token_stored' }));
} else {
const cfgDir = path.join(os.homedir(), '.mcp-figma');
fs.mkdirSync(cfgDir, { recursive: true });
const cfgPath = path.join(cfgDir, 'config.json');
const cfg = fs.existsSync(cfgPath) ? JSON.parse(fs.readFileSync(cfgPath, 'utf8')) : {};
cfg.apiKey = token;
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), { mode: 0o600 });
// Log a FACT, never the value.
console.log(JSON.stringify({ ok: true, wrote: cfgPath, tokenLength: token.length }));
}

Then verify through the MCP, which reads that config:

mcp__mcp-figma__check_api_key() → confirms the MCP server picked it up

If you need a usability check without touching the value at all:

agnt_auth → check_provider_token(provider_id: "figma") → { usable: true, token_length: 40 }

If the token is expired or missing: tell the user to update their Figma Personal Access Token in AGNT Settings → Connected Apps → Figma, then retry. Tokens are generated at https://www.figma.com/settings (Security → Personal Access Tokens).

Step 2: Fetch the File Structure

Extract the file key from the Figma URL — it's the alphanumeric string after /design/ or /file/:

  • https://www.figma.com/design/KtktcCn6MGe070tr92AbuZ/Testing → key is KtktcCn6MGe070tr92AbuZ
mcp__mcp-figma__get_file(fileKey, depth: 4)

This returns a cached JSON file on disk (not inline), typically at ~/.mcp-figma/cache/file_KEY_TIMESTAMP.json. Read it via file_operations → read, which will offload it since it's usually 50-100KB+.

Use query_data to explore the structure:

  • stats → see top-level keys (document, components, styles, etc.)
  • search for "type": "FRAME" → find exportable top-level frames (pages/screens)
  • search for "name" → find component and frame names
  • search for "characters" → extract all text content (labels, headings, prices, descriptions)

Key structure:

  • document.children[] — each is a page (CANVAS type)
  • First-level children of each page — top-level frames (your exportable screens)
  • COMPONENT / INSTANCE types — reusable UI elements (cards, buttons)

Step 3: Export Frame Images for Visual Reference

Export the main frames as PNG at 2× to see what the design looks like:

mcp__mcp-figma__get_image(fileKey, ids: ["node-id-1", "node-id-2"], format: "png", scale: 2)

Download the returned S3 URLs immediately to local files (they expire):

javascript
const resp = await fetch(s3Url);
const buf = Buffer.from(await resp.arrayBuffer());
fs.writeFileSync(outPath, buf);

Step 4: Vision-Classify Photo vs UI

Run analyze_image on each exported frame with a classification prompt:

"Identify ONLY the actual photographic images in this design. List each photo with its position and content. What's a real photo vs pure UI that can be coded in HTML/CSS?"

This is faster and more reliable than parsing JSON fill types. The vision model will clearly separate:

  • Photos: "Full-bleed hero model shot", "Product card with perfume bottle", "Night street scene"
  • UI: "Status bar", "Search input", "Navigation tabs", "Bar chart", "Price labels"

Step 5: Pull Actual Photos via Image Fills

mcp__mcp-figma__get_image_fills(fileKey)

This returns ALL embedded raster images (photographs, icons, artwork) as signed S3 URLs with content hashes.

Download and catalog them:

javascript
for (const [hash, url] of Object.entries(fills)) {
const resp = await fetch(url);
const buf = Buffer.from(await resp.arrayBuffer());
fs.writeFileSync(\`photos/fill-\${i}-\${hash.slice(0,8)}.\${ext}\`, buf);
}

Then run analyze_image on each downloaded photo with "What does this image show? One sentence." to build a role-based catalog:

javascript
const photoMap = {
hero: { file: 'fill-14-2b8760ff.jpg', desc: 'Male model hero shot' },
product1: { file: 'fill-1-0537cbdf.jpg', desc: 'Woman yellow sweater' },
service1: { file: 'fill-15-40fd2c15.jpg', desc: 'Gucci store colorful' },
ar: { file: 'fill-22-9a063460.png', desc: 'Dragon artwork' },
};

Skip these — they're UI elements, not design photos:

  • App icons (PayPal, etc.)
  • Upload placeholder graphics
  • Generic mountain/sun placeholder images
  • Tiny icons or UI decorators

Step 6: Extract Design Details for Coding

From the Figma JSON and vision analysis, extract:

DetailHow to find
Colorssearch for "color" in the JSON, or read from vision analysis
Typographysearch for "fontFamily", "fontSize", "fontWeight"
Text contentsearch for "characters" — gives every text string
Layoutsearch for "layoutMode" (HORIZONTAL/VERTICAL = flexbox)
Spacing"itemSpacing", "paddingLeft", etc.
Border radius"cornerRadius" values

Step 7: Build the HTML with Base64 Photos

This is the core output step. Use execute_javascript_code to:

  1. Read each photo file and convert to base64:
javascript
const buf = fs.readFileSync(photoPath);
const ext = photoPath.endsWith('.jpg') ? 'jpeg' : 'png';
const b64 = \`data:image/\${ext};base64,\${buf.toString('base64')}\`;
  1. Build the complete HTML as a JavaScript template literal with \${b64.hero}, \${b64.product1}, etc. interpolated into <img src="..."> tags.
  1. Write the file:
javascript
fs.writeFileSync(outputPath, html);

This is the ONLY reliable way to embed multi-MB base64 strings. You cannot paste them manually or use write_file with inline base64 — it must be generated and written programmatically in JavaScript.

Step 8: Deliver

  1. Save to workspace with a descriptive filename (e.g., yocci-coded.html)
  2. Report: file size, photo count, what's coded vs what's an image
  3. Return the full HTML in a fenced code block (the template code without base64 — the actual file with embedded base64 will be multi-MB)

What to Code vs What to Embed

Always Code in HTML/CSS/SVG

  • Navigation bars, tab bars, bottom navs
  • Buttons, CTAs, links
  • Search inputs, form elements
  • Typography, headings, labels, prices
  • Bar charts (horizontal or vertical) → CSS width/height + flexbox
  • Pie/donut charts → SVG paths
  • Area/line charts → SVG path elements
  • Tables → HTML <table> with styled rows
  • Cards → CSS border-radius, padding, shadow
  • Progress indicators → SVG circle with stroke-dasharray
  • Star ratings → repeated SVG star
  • Tags/badges → inline-block spans with background colors
  • Phone frames → CSS border-radius: 44px, border, box-shadow
  • Status bars → flexbox with text
  • Pagination dots → small circles with active state
  • Gradient overlays → CSS linear-gradient
  • Icons → inline SVG

Embed as Base64 Images

  • Hero/background photographs
  • Product photography
  • Model/portrait photos
  • Store/location photos
  • Artwork/illustrations that aren't geometric
  • Any raster content that can't be reproduced with CSS

Size Budget

ContentTypical RawAs Base64
Hero photo (full-bleed)200-400KB280-530KB
Product card photo20-80KB27-107KB
Service card background40-80KB53-107KB
Illustration/artwork200-500KB270-670KB
Total (coded + ~9 photos)—~2-3MB
vs screenshot-only approach—~7-8MB

Coding the UI instead of screenshotting it produces files that are 3-4× smaller, fully responsive, interactive, and editable.

Tools Reference

ToolPurpose
execute_javascript_code + AuthManager.getValidAccessTokenRead the Figma token IN-PROCESS and write it to the MCP config — never through a tool result
agnt_auth → check_provider_tokenConfirm the vault has a usable Figma token, without revealing it
mcp__mcp-figma__check_api_keyVerify MCP has a valid token
mcp__mcp-figma__get_fileFetch full file tree (pages, frames, components)
mcp__mcp-figma__get_file_nodesDeep dive into specific nodes with all properties
mcp__mcp-figma__get_imageExport any node as PNG/SVG at custom scale
mcp__mcp-figma__get_image_fillsGet all embedded photographs/rasters
analyze_imageVision-classify photo vs UI; catalog image contents
query_dataSearch offloaded Figma JSON for text, colors, structure
execute_javascript_codeDownload images, convert to base64, template & write HTML
write_fileSave final self-contained HTML to workspace

Common Gotchas

  1. MCP token sync is manual — AGNT vault and MCP server have separate token copies. Re-run the in-process sync in Step 1 after any token refresh.
  2. `get_image_fills` ≠ `get_image` — fills = source photos embedded in the design; get_image = rendered screenshots of frames. Use fills for photos, screenshots only for visual reference.
  3. Signed S3 URLs expire — download all images immediately after receiving URLs. Never store URLs for later use.
  4. Never route a credential through a tool result — tool results go to the model provider, into the conversations table, and onto the screen. agnt_auth has no operation that returns a credential value; read it in-process (Step 1) and log only a length or a boolean.
  5. Build HTML in JavaScript — template literals + fs.writeFileSync is the only sane way to embed multi-MB base64 strings. Cannot use write_file with inline base64.
  6. Large Figma files get offloaded — the JSON will be replaced with a data reference. Use query_data operations (stats, search, slice, json_path) to explore it.
  7. Vision analysis is faster than JSON parsing — to figure out what's a photo vs what's UI, screenshot the frame and ask the vision model. This beats traversing nested JSON looking for IMAGE fill types.
  8. Image too large for vision — if a photo exceeds 10MB, skip vision analysis for that one and identify it by context (it's usually the hero image).
All versions