Linting a DESIGN.md before your AI coding agent reads it A developer hand-wrote a DESIGN.md file for a fictional coffee shop site and ran Google's @google/design.md linter (v0.4.0), which flagged one error and four warnings: an unresolved token reference, a missing 'primary' color, a 2.15:1 contrast failure on the badge, an orphaned 'muted' token, and missing typography. After renaming the brand token to 'primary', adding typography, rounded and spacing sections, and a caption component, the file linted clean at 0 errors and 0 warnings, with the developer noting the linter only checks contrast within component color pairs and misses other issues. DESIGN.md is a Markdown file with YAML front matter that tells an AI coding agent what your UI looks like: colors, type, radii, spacing and a few components. The tokens go in the front matter, and the Markdown body says how to use them. Google publishes the format along with a CLI, @google/design.md , that lints it. I wrote a first draft by hand for Northwind Roasters, a fictional coffee shop site I use for demos, ran the linter v0.4.0 , and fixed what it found. Here is what it caught, and what it doesn't check. This is the kind of file you write in five minutes from DevTools: --- version: alpha name: "Northwind Roasters" colors: brand: " c2410c" amber: " f59e0b" background: " fffaf3" surface: " f6eadb" text: " 2b1d14" muted: " 7a6656" components: button-primary: backgroundColor: "{colors.brand}" textColor: " ffffff" badge-new: backgroundColor: "{colors.amber}" textColor: " ffffff" card: backgroundColor: "{colors.surfce}" textColor: "{colors.text}" --- Components point at tokens with {colors.name} references instead of repeating hex values, so the agent knows the card background is the surface color rather than a near-identical one. npx @google/design.md lint DESIGN.md The summary: 1 error, 4 warnings, 3 infos. It exits with code 1, so you can fail a CI step on it. The findings, shortened: | Rule | Severity | Message | |---|---|---| | broken-ref | error | Reference {colors.surfce} does not resolve to any defined token. | | missing-primary | warning | No 'primary' color defined. The agent will auto-generate key colors… | | contrast-ratio | warning | textColor ffffff on backgroundColor f59e0b has contrast ratio 2.15:1, below WCAG AA minimum of 4.5:1. | | orphaned-tokens | warning | 'muted' is defined but never referenced by any component. | | missing-typography | warning | No typography tokens defined. Agents will use default font choices… | | missing-sections | info | No 'spacing' section defined. and the same for 'rounded' | Each one in turn: brand . The format expects a color named primary , and without one the agent picks its own key colors. Renaming the token was the whole fix. 2b1d14 on the same amber is 7.59:1. muted is my caption color, but no component used it. An unused token is just a hex value with no instructions, so I added a caption component. --- version: alpha name: "Northwind Roasters" colors: primary: " c2410c" amber: " f59e0b" background: " fffaf3" surface: " f6eadb" text: " 2b1d14" muted: " 7a6656" typography: headline-display: fontFamily: "Fraunces" fontSize: 64px fontWeight: 600 lineHeight: 1.04 letterSpacing: -1.28px body-md: fontFamily: "Hanken Grotesk" fontSize: 17px fontWeight: 400 lineHeight: 1.6 rounded: lg: 10px spacing: base: 4px components: page: backgroundColor: "{colors.background}" textColor: "{colors.text}" button-primary: backgroundColor: "{colors.primary}" textColor: " ffffff" rounded: "{rounded.lg}" badge-new: backgroundColor: "{colors.amber}" textColor: "{colors.text}" card: backgroundColor: "{colors.surface}" textColor: "{colors.text}" caption: textColor: "{colors.muted}" --- Lint result: 0 errors, 0 warnings, 1 info the token summary . White on c2410c for the button is 5.18:1, so that pair passes as well. design.md diff old.md new.md gives a JSON report of what changed: brand removed, primary added, two typography tokens added, page and caption added, three components modified. It also shows the finding counts before and after errors -1, warnings -4 . That's handy for a PR description when someone edits the file. Zero warnings doesn't mean the file is right. Three things I checked by hand: Contrast is only checked inside a component. The rule looks at a component's own backgroundColor / textColor pair. My caption component only has a text color, so nothing checked it. On the page background, muted is 5.24:1. On a card it is 4.59:1, which still passes, but only just. If your agent puts captions on cards, that's the pair to check yourself. The values have to be the real ones. The linter can't know that the body text is 17px and not 16px, or that the h1 is tracked to -1.28px. Copy those from DevTools Computed on the real site, not from memory. Exports drop some things. design.md export --format css-tailwind turns the file into a Tailwind v4 @theme block. For this file it wrote colors, font families, sizes, tracking, weights, radius and spacing, but in v0.4.0 the lineHeight values didn't appear in the output, and the font families came out without fallbacks --font-body-md: "Hanken Grotesk"; . Add those by hand if you use the export. {colors.x} , and give every color at least one component that uses it. npx @google/design.md lint in CI, then check contrast on cards and the real values by hand. I made a Chrome extension, Stylasso, that writes a DESIGN.md from the page you're on: real colors with roles, fonts, type scale, radii and spacing, with components referencing the tokens. The three demo-site files it generates lint with 0 errors; one gets the same white-on-violet contrast warning I'd expect, because that's what the site really uses. There's a short guide here: DESIGN.md from any website https://stylasso.pixlasso.com/design-md?src=devto . Writing the file by hand, as above, works too. Written with help from an AI assistant. Every lint result, diff and export above comes from running @google/design.md v0.4.0 on the files shown.