DESIGN.md: How to test a Figma Design System with Claude Code and figma-cli
Screenshots and review meetings do not scale. Something else does.
Every design system has the same blind spot.
You can see what a component looks like. You cannot see whether it is still correct.
A teammate renames one variant. Someone pastes a hex value over a colour token because the token picker was two clicks away. A hover state gets lost during a cleanup. None of that shows up as a red flag. The file still opens. The component still renders. It just quietly stops being what you agreed it would be.
We have all built processes around this. Library review meetings. Screenshot comparisons. A senior designer who knows the system and spots things. That works until the system has 40 components and four people touching it. Then it stops working, because you cannot look at 144 button variants every week and mean it.
The problem got sharper with AI
When an AI builds a component for you, the same question arrives with more force: how do you know it got it right?
Right now the honest answer is that you look at it. The assistant reads a description of your system, interprets it, produces something and then a human decides whether the result is acceptable. That is a review, not a verification. It does not scale, it depends on who is looking and it gets waved through on a Friday afternoon.
The AI is also often the one telling you it worked. That is the part I could not live with. Something that generates work should not also be the thing that certifies it.
Borrowing an idea from engineering
Developers solved a version of this decades ago and the solution is unglamorous: write down what is supposed to be true, then let a program check it.
Nobody reviews a codebase by scrolling through it hoping to notice a bug. They run the tests. The tests either pass or they do not and the answer is the same regardless of who ran them or how tired they were.
Design systems can have that too. Not for taste, taste is not testable, but for everything underneath it: is the variant matrix complete, are the colours actually bound to tokens, is the hover state still wired, did the button get 24 pixels taller.
Those are facts. Facts can be checked by a machine.
Why DESIGN.md is not enough
If you have used figma-cli, you know DESIGN.md. You open a Figma file, ask for the design system as markdown and you get one file with the whole system written out: colours ranked by how much they are used, the type scale, spacing, radii, the full variant matrix of every component set, every variable with all its modes and its aliases. Claude, Cursor and Copilot read that file and build UI that matches your Figma file instead of guessing.
DESIGN.md is prose. Prose is the right format for building, because a model has to read it and interpret it. That same quality makes it useless for verifying. The only way to answer “is this still correct?” from a page of markdown is a human reading it, which puts you back in the review meeting.
So there is a second half now. No AI takes part in it at all.
A snapshot, a contract, a check
You ask Claude for these in plain language, the same way you ask it to build a card. Three things happen.
A snapshot. Your design system gets written out as one file, design.json, that captures its current state exactly: every component, every variant, every token, every mode. It lives next to your code and it gets versioned like anything else.
It is built to be comparable, which is the part that makes it work. Everything that changes for no reason is stripped out: internal ids, publish keys, timestamps. Lists that have no meaningful order get sorted. Numbers get rounded. Export the same system twice and you get two identical files, so any difference you ever see is a real change, never noise.
A contract per component. For each component you get a short, readable file that states what it promises. Button has four variants across two sizes and six states. Every fill is bound to a token. The medium size is 40 pixels tall, give or take two. Hovering goes to the hover state.
These are written by the tool reading your actual Figma file, not by a model and not by you from memory. Nobody sits down and authors 40 specifications. You read them once, decide that is what you meant, and from then on they are the reference.
The generator also refuses to promise more than it can keep, which took me a while to get right. If your token migration is half done, the contract records how many fills are bound today and fails when that number drops, instead of demanding you finish a migration you have not started. If something could only be measured on one variant instead of all of them, the file says so in writing. A green check that quietly means less than it looks like is worse than no check.
A check. You ask for a check and you get an answer in seconds: everything holds, or precisely what does not.
Not “something looks off” but the component, the property and both numbers. The small button used to be 32 pixels tall and is now 56. The state axis gained a value called inctive. A warning you cannot act on is noise. A line like that is a task.
✅ What it catches
Drift. Anything that changed since you last agreed on the state, with the exact component and the exact numbers
Variant matrix. Every combination present, no axis value that appeared without a decision
Token binding. Every fill and stroke bound to a variable, measured across all variants, not one spot check
Geometry. Heights within a stated tolerance, so a size regression is caught by number instead of by eye
Interaction. Prototype transitions still wired, verified rather than assumed
Moving tokens between files. Proof that every token survives the move with its modes and aliases, which is the failure that makes everything come back white and costs you a day to trace
It can also run by itself, nightly or on every change, so you get told when something drifted instead of discovering it yourself. That is where I would like most teams to end up.
Tested against Primer
I ran it against GitHub’s Primer, which is about as real as design systems get: 1015 nodes, 1381 variables, a Button with 144 variants.
Running the check twice in a row gives an identical result. That is the boring property that makes the whole thing trustworthy. All 576 fills and 144 strokes across every single variant come back bound.
Then I renamed one variant, changing inactive to inctive, one missing character in one of 144 names. It was caught in under a second, with the component and the value named in the report.
That is the kind of typo a review meeting is not built to catch.
Four ways to use it
Before you publish a library update. You have been in the file for two days. Run a check before you hit publish and you get a list of everything that changed, including the three things you changed by accident while dragging something else. This is the one I use most.
When you hand the system to someone else. A new team member, an agency, a contractor. Instead of a written brief they cannot fully absorb, they get contracts that say what each component must satisfy plus a way to check their own work before showing it to you. It turns “did I do this right” from a question they have to ask you into a question they can answer themselves.
When you migrate tokens. Moving a token library between files is the operation everyone dreads, because when it goes wrong it goes wrong silently. The check proves every token survived the move before you discover it in a screenshot two weeks later.
When an AI builds for you. This is the one that changes the working relationship. Claude builds the component, then the check runs. If a contract is violated it fixes it and checks again. It only reports back to you when a program with no AI in it agrees the work is done. The AI stops grading its own homework.
What stays human
All of it, at the point where it counts.
The check does not know whether your button should be 40 pixels tall. It knows what you agreed and it tells you when reality stopped matching. Deciding what the system should be, whether a change is an improvement or a mistake, whether the blue is the right blue: that is design work and it stays design work.
What goes away is the part that was never design work in the first place: squinting at a file, hoping to notice.
Red also does not mean wrong. It means changed. If the change was intended, you take a new snapshot and the new state lands in your version history as a reviewable diff, the same way code changes do.
Why this runs as a CLI and not as an MCP server
People keep asking how figma-cli differs from Figma’s official MCP server and from figma-console-mcp. The short version: those talk to Figma through the cloud, over Figma’s public API. figma-cli talks to the Figma app on your own computer, the same way a browser talks to a page it has open. That one choice decides everything below.
No rate limits. Anything built on Figma’s API runs into Figma’s ceiling. Figma’s own MCP server caps the tools that read from your file by seat type: 6 calls per month on a View or Collab seat, 200 per day on a paid Dev or Full seat, 600 per day on an Organization plan (source). An AI reads constantly, so a single “build this and match my tokens” can burn through a dozen calls before anything lands on the canvas. figma-cli never calls the API, so there is nothing to throttle.
No token to create, nothing to keep open. No API key, no plugin you have to leave running, no cloud roundtrip. It works offline.
It costs your AI far fewer tokens. An MCP server loads its instructions plus dozens of tool descriptions into the AI’s context before you have asked for anything. figma-cli has none of that overhead, and its commands and answers are short. Measured in one session, cold start to first component was around 140 tokens against roughly 1,600. Building one token-bound component was around 68 against 256 to 556. Approximate numbers from a hands-on session, not a lab benchmark, but the order of magnitude is the point.
Your AI keeps its facts straight for longer. Every verbose answer sits in the AI’s context and piles up. When the window fills, the assistant compacts it and detail gets summarised away: which node was which, what was already tried. Then it recalls things confidently and wrongly, which is more dangerous than gibberish because it sounds right. A tool that costs ten times more per operation gets you there ten times sooner.
Try it
figma-cli is free and open source. Open Claude Code or Cursor in any folder and say:
“Install github.com/silships/figma-cli and connect it to my Figma.”
Then open your design system file and ask it to snapshot your system, generate the contracts and run a check. The first check on a system nobody has ever tested usually finds something.
Repo and setup guide: github.com/silships/figma-cli




