Figma to Code skill for Codex
Match the frame using your own components and tokens. With this skill Codex treats Figma output as a spec rather than code to paste. It fetches the frame's screenshot, design context and variables, builds token and component mapping tables, converts Auto Layout, strokes and text metrics to correct CSS, reuses existing components, and compares a rendered screenshot with the design before reporting remaining deltas.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- figma-to-code Installs to ~/.agents/skills/figma-to-code/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install figma-to-code The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project figma-to-code Installs to .agents/skills/figma-to-code/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/figma-to-code.zipunzip -o figma-to-code.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) figma-to-code PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $figma-to-code, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Implements Figma frames faithfully in an existing codebase using the Figma MCP server (get_design_context, get_variable_defs, get_screenshot) or the REST API, mapping Auto Layout to flexbox and grid, Figma variables to project tokens, components to existing code components, and verifying with side-by-side screenshot comparison. Use when given a Figma link or node-id, asked to build a screen or component "from the design", to match a mockup pixel-close, or to set up Figma MCP in Codex.
Use it when
- You have a Figma link or node-id to implement
- An implementation drifts from its mockup and needs a fidelity pass
- Connecting Codex to Figma through MCP or the REST API
- Mapping Figma variables and components onto an existing codebase
Use something else when
- No design exists and the look must be invented: use frontend-design
- Writing the spec and redlines for engineers: use design-handoff
How to check the result
A screenshot of the implementation at the frame's width matches the Figma export in structure, spacing and type, and no raw hex or off-scale pixel values remain in the new code.
The mistake it prevents
Pasting the generated reference code as-is, which hard-codes values and duplicates components the project already has.
A faithful implementation matches the frame at its design width within a few pixels,
uses the project’s existing components and tokens instead of pasted values, behaves
sensibly at other widths, and covers the states the design implies. Generated code that
looks right but hard-codes #3B82F6 and padding: 23px is a screenshot, not a build.
Setup: getting design data into Codex
Pick the first that works:
| Source | How | Notes |
|---|---|---|
| Figma remote MCP server | Add to ~/.codex/config.toml, then codex mcp login figma | Needs network and OAuth in a browser |
| Figma desktop MCP server | Enable it in the desktop app’s Dev Mode, point Codex at http://127.0.0.1:3845/mcp | Desktop app must be open; selection-based |
| REST API | curl with a personal access token from an env var | Read-only; no design context generation |
| Screenshot or export only | User attaches PNGs and specs | Measure from the image; ask for key values |
# ~/.codex/config.toml
[mcp_servers.figma]
url = "https://mcp.figma.com/mcp"
Check with /mcp in the Codex TUI. The MCP server is network access; in a sandbox with
network off, ask the user to approve or to provide exports.
REST fallback (token stays in the environment, never in files or chat):
# node-id in the URL is 12-345; the API uses 12:345
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/files/$FILE_KEY/nodes?ids=12:345" > .figma/node.json
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/images/$FILE_KEY?ids=12:345&format=png&scale=2" # returns a URL to download
Before writing code
- Read
AGENTS.md, the component directory, the token/theme files and one similar existing screen. Your output must look like it was written by the same team. - Get the exact frame: a link with
node-id, not the whole file. Large frames overflow context; fetch the page outline withget_metadata, then request child frames one at a time. - Collect from MCP for each target node:
get_screenshot: the visual truth to compare against.get_design_context: structure, styles and a reference implementation (React + Tailwind by default). Treat it as a spec, not as code to paste.get_variable_defs: the Figma variables and styles used, with values.get_code_connect_mapwhen Code Connect is set up: which code component each Figma component maps to.
Workflow
- [ ] 1. Fetch screenshot, design context and variables for the frame
- [ ] 2. Map Figma variables and styles to project tokens (table)
- [ ] 3. Map Figma components to existing code components (table)
- [ ] 4. Decompose the frame into sections and new components
- [ ] 5. Implement layout (Auto Layout -> flex/grid), then content, then states
- [ ] 6. Export and optimize assets
- [ ] 7. Compare screenshots at design width; fix deltas
- [ ] 8. Check other widths and states; report gaps
2. Token mapping
Build a table before coding. Every Figma value lands on a token or gets flagged:
| Figma | Value | Project token | Status |
|---|---|---|---|
color/bg/brand | #1D5FD6 | --color-bg-action-default | match |
spacing/lg | 24 | space-6 (24px) | match |
| raw fill | #1E60D5 | --color-bg-action-default | near-duplicate, use token |
| raw padding | 23 | space-6 | off-scale, snap to 24 |
Heading/H2 | Inter 600 28/36 | text-2xl font-semibold | size match, check leading |
Snap off-scale values (1–2 px away) to the nearest token and say so in the report. Values far from any token mean a missing token; ask before adding one.
3. Component mapping
| Figma component | Variant props | Code component |
|---|---|---|
| Button | Type=Primary, Size=M, State=Hover | <Button variant="primary" size="md"> (hover is CSS) |
| Input/With label | Error=True | <TextField invalid errorMessage=...> |
| Card/Pricing | none | new: PricingCard (compose Card) |
Never rebuild an existing component because the Figma layer names differ. Hover, pressed and focus variants in Figma are CSS states, not props.
5. Auto Layout to CSS
| Figma | CSS |
|---|---|
| Horizontal / Vertical | display:flex; flex-direction: row / column |
| Gap between items | gap |
| Spacing mode “Auto” | justify-content: space-between |
| Padding | padding (per side) |
| Hug contents | width: fit-content or default auto sizing; flex: none in a flex parent |
| Fill container (main axis) | flex: 1 1 0; min-width: 0 |
| Fill container (cross axis) | align-self: stretch |
| Fixed width | width only for icons, avatars, controls; otherwise max-width |
| Wrap | flex-wrap: wrap plus row-gap from counter-axis spacing |
| Grid auto layout | display:grid; grid-template-columns |
| Absolute position child | position:absolute inside a position:relative parent |
| Min/max width | min-width / max-width |
| Clip content | overflow: hidden |
Text conversions:
- Line height in % or px: use unitless ratio (
36/28 = 1.286, round to the token). “Auto” meansnormal. - Letter spacing in %: divide by 100 and use
em(-2%->-0.02em). - Text box “fixed height” usually means the designer dragged it; let text flow.
- Text styles map to type tokens; mixed styles in one text layer map to inline elements.
Stroke conversions: inside stroke -> border with box-sizing: border-box; outside ->
outline or box-shadow: 0 0 0 1px; center -> pick inside and note the 0.5 px shift.
Drop shadows map directly to box-shadow (x, y, blur, spread, color); layer blur maps to
filter: blur(), background blur to backdrop-filter: blur().
6. Assets
- Icons: use the project’s icon library if the Figma icon matches by name; otherwise
export SVG, run SVGO, and add to the icon set (see
icon-systems). - Images: export at 2x, convert to AVIF/WebP, set
width/heightto prevent layout shift. Do not ship Figma’s placeholder images as final assets. - MCP may serve assets from a localhost URL; download them into the repo, never ship references to that URL.
7. Compare
Render at the frame’s width, then compare against the Figma screenshot:
npx playwright screenshot --viewport-size "1440,900" --full-page http://localhost:5173/pricing .shots/pricing-impl.png
Open both images (Codex can view local images) and check, in order: overall structure
and section heights, alignment edges, type size and leading, spacing between groups,
colors, radii and shadows, icon sizes. For a numeric check, overlay with 50% opacity in
the browser or use Playwright’s toHaveScreenshot against an exported reference with a
small maxDiffPixelRatio; font rendering differences make exact matches impossible.
8. Beyond the frame
- Designs usually show one width. Derive behavior at 360, 768 and 1280+: what stacks, what wraps, what truncates. If the file has mobile frames, implement those too.
- Implied states: hover, focus-visible, disabled, loading, empty, error, long text.
- If the design breaks accessibility (contrast under 4.5:1, tiny tap targets), implement the accessible version and list the deviation.
Gotchas
- Pasting
get_design_contextoutput wholesale: it uses absolute values and generic markup. Rewrite with project components and tokens. - Requesting a whole page at once: output truncates and quality drops. Go frame by frame.
- Figma fonts not installed in the project: confirm the web font and license; never silently substitute a different family.
- Hidden layers and detached instances in the file produce phantom elements.
- Design width 1440 with a 1200 container: implement the container, not 1440 fixed.
- Desktop MCP server tools act on the current selection; ask the user to select the frame or pass the node-id link.
Output contract
**Frame:** <file name / node-id> **Route/component:** <path>
**Token mapping:** <n> matched, <n> snapped (list), <n> missing (asked/added)
**Components reused:** Button, TextField, Card. **New:** PricingCard (src/...)
**Assets:** <icons/images added and where>
**Fidelity check:** compared at 1440 px; remaining deltas: <font rendering, ...>
**Responsive behavior chosen:** <what stacks at which width>
**Deviations from design:** <with reasons, e.g. contrast fix> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Figma to Code"
short_description: "Match the frame using your own components and tokens"
default_prompt: "$figma-to-code Implement this Figma frame as the new pricing page using our components" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.