The Conventions File That Makes Claude Design Use Your Design System
A conventions file built from the CSS you already ship makes every generated component come out in your own design system instead of a generic one. The file Claude Design reads is `.design-sync/conventions.md`, not the `DESIGN.md` everybody writes, and token names one word off your stylesheet are worse than no file at all.
>This is the one file the agent reads. Claude Design Sync covers the other three channels it listens on, and what to do when your repo and your canvas stop agreeing.

Claude Design Sync: Ship the Real App
The Two-Way Claude Code System for Shipping Production Apps Without Drift
Hello builders,
A conventions file built from the CSS you already ship is what makes every generated component come out in your design system rather than a generic one. Getting a claude design system as code into the loop starts with a correction, because the file everybody reaches for does not exist: there is no DESIGN.md, Claude Design has never read one, and searching the tool for it turns up nothing at all.
That’s not a pedantic point, it is the reason a lot of people conclude the whole feature is broken. We write a DESIGN.md, we commit it, we sync, and nothing happens. No error, no warning, the file just sits there. So we decide our instructions were not clear enough and go back to prompting harder at a canvas that never received a single word we wrote.
DESIGN.md is a community convention, and a good one. Somebody built a tool where you paste a URL and get back a generated DESIGN.md describing that site’s palette, type scale and components. Somebody else built an entire brand system out of one markdown file, and neither of them is lying to you. They are describing a file any model will happily read if you paste it into the conversation, because models read whatever you put in front of them. The leap that got made was from “a markdown file describing your design system is useful to a model” to “Claude Design reads a file called DESIGN.md,” and the second one is invented.
The file it actually reads
The real file is .design-sync/conventions.md, and you write it yourself, commit it, and wire it up in your config:
{ "readmeHeader": ".design-sync/conventions.md" }
Then rebuild, because it gets stitched into the generated README at build time, which means an unbuilt change is not a change.
Before writing a line of it, know who reads it. It is not a colleague who can go and look something up when the prose gets vague. It is a model that builds apps with your component library, hundreds of times, for people who will never see this file, and it will not run your build or read your source. It gets the README and the bound artifacts and nothing else.
That gives us the one test every sentence has to pass: could the design agent act on this without guessing? “Follow the design system’s conventions” fails that test. Delete it and write the convention.
The reason is mechanical. An agent in that position follows concrete, enumerated guidance and cannot follow guidance that isn’t there. Name the tokens and it uses tokens. Leave the class vocabulary unnamed and it will not guess at yours, it will invent its own, because a model cannot leave a blank. That is the mechanism behind every “the AI ignored my design system” complaint you have read.
One word apart
Now the failure that costs you, which is worse than writing nothing at all.

I wrote gap-md in the table, because gap-md is what I call it in my head and have called it in conversation for months:
| token | value |
|--------|--------|
| gap-sm | 0.5rem |
| gap-md | 1rem |
| gap-lg | 1.5rem |
The compiled stylesheet declares something else:
:root {
--gap-small: 0.5rem;
--gap-medium: 1rem;
--gap-large: 1.5rem;
}
One word apart, and now the most authoritative document in the agent’s context tells it, with total confidence, to use a variable that doesn’t exist, so it does. Every card built from that moment on has a gap that resolves to nothing. Not a wrong gap. No gap. And no error anywhere, because CSS does not owe us an exception for a variable we never declared. That file did not fail to help the agent, it poisoned it, on every screen, with more authority than any prompt of mine ever had.
So there is a mandatory pass before you commit, and it is mechanical:
grep -o 'var(--[a-z0-9-]*)' .design-sync/conventions.md | sort -u \
| while read -r v; do
n=${v#var(--}; n=${n%)}
grep -q -- "--$n" dist/styles.css || echo "NOT IN STYLESHEET: --$n"
done
NOT IN STYLESHEET: --gap-md
Re-run that against every fresh build, not once. Your components get refactored over time and the conventions file does not notice.
Go look at what you already ship
One more command, because the sync ships design docs you did not choose. Its config carries a glob that is on by default:
guidelinesGlob string or string[] of design-guideline .md files to copy
into guidelines/.
Default: ['docs/guides/**/*.md', 'docs/*.md', 'guides/**/*.md']
Anything matching gets copied into a guidelines/ folder, uploaded with your bundle, and handed to the design agent as the design system’s own usage guidance. Mine had three files in docs/: a real styling guide, a migration note from a refactor that finished long ago, and a stub with a heading and two sentences under it. All three shipped, and the agent read all three with equal seriousness, because it has no way of knowing that one of them is a ghost.
Either fix the ghosts or narrow the glob:
{ "guidelinesGlob": ["docs/design/**/*.md"] }
One rung further, if you want it
If you want your tokens to be portable data instead of CSS, there is a format for it, and two things are worth getting right before you cite it to a client. It is the Design Tokens Format Module, version 2025.10, and it is a Final Community Group Report, which is not a W3C Standard and is not on the W3C Standards Track. Say it that way, because the people who care about that distinction care a great deal.
What makes it mechanically checkable is one rule:
“An object with a
$valueproperty is a token… A group is identified as a JSON object that does NOT contain a$valueproperty.”
So brand below is a group and brand.primary is a token, and nothing else about the file needs explaining:
{
"brand": {
"$type": "color",
"primary": {
"$value": { "colorSpace": "srgb", "components": [0, 0.4, 0.8] }
}
}
}
Two naming rules travel with it. Names must not begin with $, and “…due to the syntax used for token aliases the following characters MUST NOT be used anywhere in a token or group name: { (left curly bracket), } (right curly bracket), . (period).” (Design Tokens Format Module 2025.10)
One trap on the way. The tr.designtokens.org address most links point at redirects to a working draft banner-marked “Do not reference this version as authoritative in any way,” so link the versioned URL.
Now go build something this weekend!
John Cook