Takeaways
- A
DESIGN.mdis the design system in the one place the agent actually looks: the repository. Tokens go in YAML front matter as data, rationale goes in the body as prose. - Tokens are normative, prose is context. The agent copies a value; it interprets a sentence. Put anything that must be exact in the front matter.
- Never hand-copy what the code already declares. Either the file is the source and the theme is generated from it, or the theme is the source and the file links to it. Two sources drift within a week.
- The name is now taken twice. Google Labs'
DESIGN.mddescribes a visual identity. Engineering teams have usedDESIGN.mdfor architecture records for years. Decide which one your repo means and name the other one differently. - Truth is kept by review, not by dates. A design-impact checkbox on every pull request beats a "last reviewed" field nobody trusts.
The problem it solves
An agent building a screen in your codebase has your code and nothing else. It can read globals.css and infer that there is an accent colour and a couple of radii. It cannot infer that the accent is only for one primary action per view, that the pastel bands always take dark ink, that emoji are not icons, or that the display face is for headings and never for labels. Those rules live in your head, in a Figma file the agent cannot open, and in a hundred small corrections you make after the fact.
Skills carry craft that applies to every project. A DESIGN.md carries the decisions that belong to this one. Both are markdown in the repo, both are read on demand, and they compose: the skill says how to choose a hover duration, the design file says which colour the hover uses here.
Two files share the name
Before writing one, know which one you are writing.
| Visual DESIGN.md | Engineering DESIGN.md | |
|---|---|---|
| Describes | A visual identity: colour, type, spacing, components | A system's architecture: goals, invariants, trade-offs |
| Audience | Agents building UI, designers reviewing it | Maintainers, reviewers, operators |
| Defined by | Google Labs' design.md, an alpha format with a CLI | No standard. Kubernetes proposals, Rust RFCs and MADR records come closest |
| Lives at | Repo root, uppercase DESIGN.md | docs/design/ or ARCHITECTURE.md |
In a product or front-end repository, reserve the root DESIGN.md for the visual system and put engineering design records under docs/design/ or in ARCHITECTURE.md. Asking a human or an agent to guess which meaning applies from context is how the wrong file gets edited.
The rest of this post is about the visual one, with a short section on the engineering one at the end.
What goes in the front matter
The YAML block at the top is the part the agent treats as fact. The format groups tokens roughly like this; check the current spec for exact keys, it is still alpha.
- Colours. Named roles with hex values: page, surface, text, muted text, accent, accent ink, borders, and any category or pastel families. Roles, not shades.
accentandaccent-ink, notpurple-500. - Typography. Font families by role, and a named scale with size, line height and weight per step.
- Spacing and radius. The scales as dimension values. If nested radii follow a rule, the rule goes in the prose and the values go here.
- Components. The few that define the identity: a primary button, a card, an input, with their background and text colour pairs. The linter checks contrast on exactly these pairs, so listing them buys you a free accessibility check.
Every value in this block has to be one the code actually uses. Which brings up the only hard rule.
One source, not two
Your theme already declares the tokens, in a Tailwind @theme block, a tokens.json, or a set of CSS variables. Writing the same values into DESIGN.md by hand creates a second copy, and the two disagree the first time someone tweaks a colour in a hurry.
Pick a direction and automate it.
| If | Then |
|---|---|
DESIGN.md is the source | Generate the theme from it. The CLI can export tokens to a Tailwind config or the W3C token format, so the build reads the file and the code never carries a literal. |
| The theme is the source | Generate the front matter from the theme in a script, and lint in CI that the two match. The prose is still written by hand. |
The prose can describe a value. It must not restate one. "The accent is a warm coral, chosen so it reads as an action against the near-black page" is rationale. "The accent is #f4845f" in a paragraph is a copy waiting to go stale.
What goes in the prose
The body is where taste lives, and it follows the same rules as a good skill: decisions with reasons, numbers where numbers exist, exceptions named.
Roles, not aesthetics. Say what each token is for and what it is not for. "Accent is for the one primary action in a view and for focus. It is never a background for text blocks, never a border." An agent given only the hex will put it on a section background because that is common.
Pairs with reasons. "Pastel bands take #0a0a0a ink, never the muted text token. At L 0.85 the pastel and the muted grey sit too close and the label disappears." The reason lets the agent handle the pastel you add next month.
The signatures. The two or three choices that make the identity yours: the serif display face against a geometric sans, the rounded pastel band under every hero, the absence of gradients. An identity is a small number of repeated odd choices. Write them down so they get repeated.
Do and don't, concretely. No emoji as icons, one icon library, no indigo utilities, no transition: all. Each with a one-line why.
Component notes. For the components in the front matter, the behaviour the tokens cannot express: press scale, hover treatment, which states exist, the one-primary-per-view rule.
Keep out everything the agent already knows and everything the code already says. A paragraph explaining what a border radius is, or listing every colour again, dilutes the lines that matter.
Keeping it true
A design file is a claim about the code. Claims drift. Three mechanisms keep them honest, in order of how much they cost.
- Review on the change that touches it. Add a design-impact line to the pull request template: no visual impact, existing
DESIGN.mdstill accurate, or updated in this PR. The value is forcing the judgement, not forcing an edit. - Lint in CI. Run the CLI's lint on every pull request that touches the file or the theme. It catches broken token references, missing roles, and contrast failures on component pairs. If you generate one side from the other, add a diff check that fails when they disagree.
- Make rules executable where you can. "Pastel bands take dark ink" can be a test that walks rendered pages and reads contrast. "No indigo utilities" is a grep. A rule with a check is a rule that survives the next contributor.
Dates and owners are still worth having in the front matter. They tell a reader who to ask. They do not tell a reader the file is right.
Working with it day to day
- Point skills at it. A skill's body can say "read
DESIGN.mdbefore choosing any colour or type value". The skill carries the method, the file carries the values. - Diff before merging a redesign. The CLI compares two versions and reports token and prose regressions. Run it on the branch that changes the identity and read the output like a code review.
- Let the agent propose edits. When an agent makes a call the file did not cover and the result is good, ask it to write the rule into the prose as a pair with a reason. Then review that diff like any other.
- One file per identity. A monorepo with two products has two files, or one root file and per-app overrides that only list what differs.
The engineering DESIGN.md, briefly
If your repo also needs an architecture record, it is a different document with a different life. The strongest examples across open source, from Google's smaller projects to Kubernetes proposals and MADR decision records, converge on the same questions in roughly this order: why the system exists, what it must achieve, what it deliberately does not, what constrains it, what the design is, which invariants must stay true, what else was considered, how it fails, how correctness is shown, how it ships and rolls back, and whether the document is still authoritative.
Three practices from that world transfer straight to the visual file. Write the problem before the solution. Record the alternatives you rejected, because they come back. And tie every "must" to a check, or admit it is not enforced.
Worth reading before you write yours
Every one of these is a real file in a public repo. Read two or three, then notice what they leave out.
The visual format
- google-labs-code/design.md, the format, the CLI and the reasoning behind tokens-as-data. Start with the spec, then the examples, which show how much prose a real identity needs.
Engineering design files worth copying the shape of
- google/sequence-layers says on line one that it is written for humans and coding agents, then states contracts and points at the tests that verify them. The closest thing to a visual DESIGN.md in spirit.
- google/stenographer sets its own altitude: medium and high level in the file, low level in the code. A model for what to leave out.
- google/keystone is the two-kilobyte version. Archived, but proof that a short overview can be enough.
- google/crubit records alternatives with pros and cons, and names the risk of sidecar files drifting from the source. Read it for the rejected options.
- google/keytransparency adds a threat model. If your identity has trust boundaries, this is the pattern.
- facebook/rules_pyrefly has status, author, goals, non-goals and major decisions at the top, then stops. A good template for a medium repo.
- microsoft/Agent365-devTools is the hierarchical one: a repository-level file that indexes component designs. The shape a monorepo needs.
The processes the good files borrow from
- Kubernetes KEP template, for the operational tail: rollout, rollback, monitoring, version skew.
- Rust RFC template, for the split between a guide-level explanation and a reference-level one, and for mandatory drawbacks.
- Go proposal process, for starting with a short issue and only writing the document when discussion earns it.
- MADR, for decision records with a field that asks how compliance will be confirmed.
Checklists
Before the first commit
- Decided which
DESIGN.mdthis repo means, and named the other one differently - Front matter holds roles, not shades, and every value exists in the code
- One direction of generation chosen: file to theme, or theme to file
- Component pairs listed so contrast is linted
- Owners and date in the front matter
Per prose section
- Says what a token is for and what it is not for
- Every rule has a reason and, where it exists, an exception
- Numbers where numbers exist
- No value restated that the front matter or the code already declares
- Nothing the agent already knows
Before relying on it
- Lint passes in CI on pull requests that touch the file or the theme
- Design-impact line in the PR template
- At least one rule turned into an automated check
- A skill or rules file tells the agent when to read it
---
# Machine-readable. Roles, not shades. Every value must exist in the code.
# Check the current spec for exact keys: github.com/google-labs-code/design.md
name: <product>
version: 0.1.0
owners: ["@design"]
updated: <YYYY-MM-DD>
colors:
page: "#0a0a0a"
surface: "#161616"
text: "#f5f5f5"
text-muted: "#a3a3a3"
accent: "#f4845f"
accent-ink: "#0a0a0a"
border: "rgba(255,255,255,0.08)"
typography:
display: { family: "<serif display>", weight: 700 }
body: { family: "<sans>", weight: 400 }
scale:
xs: { size: 12, lineHeight: 16 }
base: { size: 16, lineHeight: 24 }
xl: { size: 32, lineHeight: 36 }
spacing: [4, 8, 12, 16, 24, 32, 48, 64]
radius: { sm: 8, md: 12, lg: 16, pill: 999 }
components:
button-primary: { backgroundColor: accent, textColor: accent-ink }
card: { backgroundColor: surface, textColor: text }
---
# <Product> visual identity
<Two sentences on what the identity is and the one choice a competitor could not copy.>
## Signatures
- <Repeated odd choice #1, and why.>
- <Repeated odd choice #2, and why.>
## Colour roles
- **accent** is for the one primary action in a view and for focus. Never a text background, never a border. <Why.>
- **Pastel bands take dark ink.** <Why.>
## Type
- Display face for h1 and h2 only. Labels and UI text stay in the sans. <Why.>
- Body capped at 65ch.
## Surfaces and depth
- Light mode: 1px alpha border. Dark mode: solid border, no shadow. <Why.>
- Inner radius = outer radius minus padding.
## Do and don't
- No emoji as icons. One icon library.
- No gradient without a job.
- Never `transition: all`. Name the properties.
## Components
- **button-primary**: press scale 0.96, one per view.
- **card**: not a link as a whole; give it a CTA.
## Rejected
- <Rule tried and dropped, and why.>