A study by Victor MaragioglioFigma, development, and the AI-native design–development loopvictormaragioglio.com
Figma
  • Frame
  • Auto Layout
  • Variable
  • Variant
  • Instance
  • Component
  • Prototype

TheHandoff

Code
  • variant="primary"prop
  • const [loading, setLoading]state
  • <div>div
  • function Button()component
  • --color-action-primaryCSS variable
  • display: flexflex
  • <Button />instance

Two vocabularies for the same product, separated by a gap the industry calls the handoff. This study follows what happens to that gap when Figma, code, design systems and AI agents start reading the same context.

or scroll · Space to play · 31 scenes
01

The handoff

Designer → Figma → Spec → Developer → Code
01DesignerMake paying feel safe and fast(Intent, intact)Lock button while paying(Behavior, intact)OrderSummary(Naming, intact)Stack below 640px(Responsive logic, intact)Empty · error · loading(Edge states, intact)Uses Button, Field(Relationships, intact)
02Figmaimplied by hierarchy(Intent, implied)one prototype path(Behavior, implied)Frame 131(Naming, ambiguous)a 375px frame(Responsive logic, implied)error frame only(Edge states, implied)detached instance(Relationships, ambiguous)
03Specnot in spec(Intent, lost)“see prototype”(Behavior, ambiguous)“the top box”(Naming, ambiguous)“see mobile frame”(Responsive logic, ambiguous)“error: red”(Edge states, ambiguous)“blue button”(Relationships, ambiguous)
04Developer—(Intent, lost)disable it, probably?(Behavior, ambiguous)Box1(Naming, ambiguous)guess 768px(Responsive logic, ambiguous)—(Edge states, lost)new button(Relationships, lost)
05Code—(Intent, lost)onClick={submit}(Behavior, ambiguous).box-1(Naming, lost)@media (max-width: 768px)(Responsive logic, ambiguous)—(Edge states, lost)<button class="btn2">(Relationships, lost)
  • carried
  • implied
  • ambiguous
  • lost

Every handoffintroducesinterpretation.

02

Visual similarity is not structure

The same pixelscan containvery differentinformation.

A and B render identically. Switch to structure to see what a developer — or an agent — actually receives.

A
B
03

Pixels are not context

an agent receives a screenshot
screenshot.png — 1 file, 0 structure
blue?color.action.primary12px?space.3008px?radius.control
Agent infers
  • whatbutton?link?div?component Button
  • fillblue?#2D48CA?indigo?#3050D0?token color.action.primary
  • padding12px?14px?11px?spacing space.300
  • radius8px?6px?7px?radius radius.control
  • type16px / 600?15px / 500?16px / bold?size medium
  • stateunknownhover?default?state default
structured design context
Button / Primarycomponent: Buttonsize: mediumstate: defaulttoken: color.action.primaryspacing: space.300radius: radius.control

More context.Less guessing.

04

The translation problem

design ⇄ code, alternating direction
  1. Auto Layoutflex / grid
  2. Variabletoken / custom property
  3. Variantprop / variant API
  4. Instancecomponent instance
  5. Modetheme / configuration
  6. Prototyperuntime behavior
Figma · Auto Layout
Row
16Name16Action24
→ horizontal⊟ center
Code · flex / grid
.row {  display: flex;  flex-direction: row;  gap: var(--space-400);  padding: var(--space-600);  align-items: center;}

gap Hug ≈ fit-content. Fill ≈ flex: 1. Close — not equal.

Figma · Variable
Collection · BrandValue
◐color/action/primary #2C47C9
◐color/surface/default #FFFFFF
Code · token / custom property
:root {  --color-action-primary: #2C47C9;} .button {  background: var(--color-action-primary);}

gap A variable is a value with a scope. A token is a decision with a name.

Figma · Variant
❖ Button — properties
Variant
Primary ▾
Size
Large ▾
Loading
◯ false
Code · prop / variant API
type ButtonProps = {  variant: "primary" | "secondary";  size: "sm" | "md" | "lg";  loading?: boolean;};

gap Variants enumerate what exists. Props constrain what is allowed.

Figma · Instance
❖ Button main component
◇label: Place order
◇label: Save address
# Button (detached) — a fork
Code · component instance
import { Button } from "@/ui/Button"; <Button>Place order</Button><Button>Save address</Button>

gap Overrides become props. A detached instance becomes a fork nobody tracks.

Figma · Mode
ThemeLightDark
color/surface FFFFFF 141412
color/text 17171A EDEBE5
Code · theme / configuration
[data-theme="dark"] {  --color-surface: #141412;  --color-text: #EDEBE5;}

gap Modes switch values. Themes also switch context: imagery, contrast, density.

Figma · Prototype
CheckoutOn tap → NavigateConfirmationloading?error?
Code · runtime behavior
async function onSubmit() {  setState("loading");  const res = await placeOrder(cart);  if (!res.ok) return setState("error");  router.push("/confirmation");}

gap A prototype shows one path. Runtime has to handle all of them.

Not identical. Related intent, different representations.

05

Auto Layout → CSS

one state, two representations
Row · Auto layout
AvatarNameAction

Figma aligns in space.CSS aligns on axes.

Change direction: justify-content and align-items trade places.

Figma propertyCSS
Direction
flex-direction: row;
gap: var(--space-400);/* 16px */
Alignment
justify-content: flex-start;align-items: center;
Padding
padding: var(--space-400);
Frame width
width: fit-content;
“Name” width
.name { flex: none; }
06

Variables → tokens

one change, travelling through every representation

One decision.Five representations.

Change color/brand/primary
  1. 01 · Figma variable
    ◐ color/brand/primary #2C47C9
  2. 02 · Design token
    "color.action.primary": {
      $value: "{color.brand.primary}"
      // resolves to #2C47C9
    }
  3. 03 · CSS custom property
    --color-action-primary:
      #2C47C9;
  4. 04 · Component
    <Button variant="primary" />
    // bg: var(--color-action-primary)
    
  5. 05 · Runtime
    Change address

now color/brand/primary = Cobalt · production renders Cobalt

07

The component already exists

user → agent

Create this button.

agent output · without repository context
src/checkout/PlaceOrderButton.tsx
export function PlaceOrderButton({ onClick }) {  return (    <button      onClick={onClick}      style={{        background: "#2D48CA",        padding: "14px 22px",        borderRadius: 7,        fontWeight: 600,      }}    >      Place order    </button>  );}
new componentnew colornew spacingnew radiusnew behavior — no loading, no disabled
src/checkout/CheckoutForm.tsx — with repository context
import { Button } from "@/ui/Button"; <Button variant="primary" size="lg" type="submit">  Place order</Button>
repository
  • src/
  • ui/
  • Button.tsx
  • Button.test.tsx
  • tokens.css
  • checkout/
  • CheckoutForm.tsx
  • PlaceOrderButton.tsx + new
already in production
<Button  variant="primary"  size="lg"/>
loading · disabled · focus ring · tested

+16 lines · 1 new file · 4 new values+4 lines · 0 new files · 0 new values

The best componentan agent generatesmay be the oneit doesn’t generate.

08

Design system as context

from two readers to three
reads visuals, usageDesigners
Design systemcomponents · tokens · guidance+ names · types · rules, written down
reads APIs, tokens, docsDevelopers
reads only what is explicitAgents

A designer can infer intent from a picture. A developer can ask a colleague. An agent reads names, types and metadata — and nothing else.

blue.500color.action.primary
a blue. used for what?the primary action color → use on submit
Frame 892CheckoutSummary
a frame. could be anything.a checkout's summary → expects items and totals
padding: 12pxspace.control.inline
twelve pixels. a rule, or a coincidence?inline space inside every control

Semanticsare machine-readableintent.

09

Design for humans / structure for machines

Designfor humans.Structurefor machines.

One checkout, two readers. Drag the seam: left of it is what a person sees, right of it is what an agent can read.

humanmachine

Machine view of the checkout: CheckoutHeader (step 3 of 3); OrderSummary containing LineItems and Totals; ShippingAddress (filled, editable through AddressDialog); PaymentMethod (RadioGroup, card selected); Button (primary, large, default then loading, submits CheckoutForm).

10

From prompt to context

each line of context removes possible outputs
prompt

“make this look like the Figma”

context
  1. component: Button
  2. variant: primary
  3. size: medium
  4. token: color.action.primary
  5. implementation: src/ui/Button.tsx
  6. interaction: submit
  7. state: loading

Ruled out by context: <a> · <div onClick> · secondary · ghost · lg · sm · #3050D0 · #1E3A8A · new <SubmitBtn> · type="button"

Prompting is not the interface.Context is.

11

The context bridge

the agent traverses structure, not pixels

Not looking.Traversing.

With a structured bridge to Figma and access to the repository, an agent queries names, variables, layout and metadata on one side, and finds components, props, tokens, patterns and tests on the other.

Figma · design context
nameSubmitOrder
variablecolor/action/primary
layoutAuto layout ↓ · gap 16
componentButton / Primary / Large
metadata“Locks while the order submits”
hierarchyCheckout › PaymentMethod › SubmitOrder
Agent · queries
01What is SubmitOrder?
02Which token is this fill?
03Which layout primitive?
04Which props does this variant set?
05What happens on submit?
06Where does it live?
screenshot.png — reference, not specification
Codebase · implementation context
componentsrc/ui/Button.tsx
token--color-action-primary
pattern<Stack gap="400">
propsButtonProps { variant, size, loading }
test“disables submit while loading”
patternforms/useCheckoutForm.ts

plan Reuse Button (primary · lg · loading) inside <Stack gap="400"> in CheckoutForm, bound to useCheckoutForm. No new components. No new values.

12

Code Connect / component mapping

many design instances, one production component
Figma · instances
mapping · property → prop
Variant = Primaryvariant="primary"
Size = Largesize="lg"
Button.figma.tsx
figma.connect(Button, "Button", {  props: {    variant: figma.enum("Variant", {      Primary: "primary", Secondary: "secondary",    }),    size: figma.enum("Size", { Large: "lg", Medium: "md" }),    loading: figma.boolean("Loading"),  },  example: (props) => <Button {...props} />,});
src/ui/Button.tsx1 component · 1 of 4 instances mapped
what an agent or developer receives
<Button variant="primary" size="lg">  Place order</Button>

Four instances.One component. Zero lookalikes.

13

Duplication vs reuse

“build five screens”

system contextoffon

11 components · 9 values3 components · 2 token groups

Without system context, five screens produce five slightly different buttons, three modal patterns, three input components, five spacing values and four blues. With system context they consolidate into a shared Button, Dialog and Input, and shared space and color tokens.

The graph collapseswhen the system is in context.

14

Drift

two representations, one component, no sync process

Design = codeΔ 1 · radiusΔ 2 · radius · loadingΔ 3 · radius · loading · colorSynced

  • Figma · Button / Primary
  • radius 814
  • fill local #2C47C9color.action.primary
  • states defaultdefault · loading
Place order
Place order
  • Code · <Button />
  • radius 814
  • bg var(--color-action-primary) #2C47C9var(--color-action-primary) #1F6B4A
  • states defaultdefault · loading
  1. 01 · designradius.control 8 → 14code still renders 8
  2. 02 · codeadds a loading statedesign has no loading variant
  3. 03 · tokenscolor.action.primary → #1F6B4Adesign uses a detached local fill
  4. 04 · reviewreconcile: adopt 14, add loading variant, relink fillsynced — until the next change

Sync is not a state.It is a process.

15

Source of truth

Where isthe sourceof truth?

Every candidate is right — about something different. Hover a source to see what it is authoritative for, and what it borrows from the others.

Authority matrix: each source is authoritative for one kind of truth and references others.
truth ↓ · source →FigmaCodeDesign systemTokensDocumentationProduction
Design intentwhat we meantauthoritativereferencesreferencesnot concernedreferencesnot concerned
Runtime behaviorwhat actually runsreferencesauthoritativereferencesnot concernedreferencesreferences
Rules & usagehow things may be combinedreferencesreferencesauthoritativereferencesreferencesnot concerned
Shared decisionsvalues everyone agreed onreferencesreferencesreferencesauthoritativenot concernedreferences
Rationalewhy it is this wayreferencesnot concernedreferencesnot concernedauthoritativenot concerned
Actual experiencewhat users actually getnot concernedreferencesnot concernednot concernednot concernedauthoritative

There may not beone source of truth.

There may bea network ofauthoritative sources.

16

Design → code

intermediate context, made visible

screenshot → magic → code

Intent → structure → system → context → implementation

element01 Design structure02 System mapping03 Component discovery04 Implementation05 Runtime
PaymentForm#PaymentForm · auto layout ↓ 16Stack · gap space.400✓ src/ui/Stack.tsx<Stack gap="400">
CardNumber◇Field / Card numberField · type card✓ src/ui/Field.tsx<Field label="Card number" />4242 4242 4242 4242
Expiry◇Field / ExpiryField · type expiry✓ src/ui/Field.tsx<Field label="Expiry" />09 / 28
CardBrandIcon▭Visa logo (image)no system match✗ not found — flag for review{/* awaiting review */}?
SubmitOrder◇Button / Primary / LButton · primary · lg · color.action.primary✓ src/ui/Button.tsx<Button type="submit" size="lg">

Same five steps as the handoff.Opposite direction of information.

In scene 01 meaning leaked out at every step. Here every step adds context — and the one element the system cannot place is flagged for a person, not invented.

17

Code → design

projecting a production component back into design
src/ui/ProductCard.tsx · production
type ProductCardProps = {  product: Product;  size?: "compact" | "regular";  state?: "default" | "soldOut";  onAdd?: () => void;}; // surface.raised · radius.surface · space.400// @container (min-width: 360px) → regular
  1. 1props → component propertiesmapped
  2. 2states → variantshover exists in CSS, not in the set — review
  3. 3tokens → variable bindingsmapped
  4. 4responsive → variantscontainer query becomes two fixed variants — an approximation
  5. 5onAdd → ?behavior has no design property — stays in code
Figma · ❖ ProductCard — component set
1
Size
compact · regular
State
default · soldOut
Product
text, image
2
Field notebook$12.00AddField notebookSold outNotify meField notebook$12.00Field notebookSold outhover?
3Fill → surface.raisedRadius → radius.surfacePadding → space.400
4< 360 → compact≥ 360 → regular≈ fluid in code, stepped in design
5onAdd() — not representable as a property

The flow is becomingbidirectional.

Code can describe itself to design. It cannot decide what design should do with that description. The projection needs a reviewer.

18

The loop

  1. i A chain. Each step waits for the previous one.
  2. ii Production feeds back into design. The chain closes.
  3. iii Every participant can read and write every other. No first step.

The handoffis becominga loop.

Design, system, agent, code, production and feedback form a chain that closes into a loop, then becomes four mutually connected participants: Figma, design system, agent and codebase.

19

AI-native design

What changes whendesigners also workwith agents?

Not a button that says generate. An agent that works on the file’s structure, proposes a change, and leaves the decision where it belongs.

agent proposes
  • Frame 127Checkout
  • Frame 131OrderSummary
  • Group 12ShippingAddress
  • Rectangle 89SubmitOrder
designer decides

Do these names match how the team talks about the product?

20

AI-native development

same model, different context

Agent A · screenshot + prompt

  • screenshotcheckout.png
  • prompt“build the checkout button”
agent
<div  onClick={submit}  style={{ background: "#2D48CA",    padding: "14px 22px", borderRadius: 7 }}>  Place order</div>
  • div — not focusable, not a button
  • #2D48CA — close to the token, not the token
  • no loading state — double submits

Agent B · structured context

  • figma contextSubmitOrder → Button / Primary / L
  • design-system docsforms.md · submit buttons lock while pending
  • repositorysrc/checkout/CheckoutForm.tsx
  • component library@/ui/Button · @/ui/Stack
  • typesCheckoutFormValues · Money
  • testscheckout.test.tsx · “disables submit while loading”
agent
import { Button } from "@/ui/Button"; <Button type="submit" size="lg"  loading={form.isSubmitting}>  Place order</Button>
  • existing component, existing token
  • loading bound to form state, as the test expects

AI-native developmentis a context problem.

21

The agent should ask the system

before writing a line

userbuild this screen

  1. agentDoes this component already exist?Yes — Button, primary, lg.
  2. agentWhich token represents this color?color.action.primary
  3. agentWhat responsive behavior is expected?Single column below 640px; summary collapses.
  4. agentWhat should appear while loading?Button loading, form disabled.
  5. agentWhich accessibility pattern applies?Errors via aria-describedby + live region.
  • Component library@/ui
  • Tokenstokens.json
  • FigmaCheckout / Mobile
  • Testscheckout.test.tsx
  • Docsa11y/forms.md
then, and only then, write
<Stack gap="400">  <Field label="Card number" {...field("card")} />  <Button type="submit" size="lg" loading={pending}>    Place order  </Button></Stack>
discoverwrite

Generate less.Discover more.

22

Semantic quality

naming quality → agent comprehension

Namingis part ofthe interface.

  1. Frame 179CheckoutForm
  2. Rectangle 22PaymentMethod
  3. Group 4SubmitOrder
  4. Blue 500color.action.primary
  5. Spacing 16space.control.inline
23

Design systems as agent APIs

one entry point, three readers · tokens
Humanbrowses the color page
Codevar(--color-action-primary)
Agenttokens.get("color.action.primary") → { value: "#2C47C9", use: "primary actions" }

The design systemis becomingan API for intent.

24

What AI should not do

consequences, not rules
  1. 01Invent components unnecessarilysrc/ui/Button.tsxsrc/checkout/BlueButton.tsxTwo buttons to maintain. Only one gets the next fix.
  2. 02Ignore existing tokens- background: var(--color-action-primary);+ background: #2D48CA;The next rebrand misses this screen.
  3. 03Duplicate patternsThree ways to confirm. Users learn none of them.
  4. 04Remove accessibility behavior- <button type="submit">+ <div onClick={submit}>Tab skips it. A screen reader announces nothing.
  5. 05Guess interactionson click → ?The click does something. Nobody decided what.
  6. 06Ignore responsive logicCorrect at 1440px. Broken at 375px.
  7. 07Replace semantics with arbitrary values- padding-inline: var(--space-control-inline);+ padding-inline: 13px;The value survives. The reason doesn’t.
  8. 08Treat screenshots as complete specificationsdefaultloadingerroremptydisabledOnly the state that was photographed exists.
25

Human judgment

  1. Is this pattern appropriate?informs an agent can list where the pattern is already used
  2. Does this interaction make sense?informs an agent can prototype three versions of it
  3. Should this component exist?informs an agent can find the four near-duplicates
  4. Is this accessible?informs an agent can run the contrast and ARIA checks
  5. Does this solve the user problem?informs an agent can summarise the research notes
  6. Should this become part of the system?informs an agent can count how often it was rebuilt

Automationdoes not removejudgment.

Agents can gather the evidence for every one of these questions. None of them is answered by the evidence alone.

26

Who owns the decision?

AI changes the boundary.It does not erase responsibility.

Regions move. The accountable name on each decision does not.

Decisions, who is accountable, and who executes
decisionaccountableexecuted by
requirementsproductproduct
intentdesignerdesigner
interactiondesignerdesigner
visual hierarchydesignerdesigner
runtimeengineerengineer
performanceengineerengineer
architectureengineerengineer
shared rulesdesigner + engineerdesigner + engineer
translationdesigner + engineeragent, reviewed
discoveryengineeragent, reviewed
executionengineeragent, reviewed
27

Does Figma disappear?

If code can generate design, and design can inform code, what is Figma for?

a moment in the work

Pick a moment. The roles overlap; the moment decides which one leads.

Figma as canvasFigma as system modelFigma as review layerFigma as context sourceFigma as collaboration spaceCode as runtime truthSystem as shared language

Not a replacement question.A role question — and the role moves.

28

Future workflow

three passes, three different returns

More connected.Not more magical.

No pass goes straight from intent to done. Each review sends the work back to a different place — that is what a loop looks like when it is working.

pass 1
Guests drop at account creationThree guest-flow layoutsField, Button, Stack · no email patternBuilds guest step from existing partsPR · 3 files · 0 new componentsBehind a flag, 10% of traffic“Continue as guest” reads as a dead end
↺ copy and hierarchy → back to exploration
pass 2
Same intentGuest as default, account optionalShippingEstimate exists in two placesFlags the duplicate before writingPR · reuses one, deletes one50% of trafficShippingEstimate should be a system component
↺ promote a pattern → back to the system
pass 3
Same intent—ShippingEstimate in the library + docsUses the new component everywherePR · 1 component, 4 call sites100% of trafficMeasure completion before the next change
↺ evidence → back to intent
29

Interactive lab

conceptual visualization — not a benchmark

Context qualitychanges output quality.

Choose what the agent receives, then read what it “implements”. The outputs are hand-authored to show the mechanism — no model is running here.

context given to the agent · 1 of 7
agent
agent output
Card number
MM/YY
Pay nowBack to cart
defaultloadingerrorstates shown in the render: 1 of 3
// components/NewPayment.jsximport { useState } from "react"; export function NewPayment() {  const [card, setCard] = useState("");  return <div style={{ display: "flex", gap: 13 }}>    <input placeholder="Card number" />    <div onClick={pay} style={{ background: "#2D48CA" }}>      Pay now    </div>  </div>;}
  • structure inferred from pixels
  • values sampled from pixels
  • rebuilds lookalike components
  • new file, new conventions
  • placeholder labels, color-only errors
  • only the photographed state

The handoffis becominga loop.

Design is notbecoming code.

Code is notbecoming design.

The boundaryis becomingprogrammable.

00 Intro