Getting Started
Figma to code
Design files and code drift apart by default. What stops it is not tooling — it is agreeing on names, and agreeing on which side owns each decision.
Who owns whatPermalink to this section
Most handoff problems are ownership problems in disguise. When two sources both claim to be authoritative, they will disagree, and nobody will know which to trust.
| Decision | Owned by | Why |
|---|---|---|
| Token values | Code | src/tokens is the source. The Figma library mirrors it, never the other way round. |
| Token names | Shared | Renaming requires agreement on both sides — a name that only exists in one place is a future mismatch. |
| Component variants | Shared | The variant set is a contract. Adding one is a system change, not a file change. |
| Component states | Code | Hover, focus, disabled and loading are behavioural. The implementation defines them. |
| Layout and composition | Design | How components are arranged on a page is a design decision every time. |
| Interaction and motion | Code | Timing and easing are tokens, applied by the components themselves. |
| Accessible names | Shared | The label a screen reader announces is content, and content is written, not styled. |
Names match exactlyPermalink to this section
A token called brand-primary in code is called brand-primary in Figma. Not “Brand / Primary”, not “Purple 600” — the same string, so that a design annotation is directly actionable.
Figma variable
- brand-primary
- surface-raised
- border-default
- space-16
- radius-lg
CSS custom property
- --brand-primary
- --surface-raised
- --border-default
- --space-16
- --radius-lg
The test
Someone should be able to read a token name off a design file and search for it in the codebase. If that search fails, the naming has already drifted.
Reading a specPermalink to this section
A design file communicates intent. Translating it means mapping measurements back onto the nearest token, not reproducing them literally.
Spec says 22px
→ space-24
Do — snap to the nearest token
A 2px difference is invisible; an off-scale value is permanent technical debt that spreads by copy-paste.
Spec says 22px
→ padding: 22px
Don't — reproduce measurements literally
One arbitrary value becomes ten as soon as the component is duplicated, and the scale stops meaning anything.
| Spec value | Nearest token | Use |
|---|---|---|
| 3px, 5px | space-4 | 4px |
| 14px, 18px | space-16 | 16px |
| 22px, 26px | space-24 | 24px |
| #e96516 | brand-primary | #c94e0c |
| 11px radius | radius-md | 12px |
| 200ms | duration-fast or duration-normal | 150ms or 240ms |
If a spec value is genuinely between two tokens and the difference matters, that is worth a conversation — either the scale has a gap, or the design is making a distinction the system does not support yet. Both are useful things to find out.
What does not transferPermalink to this section
Some things exist only in code, and design files should not attempt to specify them.
Focus rings
Applied globally by :focus-visible. A design file showing a custom focus treatment is describing a bug.
Hover and pressed states
Defined by the component's variants. Redrawing them in Figma creates two sources of truth.
Loading states
The spinner, the aria-busy attribute and the interaction blocking are behavioural.
Responsive collapse
Which parts of the interface disappear at each breakpoint is documented in the layout foundation.
Reduced motion
There is no static representation of an animation that has been removed.
Live regions
Whether a change is announced is not visible in any design file.
Implementing a screenPermalink to this section
The order that produces the fewest surprises.
- Identify the componentsWalk the design and name every part using system component names. Anything that has no name is either a composition of existing components or a gap worth raising before any code is written.
- Build the structure with real semanticsHeadings in order, lists as lists, buttons as buttons. Getting this right first is far cheaper than retrofitting it after the styling looks correct.
- Apply spacing from the scaleMap every measurement to the nearest step. Flag anything that will not snap.
- Compose components, do not restyle themIf a component needs to look different, that is a variant request. Overriding a component's internals with className is how a design system stops being one.
- Check the states the design did not showEmpty, loading, error, long content, and the narrowest supported width. Design files almost never include all five.
- Run the accessibility checklistKeyboard path, focus order, labels, contrast, reduced motion. The list is on the accessibility foundation page.
Annotating a designPermalink to this section
What to write on a design file so that implementing it does not require a meeting.
Component: Button
Variant: destructive
Size: md
Icon: Trash2 (leading)
Label: "Delete workspace"
Behaviour: Opens the delete confirmation dialog.
Does not delete directly.
Empty: n/a
Loading: While the delete request is in flight,
loading={true} — label stays visible.
Error: Failure message appears above the dialog
footer; the dialog stays open.
A11y: Dialog title names the workspace.
Cancel receives initial focus.Keeping the library in syncPermalink to this section
The Figma library mirrors the code, and the sync runs in one direction only.
- A token change lands in src/tokens and ships in a release.
- The release notes list every changed token by name.
- The Figma variables are updated to match, in the same week, by whoever owns the library.
- A component's variant set changes only through the contribution process — never directly in Figma.
- Anything present in Figma but absent from code is marked as exploratory, so it is never mistaken for a spec.
One direction, always
When a designer changes a variable in Figma and the code is not updated, the file becomes a proposal rather than a specification — and the next person to implement from it will build the wrong thing. Token changes start in code.