DESIGN.md in CI: Linting Design Specs
An engineering guide to DESIGN.md: the YAML + Markdown format, lint/diff/export/spec commands, WCAG AA coverage, exit code traps, and a complete CI example.
Over the past year, "how do we keep an agent producing visually consistent UI" has become a real engineering problem. The answer is converging on a simple idea: write the design system down in a plain-text file and make the agent read it before touching any UI.
DESIGN.md is the most watched take on that idea. It comes from an official Google Labs open-source repository (Apache-2.0, currently alpha) and ships with a standalone CLI: lint, diff, export, and spec. Version 0.4.0 has been widely tested by the community in early October.
The more important shift is positioning: DESIGN.md is no longer just "a document the agent reads", but an engineering artifact you can lint, diff, and block merges with. Design token governance finally has an official, executable toolchain.
Note: the DESIGN.md format is still alpha and will keep changing; pin the CLI version when you wire it into CI. This article reflects v0.4.0 behavior.
1. From "a Document for the Agent" to a Checkable Artifact
A convention is settling in for the agent era: give each responsibility its own plain-text spec file.
| File | Owns | Core role |
|---|---|---|
| CLAUDE.md | Behavior | How the agent should work and which processes to follow |
| DESIGN.md | Visuals | Colors, type, spacing, component rules, and design intent |
| AGENTS.md | Collaboration | How multiple agents or tools coordinate |
A popular community summary nails it: CLAUDE.md governs behavior, DESIGN.md governs visuals, AGENTS.md governs collaboration.
The real value of DESIGN.md is checkability: contrast, token references, and section completeness can all be verified with a single command, and that check can run in CI so non-compliant specs never reach the main branch.
2. The Format at a Glance: YAML Values + Markdown Intent
DESIGN.md uses a two-layer structure:
- YAML frontmatter: the canonical values (tokens) such as color hex codes, font names, and type scales.
- Markdown body: the design intent — why these values exist and how to use them.
The split matters: machines read YAML to lint and export; humans and agents read Markdown to understand context.
Fixed section order
The spec requires a fixed section order, and the lint tool checks completeness against it:
- Overview
- Colors
- Typography
- Layout
- Elevation & Depth
- Shapes
- Components
- Do's and Don'ts
When writing a prompt-style design spec, or asking an agent to draft a DESIGN.md, follow this order or lint will complain.
Illustrative structure
A minimal structure sketch (fields depend on the actual spec):
---
colors:
primary: "#1a1a1a"
background: "#ffffff"
accent: "#2563eb"
typography:
base: "Inter"
scale: "1.25"
---
## Overview
A documentation product that values readability and restrained accent color...
## Colors
primary is for body text and primary buttons; accent is reserved for links and focus states...
3. The Four CLI Commands: lint / diff / export / spec
The four commands cover the full lifecycle of a design spec:
| Command | Purpose |
|---|---|
lint | Validate DESIGN.md against the rule set, including WCAG AA contrast |
diff | Compare two specs or two revisions |
export | Export to Tailwind config or W3C DTCG design tokens |
spec | Print the normalized spec for team alignment |
Run a local check:
npx @google/design.md lint DESIGN.md
What lint covers, and where WCAG AA stops
lint ships with 8 rules. The loudest one is contrast checking: text and background pairs are verified against the WCAG AA 4.5:1 threshold.
Know the boundaries: lint does not inspect focus states, semantics, or motion. It catches hard failures; it is not a substitute for design review.
The exit code trap: why contrast issues do not fail CI
This is the easiest trap to fall into: when lint finds contrast problems it warns but does not error, and the process still exits 0. Wire it into CI as-is, and a non-compliant spec passes.
Strict enforcement means reading the JSON output and returning a non-zero exit code yourself. The check below is illustrative (adapt to the actual output format):
# Illustrative: persist lint output and fail CI when a contrast warning appears
npx @google/design.md lint DESIGN.md > lint-result.json
if grep -qi "contrast" lint-result.json; then
echo "Contrast check failed"
exit 1
fi
4. In Practice: Adding DESIGN.md Checks to CI
A GitHub Actions configuration you can adapt directly. Two details matter: use the official designmd command name from the workaround, and pin the version:
name: design-lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Lint DESIGN.md
run: npx -p @google/design.md@0.4.0 designmd lint DESIGN.md
Strict mode: parsing the JSON output
If the team requires "contrast failures must block merges", add a step on top: persist the JSON output, look for contrast warnings, and exit non-zero when found. The approach matches the earlier snippet — the key is translating a "warning" into CI semantics for "failure".
5. Exporting to Tailwind Config and DTCG
lint keeps the spec correct; export makes it usable:
- Export to Tailwind config so the frontend consumes design tokens directly instead of scattering colors and fonts across components.
- Export to W3C DTCG to interoperate with the broader design toolchain.
Worth noting: the DESIGN.md CLI runs independently and needs no Stitch account. Even if your team never uses Stitch to generate interfaces, you can adopt it purely as a spec linter and exporter.
6. Troubleshooting: Three Common Failures
| Symptom | Cause | Fix |
|---|---|---|
| ENOVERSIONS | Dependency or version resolution failed | Pin the CLI version; clear the cache and reinstall |
| No output on Windows | design.md command collides with a file association | Use the workaround command |
| broken-ref | The spec references a missing token or section | Fix the reference at the reported line |
On Windows / PowerShell, when the command name collides, use:
npx -p @google/design.md designmd lint DESIGN.md
7. FAQ
Q1: Can I use DESIGN.md and the CLI without a Stitch account?
Yes. Both the format and the CLI are independent projects; local runs and CI integration do not depend on Stitch cloud services.
Q2: Why does a contrast problem not fail CI?
lint reports contrast issues as warnings and exits 0; strict blocking requires parsing the JSON output and returning a non-zero exit code yourself.
Q3: What is the smallest useful setup for a new team?
Two steps: add one line to AGENTS.md ("read DESIGN.md before any UI work"), then add a lint check to CI. Fill in the spec as you go.
8. Summary
DESIGN.md turns "design rules" from a verbal agreement into an engineering artifact: writable, checkable, exportable, and enforceable. For teams already practicing CI, the cost of adoption is nearly zero — one command to check, one pipeline to hold the line.
Keep reading:
- See how the CLI ties design to code: Stitch CLI guide
- Learn how design systems are extracted: URL to design system
- Start from prompt writing: Stitch prompt tutorials