Axon 911 Frontend DocsAxon 911 Frontend Docs
Axon 911 Frontend Docs
    Loading navigation…
U
User
Switch to light mode
  1. Overview
  2. Figma Code Connect

Figma Code Connect

How Figma Dev Mode shows real @prepared911/ui snippets, where the templates live, and the check that keeps them honest.

Code Connect replaces the generic Dev Mode snippet for a component instance with one that uses the real @prepared911/ui component and props. The reference file is the Axon 911 Design System in Figma. A Figma component set maps to a code component only when its properties mirror the code props: same names, same values, same defaults.
Where the templates live
Each template sits beside the component it maps and is owned by the same team.
UnitTemplate
Componentpackages/ui/src/components/<id>/<id>.figma.ts
Blockpackages/ui/src/blocks/<id>/<id>.figma.ts
A template is a parserless figma.code template. It starts with three header lines and ends with a default export that has an id:
Map every Figma property that has a code prop. Do not write a prop the component does not declare; if a Figma property has no code prop, record it as a parity mismatch for the Figma owner, or classify it with the conventions below.
The check
pnpm --filter @prepared911/ui code-connect:check runs in ci:gates and check. It fails when:
  • a published component or block has no template and is not in scripts/code-connect/unmapped-baseline.json;
  • a template writes a prop that the catalog does not list for the component;
  • a template names an enum member the code does not export;
  • a template's source or component does not resolve, a header line is missing, or the url is outside the Design System file;
  • a baseline id now has a template, or is no longer published.
A new component ships with a template, or the pull request adds its id to the baseline and says why. The baseline only shrinks.
Conventions for Figma axes
One rule per kind of axis, so every template reads the same way. Read each Figma property once, at the top of the template, with the getter that matches its Figma type.
Figma propertyTemplate
BOOLEANgetBoolean("Loading")
VARIANT whose options are False and True (boolean as a variant: Disabled, Icon only)getEnum("Disabled", { False: false, True: true }). getBoolean is for BOOLEAN properties only; the drift check flags it on a variant. The axis is named for the code prop in sentence case. When there is no such prop, the axis picks a branch of a union (Icon only picks icon plus label instead of children) and the template says so in a constant.
VARIANT with a third option (Toggle: None, Off, On)getEnum to lowercase words ("none", "off", "on"), then derive the props from the words.
VARIANT that is an enum in code (Intent, Variant, Size)getEnum with one entry per Figma option, mapped to the enum member ("ButtonIntent.Accent").
TEXT, SLOTgetString, getSlot. Copy is a copy("...") placeholder, never the Figma text (ADR 0009).
Story-only axes (State, Scene, Story) exist so Figma can draw a look the browser makes by itself. Never invent a prop for one. Apply the first rule that fits:
  1. The component has a prop that forces the look: emit it, and only when no other prop conflicts. Button State=Pressed emits active.
  2. The look is a pseudo-class and no prop forces it: emit one // line at the top of the snippet that names the pseudo-class, and no prop. Button State=Hover and State=Focus do this.
  3. The axis is only a scene or a story (Scene): do not read it, and list it in the header as // story-only=Scene.
A property that stays on the Figma set after another one replaced it goes in the header as // retired=Show label,Pressed. The drift check then stops asking for a mapping and reminds you to remove it from Figma. Anything that a snippet cannot express is written down in the component's table below.
Button
The Button set (node 2072:3) has these properties: Intent, Variant, Size, Disabled, State, Icon only, Toggle, Loading, Show leading, Show trailing, Label, Leading and Trailing. Show label and Pressed are retired: Icon only and Toggle replace them.
FigmaSnippet
Icon only=Trueicon={<Leading />} and label={copy(...)}, no children, no leading or trailing
Icon only=Falsechildren, plus leading and trailing when Show leading and Show trailing are on
Toggle=Onpressed and onPressedChange={handlePressedChange}
Toggle=Offpressed={false} and onPressedChange={handlePressedChange}
Toggle=Noneno toggle props
State=Restnothing
State=Pressedactive (data-pressed, no aria-pressed), except on a toggle, where active is not defined
State=Hovera comment: the browser paints :hover, Button has no prop that forces it
State=Focusa comment: the browser paints :focus-visible, Button has no prop that forces it
Disabled=Truedisabled
Loading onloading and loadingLabel={copy(...)}, except on a toggle, where loading is not defined
What a snippet cannot say: tooltip, type, pressedIcon, defaultPressed, preventFocusOnPress, className and native attributes have no Figma property. Hover and Focus cannot be set in code at all. handlePressedChange and the copy keys are placeholders the developer fills in.
The drift check
code-connect:check reads only the repository, so it cannot see Figma change under a template. code-connect:drift closes that gap:
For every template it reads the live component set (one request per Figma file) and runs the template against the set's real property definitions. It reports:
  • a property the template reads that the set no longer has, or reads with the wrong getter (getBoolean on a variant);
  • a getEnum map that skips an option the set gained, or names one it lost;
  • a Figma property that the template neither reads nor lists under // story-only= or // retired=.
It exits 1 on any of these. A retired property that Figma still declares is only a note. The code-connect-drift workflow runs it every night and on demand with a Figma token read from the FIGMA_ACCESS_TOKEN repository secret, or the Figma repository secret when that one is empty, and a failed run is the alert. When neither secret is set the run skips with a warning that names both. It is not a pull request check and not a required check.
Try a template
This parses every template and prints what would be published. It needs no Figma token. publish --dry-run does need one: it lists the files it would publish, then exits 1 with "Couldn't find a Figma access token". Publishing is a separate, manual step; see the Figma Code Connect decision record in packages/ui/docs/decisions/0030-figma-code-connect.md.
Mapped components
  • Button
Previous
Getting Started
Next
Releases
On this page
Where the templates live
The check
Conventions for Figma axes
Button
The drift check
Try a template
Mapped components