Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Extract Figma designs and generate production-ready React/Next.js components with TypeScript, Tailwind CSS, and pixel-perfect accuracy. Use when a user provides a Figma URL or asks to convert Figma designs to code.
.claude/skills/majiayu000-figma-to-code/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -14% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 88% | 0% |
Extract complete, lossless design information from Figma and generate production-ready React/Next.js components with TypeScript and Tailwind CSS.
Use 100% of Figma MCP output. Every className, every property matters.
tsx// ✅ CORRECT: Keep ALL className from Figma MCP <div className="absolute font-source-serif h-[108px] leading-[1.8] left-[100px] not-italic text-[20px] text-[rgba(29,38,45,0.8)] text-justify top-[210px] w-[1096px] whitespace-pre-wrap"> // ❌ WRONG: Removing any className <div className="absolute left-[100px] top-[210px] font-source-serif text-[20px]">
absolute contents Structures🔥 CRITICAL: Figma MCP returns nested absolute contents containers. display: contents makes the parent "disappear" - children are positioned relative to the nearest positioned ancestor (root)!
Key Insight: Children's positions are ALREADY absolute - DO NOT add parent's top/left!
tsx// ❌ WRONG: Figma MCP output (has redundant parent wrapper) <div className="absolute contents left-0 top-[41px]"> <p className="absolute left-[100px] top-[41px]">TITLE</p> <div className="absolute left-0 top-[100px]">Line</div> </div> // ✅ CORRECT: Just remove the parent wrapper, keep children's positions AS-IS <> <p className="absolute left-[100px] top-[41px]">TITLE</p> <div className="absolute left-0 top-[100px] w-[1920px] h-[1px] bg-[#C5CBCE] opacity-30" /> </>
Position Handling Rules:
| Parent Type | Child Position | Action | |-------------|----------------|--------| | absolute contents | Child has own top/left | Keep child position AS-IS, just remove parent | | absolute (no contents) | Child has relative top/left | Calculate: parent + child | | relative | Child has top/left | Calculate: parent + child |
🔥 The Golden Rule:
If parent has "contents" class → Child positions are already absolute → Keep AS-IS
If parent has NO "contents" class → Child positions are relative → Add parent + childReference: Verified correct positions (from production HTML):
top-[41px] (not 82px)top-[100px] (not 141px)top-[980px]top-[1004px]NEVER hardcode dimensions!
typescript// 1. Get metadata first const metadata = await mcp__figma__get_metadata({ fileKey: 'xxx', nodeId: '11:1420' }) // 2. Extract from XML // <frame width="1920" height="1080"> const pageWidth = 1920 const pageHeight = 1080 // 3. Use extracted values <div className="w-[1920px] h-[1080px]">
🔥 CRITICAL: Use Google Fonts CDN directly, NOT next/font/google!
next/font/google generates CSS variables and self-hosts fonts, but the font rendering may differ from reference HTML that uses Google Fonts CDN directly. This causes:
tsx// ❌ WRONG: Using next/font/google import { Source_Serif_4, Kaisei_Tokumin } from 'next/font/google' const sourceSerif = Source_Serif_4({ subsets: ['latin'], variable: '--font-source-serif' }) // This may render fonts differently than Google Fonts CDN! // ✅ CORRECT: Use Google Fonts CDN directly in layout.tsx export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" /> <link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Kaisei+Tokumin:wght@400;500;700;800&display=swap" rel="stylesheet" /> </head> <body>{children}</body> </html> ) }
Key: Include opsz (optical size) axis for Source Serif 4 - this affects character widths!
css@layer utilities { /* Use direct font-family names, NOT CSS variables */ .font-source-serif { font-family: 'Source Serif 4', serif; } .font-kaisei { font-family: 'Kaisei Tokumin', serif; } }
typescript// Figma MCP returns: font-['Kaisei_Tokumin:ExtraBold',sans-serif] font-['Source_Serif_Pro:SemiBold',sans-serif] // ✅ Convert to Tailwind classes: font-kaisei font-extrabold font-source-serif font-semibold // Font name corrections (Google Fonts 2024): 'Source Serif Pro' → 'Source Serif 4' 'Source Sans Pro' → 'Source Sans 3'
⚠️ Figma's font weight names may NOT match CSS font-weights!
Figma renders fonts differently than browsers. What Figma calls "Bold" might visually appear lighter than CSS font-weight: 700.
| Figma Weight Name | Expected CSS | May Actually Need | |-------------------|--------------|-------------------| | Regular | 400 | 400 | | Medium | 500 | 500 | | Bold | 700 | 500 or 600 (test visually!) | | ExtraBold | 800 | 700 (test visually!) |
Solution: Always compare with Figma screenshot. If text looks too bold, try one weight lighter:
font-bold (700) → try font-medium (500)font-extrabold (800) → try font-bold (700)Must add to globals.css:
cssbody { overflow-x: auto; /* Allow horizontal scroll */ } .page-container { min-width: max-content; /* Prevent compression */ display: inline-block; /* Keep layout intact */ }
Optimize line images:
tsx// ❌ Before: Image-based line <div className="absolute h-0 left-0 top-[100px] w-[1920px]"> <div className="absolute inset-[-1px_0_0_0]"> <img src={imgLine} /> </div> </div> // ✅ After: CSS-based line <div className="absolute left-0 top-[141px] w-[1920px] h-[1px] bg-[#C5CBCE] opacity-30" />
tsx// ❌ Before: External image <img src={imgVector} /> // ✅ After: Inline SVG <svg viewBox="0 0 35 34" fill="none"> <path d="M17.5 0L0 34..." fill="#1d262d"/> </svg>
🔥 CRITICAL: Figma MCP outputs fixed heights for text blocks, but this causes line-wrapping issues!
Font metrics differ between Figma's rendering and browser rendering (even with the same font family). Fixed heights can cause:
tsx// ❌ WRONG: Figma MCP output with fixed height <p className="absolute h-[72px] leading-[1.8] left-[100px] text-[20px] top-[570px] w-[1096px]"> Long text that might wrap differently in browser... </p> // ✅ CORRECT: Remove h-[Xpx], let text flow naturally <div className="absolute leading-[1.8] left-[100px] text-[20px] top-[570px] w-[1096px]"> <p className="mb-0">Long text that might wrap differently in browser...</p> </div>
When to keep fixed heights:
h-[34px]When to remove fixed heights:
h-[Xpx]text-justify - especially importantPattern: Use <div> wrapper with <p className="mb-0">:
tsx// This matches reference HTML structure and ensures proper text flow <div className="absolute font-source-serif leading-[1.8] left-[100px] text-[20px] top-[570px] w-[1096px]"> <p className="mb-0">Text content here...</p> </div>
Detailed material starting at ### **Rule 8: Table Pattern Detection & Conversion** has been moved to reference/extended.md to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 39,904 | 21,749 | -45% | 1 | 1 | 0% | 8,278 | 7,156 | -14% | 0 | 0 | — |
case-02 | fail→pass | 37,743 | 16,904 | -55% | 1 | 1 | 0% | 6,168 | 5,943 | -4% | 0 | 0 | — |
case-03 | fail→pass | 50,731 | 34,119 | -33% | 1 | 1 | 0% | 8,087 | 10,084 | +25% | 0 | 0 | — |
case-04 | pass→pass | 18,727 | 12,145 | -35% | 1 | 1 | 0% | 2,838 | 4,966 | +75% | 0 | 0 | — |
case-05 | pass→pass | 9,452 | 8,112 | -14% | 1 | 1 | 0% | 1,439 | 4,111 | +186% | 0 | 0 | — |
case-06 | pass→pass | 9,385 | 15,623 | +66% | 1 | 1 | 0% | 1,785 | 5,815 | +226% | 0 | 0 | — |
case-07 | fail→pass | 16,965 | 6,383 | -62% | 1 | 1 | 0% | 2,529 | 3,572 | +41% | 0 | 0 | — |
case-17 | fail→pass | 13,735 | 7,661 | -44% | 1 | 1 | 0% | 1,950 | 3,660 | +88% | 0 | 0 | — |
case-08 | fail→pass | 11,766 | 6,333 | -46% | 1 | 1 | 0% | 1,917 | 3,569 | +86% | 0 | 0 | — |
case-09 | fail→pass | 12,668 | 4,128 | -67% | 1 | 1 | 0% | 2,064 | 3,292 | +59% | 0 | 0 | — |
case-10 | fail→pass | 12,012 | 5,947 | -50% | 1 | 1 | 0% | 2,180 | 3,748 | +72% | 0 | 0 | — |
case-11 | pass→pass | 16,805 | 6,062 | -64% | 1 | 1 | 0% | 2,544 | 3,737 | +47% | 0 | 0 | — |
case-12 | fail→pass | 18,687 | 6,207 | -67% | 1 | 1 | 0% | 2,755 | 3,744 | +36% | 0 | 0 | — |
case-13 | fail→pass | 9,794 | 4,904 | -50% | 1 | 1 | 0% | 1,814 | 3,463 | +91% | 0 | 0 | — |
case-14 | pass→pass | 10,332 | 4,778 | -54% | 1 | 1 | 0% | 2,010 | 3,399 | +69% | 0 | 0 | — |
case-15 | pass→pass | 7,672 | 3,524 | -54% | 1 | 1 | 0% | 1,299 | 3,003 | +131% | 0 | 0 | — |
case-16 | fail→pass | 15,988 | 3,263 | -80% | 1 | 1 | 0% | 2,709 | 3,107 | +15% | 0 | 0 | — |
case-18 | pass→pass | 14,557 | 7,183 | -51% | 1 | 1 | 0% | 2,074 | 3,827 | +85% | 0 | 0 | — |
case-19 | fail→pass | 11,233 | 6,370 | -43% | 1 | 1 | 0% | 1,927 | 3,667 | +90% | 0 | 0 | — |
case-20 | fail→pass | 14,204 | 3,583 | -75% | 1 | 1 | 0% | 2,234 | 3,019 | +35% | 0 | 0 | — |
case-21 | fail→pass | 8,523 | 5,597 | -34% | 1 | 1 | 0% | 1,216 | 3,289 | +170% | 0 | 0 | — |
case-22 | fail→pass | 12,374 | 3,502 | -72% | 1 | 1 | 0% | 1,806 | 3,077 | +70% | 0 | 0 | — |
case-23 | fail→pass | 14,150 | 7,841 | -45% | 1 | 1 | 0% | 2,320 | 3,812 | +64% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +70 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.