Figma to Code
Convert a Figma design into React with @alma-oss/spirit-web-react. Target this repository’s
current main / latest APIs. Do not invent APIs, tokens, breakpoints, routes, data, or assets.
Do not use this skill for:
- Design-system review or handoff reports — use
/spirit:review-figma-design
- Writing or updating Figma Code Connect mappings
- Building or syncing designs in Figma from code
- Vanilla
packages/web markup or other non-React packages
References
- Component selection
- Layout
- Typography
- Cards
- Forms
- Accessibility:
/spirit:accessibility
Source Authority
Resolve conflicts in this order. A later source does not override an earlier one.
- Valid APIs, defaults, deprecations, composition — current Spirit source in this order:
implementation, public types,
packages/web-react/DEPRECATIONS.md, then the component README.
If those disagree, implementation and types win. Do not copy stale README examples.
- Component identity and variants — Figma Code Connect mappings (when present).
- Visual measurements, tokens, assets — Figma design context, variables, and exported assets.
- Behavior, copy, routing, and accessibility — product requirements and the surrounding app.
Normalize Code Connect and generated snippets to the current API: replace removed or deprecated
props, omit values equal to current defaults, and keep design intent. Code Connect is not always
complete or up to date.
Never guess from screenshots. If Figma data, Code Connect, or product context is missing, stop,
list the assumptions, and ask. Do not silently invent breakpoints, icon names, colors, spacing,
accessible names, hrefs, or image sources.
Workflow
1. Acquire the Design
If a Figma design-to-code skill is available, follow it before calling get_design_context.
- Parse a Figma URL when provided (
fileKey, nodeId from node-id with - → :). If the MCP
needs fileKey even for the current selection, ask for a node URL instead of guessing.
- Call
get_design_context first. Treat the returned code as a reference, not paste-ready
output. Adapt it to Spirit React.
- Use supporting tools only when needed: metadata to pick a node, variable definitions for tokens,
screenshots for visual comparison, asset download for images/icons, motion context for animation.
- When the MCP lets you set client context, request React and TypeScript.
2. Map Structure, Not Figma’s Layer Count
Preserve reading order, visual grouping, max-width boundaries, and Spirit composition contracts.
Figma frames often exist for editing, clipping, or naming — they are not 1:1 DOM nodes.
- Use a Spirit component when it owns the needed semantics, behavior, or styling contract.
- Use native semantic elements (
p, ul/ol/li, article, form) when no Spirit component is
responsible, including inside Card body content.
- Do not wrap extra layout components around Card, Dialog, Drawer, or other families that require a
flat, ordered composition.
- Add
Container only where a distinct width constraint exists. See Layout.
- When the design is a form, wrap both the field area and the action buttons in a named native
<form>. See Forms.
3. Choose Components and Props
- Route from Figma component names, text styles, and Code Connect to Spirit components using
Component selection.
- Confirm props against current types and defaults. Omit props that match defaults.
- Heading and
UNSTABLE_DisplayHeading elementType is always required. CardTitle already
defaults to h4. Map Figma Display/* styles to UNSTABLE_DisplayHeading, not Heading.
- Keep exact icon identifiers from Figma:
Icon uses name, IconBox uses iconName. Do not
replace placeholder with a “better” icon.
- Map Figma spacing tokens such as
--global/spacing/space-1400 to spacing="space-1400".
- Map CSS alignment to Spirit
alignmentX / alignmentY (flex-start → left/top,
flex-end → right/bottom). On vertical Flex, align-items is alignmentX. On horizontal
Flex, justify-content is alignmentX.
- Prefer
Box colorScheme for semantic surfaces. Use component-specific color dictionaries; do
not reuse Heading/Text colors on Box, Section, Link, or Button.
Deprecated or removed APIs must not appear in new code. Current traps:
emphasis → fontWeight and isItalic
- Flex
row / column → horizontal / vertical (plus horizontal-reversed when needed)
hideOnCollapse → isDisposable on UncontrolledCollapse
CardDescription, CardTitle headingProps, CardLink isExternal, Button variant
If Code Connect still emits those, tell the user and emit the current API.
4. Responsive Behavior
Spirit breakpoints are mobile from 0, tablet from 48rem, desktop from 80rem
(typically 768px and 1280px). Do not infer a 1024px desktop breakpoint from frame width.
- Encode only transitions supported by Figma frames, variables, or annotations.
- A scalar applies at every breakpoint. Partial objects cascade forward
(
{ mobile: 'small', desktop: 'large' } keeps small on tablet).
- If only one frame is provided, match that frame. Do not invent other breakpoints.
- Figma layout guides (column counts, gutters) are often absent from MCP output. If the user
mentions them, implement with Grid and ask when the column count is unclear.
5. Accessibility
Apply /spirit:accessibility. This step is a semantics pass, not a prop re-check. Cover
landmarks, heading outline, list markup, and role/elementType from meaning — not from the Figma
component name.
- Choose
elementType from document outline, not from visual size. Visual style and semantics
are independent.
- Prefer not to skip heading ranks; fix the outline rather than forcing a visual size onto the
wrong tag. Keep the page
h1 out of an anonymous <section>. See Layout.
- Repeating cards or items that are a list in meaning use
ul/ol with li, not a bare Flex/Grid
of divs.
- Preserve DOM source order. If Flex
horizontal-reversed or similar changes visual order, keep
the accessible reading order correct.
- Give controls accessible names, states, and relationships (label, description, error, required).
- Decide decorative vs informative for images and icons; do not invent alt text from pixels.
- Do not nest interactive elements. A stretched CardLink may coexist with footer buttons because
Card raises those controls; do not add extra wrapping links.
- Honor reduced-motion and focus management for Dialog, Drawer, Dropdown, and similar overlays.
- Personal-data autocomplete and checkbox-revealed field ARIA live in
/spirit:accessibility.
6. Assets
- Prefer Figma MCP assets, files already in the repo, or an explicit product image source.
- MCP asset URLs expire; for committed code, download the bytes or wire the app’s image source.
- Do not use
picsum.photos or other random remote placeholders.
- If no asset is available, use a clearly labeled local placeholder and flag it in the handoff.
Validation
Validation is required, not optional. After implementation:
- Confirm every Spirit prop against current types. Fix invalid token names and removed APIs.
- Run linters on edited files (
ReadLints in this repo) and fix new issues.
- Run relevant unit tests when the change lives in this repository.
- Render and compare. In this repo: start Vite with
make start or yarn start
(http://localhost:3456/packages/web-react/), inspect with Playwright MCP, then use Docker
Make targets (make test-e2e, make test-e2e-a11y) for suite runs. Do not skip visual
comparison silently.
- Re-fetch Figma context if the implementation drifted from tokens, icons, or composition.
If a gate cannot run, say so in the handoff (verified vs unverified) and list what was skipped.
Implementation Checklist
- [ ] Design acquired with
get_design_context; missing data flagged, not guessed
- [ ] Code Connect intent kept; snippets normalized to current APIs and defaults
- [ ] Icon identifiers unchanged (
Icon name, IconBox iconName)
- [ ] No deprecated or removed APIs (
emphasis, row/column, hideOnCollapse, CardDescription)
- [ ] Semantic structure preserved; Figma-only wrapper frames not copied 1:1
- [ ] Card vs Box decided by composition (artwork/title/footer/link), not Figma name
- [ ] Page
h1 not nested in an anonymous Section; repeating cards use list markup
- [ ] Heading
elementType set; Display/* → UNSTABLE_DisplayHeading; typography mapped by text style, not by size alone
- [ ] Form wraps fields and actions; submit first in DOM; personal-data autocomplete per
/spirit:accessibility
- [ ] Responsive values exist only where Figma supports them; breakpoints are Spirit tokens
- [ ] Assets are stable; alt/decorative decisions are explicit
- [ ] Accessibility, TypeScript, lint, render, and (when in this repo) tests/a11y checks ran — or the handoff is marked unverified
- [ ] Unclear patterns were asked about instead of improvised
1---2name: spirit-figma-to-code3description: Implement production React UI from a Figma design using @alma-oss/spirit-web-react. Use when the requested output is React code and a Figma node, URL, selection, screenshot, or specification is provided. Do not use for design review, Code Connect authoring, code-to-Figma workflows, or non-React Spirit packages.4---56# Figma to Code78Convert a Figma design into React with `@alma-oss/spirit-web-react`. Target this repository’s9current main / latest APIs. Do not invent APIs, tokens, breakpoints, routes, data, or assets.1011Do **not** use this skill for:1213- Design-system review or handoff reports — use `/spirit:review-figma-design`14- Writing or updating Figma Code Connect mappings15- Building or syncing designs **in** Figma from code16- Vanilla `packages/web` markup or other non-React packages1718## References1920- [Component selection](references/component-selection.md)21- [Layout](references/layout.md)22- [Typography](references/typography.md)23- [Cards](references/cards.md)24- [Forms](references/forms.md)25- Accessibility: `/spirit:accessibility`2627## Source Authority2829Resolve conflicts in this order. A later source does not override an earlier one.30311. **Valid APIs, defaults, deprecations, composition** — current Spirit source in this order:32 implementation, public types, `packages/web-react/DEPRECATIONS.md`, then the component README.33 If those disagree, implementation and types win. Do not copy stale README examples.342. **Component identity and variants** — Figma Code Connect mappings (when present).353. **Visual measurements, tokens, assets** — Figma design context, variables, and exported assets.364. **Behavior, copy, routing, and accessibility** — product requirements and the surrounding app.3738Normalize Code Connect and generated snippets to the current API: replace removed or deprecated39props, omit values equal to current defaults, and keep design intent. Code Connect is not always40complete or up to date.4142Never guess from screenshots. If Figma data, Code Connect, or product context is missing, stop,43list the assumptions, and ask. Do not silently invent breakpoints, icon names, colors, spacing,44accessible names, hrefs, or image sources.4546## Workflow4748### 1. Acquire the Design4950If a Figma design-to-code skill is available, follow it before calling `get_design_context`.51521. Parse a Figma URL when provided (`fileKey`, `nodeId` from `node-id` with `-` → `:`). If the MCP53 needs `fileKey` even for the current selection, ask for a node URL instead of guessing.542. Call `get_design_context` first. Treat the returned code as a **reference**, not paste-ready55 output. Adapt it to Spirit React.563. Use supporting tools only when needed: metadata to pick a node, variable definitions for tokens,57 screenshots for visual comparison, asset download for images/icons, motion context for animation.584. When the MCP lets you set client context, request React and TypeScript.5960### 2. Map Structure, Not Figma’s Layer Count6162Preserve reading order, visual grouping, max-width boundaries, and Spirit composition contracts.63Figma frames often exist for editing, clipping, or naming — they are not 1:1 DOM nodes.6465- Use a Spirit component when it owns the needed semantics, behavior, or styling contract.66- Use native semantic elements (`p`, `ul`/`ol`/`li`, `article`, `form`) when no Spirit component is67 responsible, including inside Card body content.68- Do not wrap extra layout components around Card, Dialog, Drawer, or other families that require a69 flat, ordered composition.70- Add `Container` only where a distinct width constraint exists. See [Layout](references/layout.md).71- When the design is a form, wrap **both** the field area and the action buttons in a named native72 `<form>`. See [Forms](references/forms.md).7374### 3. Choose Components and Props75761. Route from Figma component names, text styles, and Code Connect to Spirit components using77 [Component selection](references/component-selection.md).782. Confirm props against current types and defaults. Omit props that match defaults.793. Heading and `UNSTABLE_DisplayHeading` `elementType` is always required. CardTitle already80 defaults to `h4`. Map Figma **Display/\*** styles to `UNSTABLE_DisplayHeading`, not `Heading`.814. Keep exact icon identifiers from Figma: `Icon` uses `name`, `IconBox` uses `iconName`. Do not82 replace `placeholder` with a “better” icon.835. Map Figma spacing tokens such as `--global/spacing/space-1400` to `spacing="space-1400"`.846. Map CSS alignment to Spirit `alignmentX` / `alignmentY` (`flex-start` → `left`/`top`,85 `flex-end` → `right`/`bottom`). On vertical Flex, `align-items` is `alignmentX`. On horizontal86 Flex, `justify-content` is `alignmentX`.877. Prefer `Box colorScheme` for semantic surfaces. Use component-specific color dictionaries; do88 not reuse Heading/Text colors on Box, Section, Link, or Button.8990Deprecated or removed APIs must not appear in new code. Current traps:9192- `emphasis` → `fontWeight` and `isItalic`93- Flex `row` / `column` → `horizontal` / `vertical` (plus `horizontal-reversed` when needed)94- `hideOnCollapse` → `isDisposable` on UncontrolledCollapse95- `CardDescription`, `CardTitle headingProps`, `CardLink isExternal`, Button `variant`9697If Code Connect still emits those, tell the user and emit the current API.9899### 4. Responsive Behavior100101Spirit breakpoints are `mobile` from `0`, `tablet` from `48rem`, `desktop` from `80rem`102(typically 768px and 1280px). Do not infer a 1024px desktop breakpoint from frame width.103104- Encode only transitions supported by Figma frames, variables, or annotations.105- A scalar applies at every breakpoint. Partial objects cascade forward106 (`{ mobile: 'small', desktop: 'large' }` keeps `small` on tablet).107- If only one frame is provided, match that frame. Do not invent other breakpoints.108- Figma layout guides (column counts, gutters) are often absent from MCP output. If the user109 mentions them, implement with Grid and ask when the column count is unclear.110111### 5. Accessibility112113Apply `/spirit:accessibility`. This step is a **semantics pass**, not a prop re-check. Cover114landmarks, heading outline, list markup, and role/`elementType` from meaning — not from the Figma115component name.116117- Choose `elementType` from document outline, not from visual size. Visual style and semantics118 are independent.119- Prefer not to skip heading ranks; fix the outline rather than forcing a visual size onto the120 wrong tag. Keep the page `h1` out of an anonymous `<section>`. See [Layout](references/layout.md).121- Repeating cards or items that are a list in meaning use `ul`/`ol` with `li`, not a bare Flex/Grid122 of `div`s.123- Preserve DOM source order. If Flex `horizontal-reversed` or similar changes visual order, keep124 the accessible reading order correct.125- Give controls accessible names, states, and relationships (label, description, error, required).126- Decide decorative vs informative for images and icons; do not invent alt text from pixels.127- Do not nest interactive elements. A stretched CardLink may coexist with footer buttons because128 Card raises those controls; do not add extra wrapping links.129- Honor reduced-motion and focus management for Dialog, Drawer, Dropdown, and similar overlays.130- Personal-data autocomplete and checkbox-revealed field ARIA live in `/spirit:accessibility`.131132### 6. Assets133134- Prefer Figma MCP assets, files already in the repo, or an explicit product image source.135- MCP asset URLs expire; for committed code, download the bytes or wire the app’s image source.136- Do not use `picsum.photos` or other random remote placeholders.137- If no asset is available, use a clearly labeled local placeholder and flag it in the handoff.138139## Validation140141Validation is required, not optional. After implementation:1421431. Confirm every Spirit prop against current types. Fix invalid token names and removed APIs.1442. Run linters on edited files (`ReadLints` in this repo) and fix new issues.1453. Run relevant unit tests when the change lives in this repository.1464. Render and compare. In this repo: start Vite with `make start` or `yarn start`147 (`http://localhost:3456/packages/web-react/`), inspect with Playwright MCP, then use Docker148 Make targets (`make test-e2e`, `make test-e2e-a11y`) for suite runs. Do not skip visual149 comparison silently.1505. Re-fetch Figma context if the implementation drifted from tokens, icons, or composition.151152If a gate cannot run, say so in the handoff (`verified` vs `unverified`) and list what was skipped.153154## Implementation Checklist155156- \[ \] Design acquired with `get_design_context`; missing data flagged, not guessed157- \[ \] Code Connect intent kept; snippets normalized to current APIs and defaults158- \[ \] Icon identifiers unchanged (`Icon name`, `IconBox iconName`)159- \[ \] No deprecated or removed APIs (`emphasis`, `row`/`column`, `hideOnCollapse`, `CardDescription`)160- \[ \] Semantic structure preserved; Figma-only wrapper frames not copied 1:1161- \[ \] Card vs Box decided by composition (artwork/title/footer/link), not Figma name162- \[ \] Page `h1` not nested in an anonymous Section; repeating cards use list markup163- \[ \] Heading `elementType` set; Display/\* → `UNSTABLE_DisplayHeading`; typography mapped by text style, not by size alone164- \[ \] Form wraps fields and actions; submit first in DOM; personal-data autocomplete per `/spirit:accessibility`165- \[ \] Responsive values exist only where Figma supports them; breakpoints are Spirit tokens166- \[ \] Assets are stable; alt/decorative decisions are explicit167- \[ \] Accessibility, TypeScript, lint, render, and (when in this repo) tests/a11y checks ran — or the handoff is marked unverified168- \[ \] Unclear patterns were asked about instead of improvised