Skip to content
StrictThemes

Guides / Explained

What is a DESIGN.md, and what goes in one

The parts of a DESIGN.md file, with one real theme taken apart, the sizes of 23 real files, a lint run that fails on purpose and a 45-line file you can copy.

Last checked

A coding agent builds a page from whatever it knows about your design. If that is nothing, it uses its own habits: a grey it likes, corners a little too round, a 14px that is not yours. If it is a paragraph in a chat message, the paragraph is gone by the next session.

A DESIGN.md is where the design lives between sessions. It is one Markdown file in the project's root, and it has two readers. An agent reads it before it builds. A program reads it afterwards and checks what was built. This guide takes one real file apart, says how big such a file should be, and ends with a small one to copy.

One file, two halves

The format was published by Google Labs in 2026 and is marked alpha. Strict uses it as it is and adds one key, strict, which is covered below.

The file starts with front matter: a block of YAML between two --- lines, the same device a blog post uses for its title and date. Here it holds tokens. A token is a design value with a name, such as primary: "#963614". After the front matter comes ordinary Markdown: up to eight sections of prose that say what the tokens are for.

A diagram of a DESIGN.md file. The front matter holds name and description, colors, typography, rounded, spacing, components and the strict block. Below it are eight prose sections: Overview, Colors, Typography, Layout, Elevation and Depth, Shapes, Components, and Do's and Don'ts.
The two halves of the file. The strict block, in blue, is the part Strict adds.

The halves do different work. The tokens say what may be used, and a program can read them. The prose says when and why, and only a reader that understands sentences can use it. A file with tokens alone gives you an agent that uses the right terracotta on everything.

The token groups

The examples below are from Kiln, one of the 23 themes in the repo, at packages/themes/themes/kiln/DESIGN.md.

colors

Each color is named for its job, not its look. Kiln has 15. These are five of them:

colors:
  primary: "#963614"
  on-primary: "#FFF6EC"
  background: "#F4ECE1"
  surface: "#FBF6EE"
  foreground: "#2A1D14"

primary is the fill of the main action. on-primary is the color of text placed on that fill. background is the page, surface is a card laid on the page, foreground is the text. Design tokens for coding agents explains why the names matter more than the values.

typography

A typography token is a whole text style, not a size alone:

typography:
  h1:
    fontFamily: Bricolage Grotesque
    fontSize: 38px
    fontWeight: 700
    lineHeight: 1.1
    letterSpacing: -0.02em

Kiln has five: display, h1, h2, body and label. Keeping the weight and line height with the size stops an agent from pairing a 38px heading with body line height.

rounded and spacing

Two short lists of lengths. rounded is corner sizes. spacing is the steps allowed for padding, margin and gap.

rounded:
  sm: 2px
  md: 4px
  lg: 10px
spacing:
  xs: 4px
  sm: 8px
  md: 16px
  lg: 24px
  xl: 40px
  2xl: 80px

components

A component here is not code. It is a named pairing of the tokens above, written as references in curly brackets:

components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label}"
    rounded: "{rounded.md}"
    padding: "{spacing.sm} {spacing.md}"

This is the part a linter can test. It knows which text goes on which fill, so it can work out the contrast between them before any page exists.

The eight sections

All eight are optional. The ones you write must come in this order, and a few have a second accepted name (Brand & Style for the first, Layout & Spacing for the fourth).

Section What belongs in it From Kiln
Overview Who the product is for and how it should feel "warm, plain and sure of itself"
Colors What each color is for, and where it must not go Denim blue is for a badge, "never for large areas"
Typography Which font does which job "Labels are sentence case, never uppercase"
Layout Grid, widest width, where each spacing step goes "80px between sections, 24px inside a card"
Elevation & Depth Shadows, borders, how layers are told apart "Flat, like printed card"
Shapes Which corner size goes on what "No pills, no circles except avatars"
Components When to use each component Terracotta "for the one main action"
Do's and Don'ts What a reviewer would say "Don't use pure black or pure white"

Notice what the right-hand column has in common. None of it can be derived from the hex values. It is the half of the design that used to live in a designer's head.

The strict block

A plain DESIGN.md describes. The strict block turns the description into rules: it says how closely the built UI has to follow the tokens. This is Kiln's, without its dark mode colors:

strict:
  version: 1
  contrast: AA
  colors:
    allow: tokens
    tolerance: 0.02
  spacing:
    allow: scale
    extra: ["0", "1px", "2px"]
  rounded:
    allow: scale
    full: 64px
  typography:
    allow: scale
    fonts: declared
  tells:
    purple-gradient: error
    emoji-icons: error
    nested-cards: warn
    glow-shadows: error
Line What it means
contrast: AA Text needs 4.5:1 against what is behind it. AAA asks for 7:1.
colors.allow: tokens Only the listed colors may appear. tolerance is how far a color may drift from one and still count.
spacing.allow: scale Padding, margin and gap come from the spacing tokens, plus the values in extra.
rounded.full: 64px The largest box that may be fully round. Kiln's prose allows avatars, so this lets them through.
typography Only the declared sizes, weights, line heights and font families.
tells Signs of generic machine-made UI, each set to off, warn or error.
modes Extra color modes. Kiln's dark mode overrides 13 of its 15 colors.

A file without the block is still valid. The defaults then apply: AA contrast, token colors only, scale-only spacing, corners and type. A tool that does not know Strict sees strict as an unknown key, which the format treats as a warning. The spec page has every field.

How big a good file is

We ran one script over all 23 themes in the repo. It parses each file with lintSpec from @strictthemes/spec and counts.

Median Smallest Largest
Colors 13 12 18
Typography tokens 5 5 5
Spacing steps 6 5 7
Corner sizes 3 1 3
Components 6 6 6
Words of prose 521 414 664
Lines in the file 184 177 191

Three things to take from it:

  • The file is short. Under 200 lines, about two thirds of them front matter. An agent can read the whole thing at the start of every task.
  • The prose is about 500 words. That is one page of writing. If yours is 50 words, the agent is guessing at intent. If it is 5,000, you have written a style guide, and the rules that matter are buried.
  • The counts barely vary. Every theme has exactly five text styles and six components. Twelve colors is the floor because all 23 files share the same twelve names. The themes with more added colors of their own, such as Kiln's two glaze colors for drawn pots.

All 23 files have a dark mode. 22 ask for AA contrast and one, Civic, asks for AAA.

Lint it

The linter tells you whether the file is well formed and whether its own components are readable. Run it on Kiln:

npx @strictthemes/spec lint kiln/DESIGN.md
info     orphaned-tokens  colors
         Colors no component refers to: border, error, success, glaze-pale, glaze-dark
info     token-summary
         15 colors, 5 typography, 3 rounded, 6 spacing, 6 components, 1 extra modes

kiln/DESIGN.md: 0 errors, 0 warnings

The info lines are notes, not faults. Now a copy broken on purpose in two places: on-primary changed to a pale terracotta, #E8B79E, and the Typography section moved above Colors.

error    contrast-ratio  components.button-primary
         #E8B79E on #963614 has a contrast ratio of 4.13:1, below WCAG AA (4.5:1)
warning  section-order  Colors
         Section "Colors" is out of order. Expected order: Overview, Colors, Typography, Layout, Elevation & Depth, Shapes, Components, Do's and Don'ts
info     orphaned-tokens  colors
         Colors no component refers to: border, error, success, glaze-pale, glaze-dark
info     token-summary
         15 colors, 5 typography, 3 rounded, 6 spacing, 6 components, 1 extra modes

broken/DESIGN.md: 1 errors, 1 warnings

The command exits with code 1, so it can stop a commit or a CI job. The contrast error exists only because button-primary names its fill and its text. A color pair that no component uses is not tested, which is a reason to write components even if you never read them again. Contrast for non-designers explains the 4.5:1.

The same file, as code and as a page

The tokens are data, so they convert. This prints Kiln as a Tailwind CSS 4 theme (shortened here):

npx @strictthemes/spec export DESIGN.md --format tailwind
@theme {
  --color-primary: #963614;
  --color-on-primary: #FFF6EC;
  ...
  --text-h1: 38px;
  --text-h1--line-height: 1.1;
  --text-h1--font-weight: 700;
  --text-h1--letter-spacing: -0.02em;
  ...
  --radius-md: 4px;
  --spacing-space-md: 16px;
  ...
}

.dark {
  --color-primary: #EC8B5E;
  --color-on-primary: #2A1206;
  ...
}

The other formats are shadcn, shadcn-css, nuxt-ui and dtcg (the W3C design tokens JSON). The theme pages on this site are drawn from the same file: the swatches, the type samples, the rules and the guidance on Kiln's page all come out of its DESIGN.md.

The top of the Kiln theme page on strictthemes.com: the theme's name and description on a clay-colored page, an install command, links to DESIGN.md and its exports, and a row of links to preview, colors, typography, shape and space, rules and guidance.
The same file, rendered: Kiln's page on this site, in Kiln's own colors and fonts.

A small one to copy

This file is 45 lines and lints with 0 errors and 0 warnings. Change the values and the sentences to yours.

---
name: Plainsong
description: A quiet theme for a small product site.
colors:
  primary: "#1D4ED8"
  on-primary: "#FFFFFF"
  background: "#FFFFFF"
  surface: "#F4F4F2"
  foreground: "#18181B"
  neutral: "#5F5F66"
  border: "#DADAD6"
typography:
  h1: { fontFamily: Inter, fontSize: 36px, fontWeight: 700, lineHeight: 1.15 }
  body: { fontFamily: Inter, fontSize: 16px, fontWeight: 400, lineHeight: 1.6 }
  label: { fontFamily: Inter, fontSize: 13px, fontWeight: 500, lineHeight: 1.3 }
rounded: { sm: 2px, md: 6px }
spacing: { xs: 4px, sm: 8px, md: 16px, lg: 32px, xl: 64px }
components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label}"
    rounded: "{rounded.md}"
    padding: "{spacing.sm} {spacing.md}"
  card: { backgroundColor: "{colors.surface}", textColor: "{colors.foreground}", rounded: "{rounded.md}", padding: "{spacing.md}" }
  page: { backgroundColor: "{colors.background}", textColor: "{colors.foreground}", typography: "{typography.body}" }
  caption: { backgroundColor: "{colors.surface}", textColor: "{colors.neutral}", typography: "{typography.label}" }
strict:
  version: 1
  contrast: AA
  tells: { purple-gradient: error, emoji-icons: error }
---

## Overview

A product site for people who want the facts fast. Calm, plain, one blue.

## Colors

Blue is for the one main action on a view. Grey text is for captions only.

## Do's and Don'ts

- Do keep one primary button per view.
- Don't add a color, size or corner that is not a token above.

The linter prints three notes for it: five sections are not present, border is used by no component, and the token counts. None stops it passing. It has no dark mode, and it is missing most of its prose. Treat it as the skeleton to fill, not a finished theme.

What the file cannot do

  • It does not enforce itself. The linter checks the file. Whether a page follows it is a second job, done by Strict Check against the code and the rendered pages.
  • Nothing checks the prose. "One primary button per view" is read by the agent and by you. The tools check the numbers, not the sentences.
  • It does not load fonts. The file names font families. Every export ends each family in a generic fallback, and the project still has to add the font files.
  • It is not loaded on its own. An agent reads it only if something tells it to. DESIGN.md, AGENTS.md and Cursor rules covers where that instruction goes.

Next