Use when authoring, auditing, or exporting a DESIGN.md design system — design tokens, tailwind config, deslopify a generic look, дизайн-система, фирменные цвета, единый стиль, извлеки палитру по скриншоту, derive one from a brief, or extract one from a codebase or its CSS.
DESIGN.md records a design system as machine-readable YAML tokens plus a prose
rationale, so an implementing agent stops using its own defaults. Three gaps
stand between an agent and a usable file:
Syntactic — schema, references, the eight body sections. Closed by references/spec-anatomy.md.
Procedural — verifiable output handed back unverified. Closed by scripts/lint and Route 4.
Substantive — a valid file is not a useful one. Closed by references/anti-slop.md, and by nothing else.
The third bites hardest because nothing reports it: a file of framework defaults,
system-ui, one radius and four truisms lints at zero errors. Toolchain:
@google/design.md@0.4.0 (Apache-2.0, Google LLC), run via npx.
When to Use This Skill
Use this skill when the user:
asks for a DESIGN.md, a design system, a token file, or design tokens;
asks to extract a palette from a screenshot, a mockup, or an image, or to
derive a system from a brand brief, a codebase, or a tailwind config;
asks to audit, repair or lint an existing DESIGN.md, export its tokens to
Tailwind v3/v4, DTCG or CSS custom properties, or compare two revisions;
says the output looks generic and asks to deslopify it, or writes any of
дизайн-система, фирменные цвета, единый стиль, извлеки палитру, по скриншоту.
Out of scope: images, logos, component implementation, palettes derived from one
seed colour. This skill produces the specification, not the interface.
Red Flags
STOP and READ if you are thinking:
"The linter found no errors, so the file is finished." → WRONG. Only an
unresolvable reference and an unparseable token value move the exit code.
examples/fixture-clean.md lints 0 errors, 0 warnings, 1 info, yet
scripts/check-contrast exits 1 on it: outline measures 1.72:1 on surface,
under the 3.0:1 non-text floor. Exit 0 is the entry requirement, not a verdict.
"I recognise the typeface in this screenshot." → WRONG. Grotesques are
indistinguishable at UI sizes, and a family name in fontFamily reads as a
measured fact forever after. Offer candidates, label yours a placeholder, ask.
"The brief said nothing about spacing, so a 4/8/16/24 ladder is safe." →
WRONG. An invented value and a measured value are indistinguishable in
YAML. Declare the section in omitted — that field exists for exactly this.
"ink and paper describe this palette better than primary and surface."
→ WRONG. orphaned-tokens emits one warning per colour whose family sits
outside the MD3 baseline and which no component references. Deleting
components silences them — the rule returns early on an empty map — and
leaves a token dump.
Rationalization Table
Agent Excuse
Reality
"Exit code 0 means the file passed."
Exactly one rule produces errors. A file with no primary, no typography, an out-of-order body and a misspelled key that export drops still exits 0. Report the summary triple, never the exit code alone.
"The linter said nothing about my palette, so the palette is fine."
No rule reads a hue ramp, a type scale, a radius set, or one word of prose. references/anti-slop.md is the only check that does.
"I will read the hex values off the screenshot myself."
Compression, antialiasing, gradients and the colour profile each move the value. A hex written from perception is a fabrication shaped like a measurement. scripts/extract-palette measures; you assign roles.
"#3b82f6 is a safe accent to start from."
That is Tailwind blue-500. It measures 3.68:1 on white, below AA. Six of the nine most-copied framework accents fail 4.5:1 on white, and readers recognise them on sight.
"token-summary is noise; I skip the info lines."
It is the only trace of a silently dropped token: a spacing value written 16 with no unit vanishes and raises no finding at all. Read the counts back against what you wrote.
"I will add elevation to the component."
The sub-token set is closed at eight names. An invented key is a broken-ref warning and every exporter drops it. Depth belongs in ## Elevation & Depth prose.
"One lint pass is enough."
section-order reports at most one inversion per run, and orphaned-tokens stays silent until the first component exists. Re-lint after every fix round.
"The template in assets/ is close enough to ship."
template-skeleton.md lints clean carrying a conspicuous magenta accent and fifteen PROVISIONAL markers; a clean lint on an unedited copy proves only that the structure survived.
Execution Mode
Mode: hybrid
Why this mode: measurement is script work, judgement is not. Colour values,
contrast ratios and lint findings come from scripts/ and the upstream CLI,
because each is a number an agent gets wrong by eye. Template choice, role
assignment, naming and the prose body are the agent's alone, and every route
below ends in a script run.
Script Contract
Three executables in scripts/, all Python 3, all read-only: each writes a
report to stdout and modifies no file. Pass absolute paths.
WCAG 2.x ratios for every intended text-on-surface pair, a typography table and an advisory matrix, at AA 4.5, AA-large 3.0, AAA 7.0 and the 3.0 non-text threshold; --pair answers one pair the matrix never prints, UNCHECKED FILLS every component fill with no textColor
dominant colours as HEX, SHARE, BUCKETS, HUE, L*, C* and a heuristic HINT
--strict fails lint on warnings, which is what makes a gate meaningful, and
--list-rules prints all fourteen rule rows offline. Exit codes differ per script:
Code
lint
check-contrast
extract-palette
0
no errors (and no warnings under --strict)
every gated intended pair meets the gate — under --pair, every queried pair
success
1
errors, or warnings under --strict
a gated intended pair fails — under --pair, a queried pair does — or --self-test mismatched
an out-of-range option value, or a --region malformed, outside the image, or combined with --ignore-edges
2
input problem, or a usage error
input problem, a usage error, or a --pair token name that is unknown or has no ratio
image missing, unreadable, or corrupt
3
npx or the CLI unavailable, the call timed out, or non-JSON output
the export call failed, or no colors defined
no available decoder handles the format
With several files lint returns the worst code, ordered 3 > 2 > 1 > 0.
Artifacts: none — no script writes a file. Exit 3 means "the tool did not
run", never "the file is fine": report no verdict from such a run. The raw form,
for diff, spec and anything the wrappers omit:
cd /tmp && npx --yes @google/design.md@0.4.0 lint|diff|export|spec <ABSOLUTE-PATH>
cd /tmp is load-bearing: a workspace declaring the same bin name shadows it.
export requires --format (css-tailwind, json-tailwind, tailwind,
dtcg, css-vars); lint --format text silently returns JSON. Full flags: each
--help and scripts/README.md §2 (--version PIN takes an argument).
Safety Boundaries
Writable scope: only the DESIGN.md the user asked for, at a path they named
or in their working directory — nothing else in their tree.
Never overwrite the skill's own material: assets/, examples/ and
references/ are read-only, every template and fixture lint-verified as it
stands. Copy a template out and edit the copy.
Network: lint and check-contrast shell out to
npx --yes @google/design.md@0.4.0, which reaches registry.npmjs.org on the first call
— roughly 30 seconds, then the npx cache, and the skill's only network access. lint
announces that wait on stderr; check-contrast prints nothing until its report, so a
cold run of it looks hung. Offline with a cold cache both exit 3, never a clean verdict.
User-supplied images: extract-palette decodes an image the user supplied,
reading pixels and writing nothing; it spawns no process and makes no network
call. Confirm the path, do not fetch an image from a URL unless asked.
Installs: bash scripts/install.sh is optional and puts Pillow into
scripts/.venv/ only — nothing global. Never vendor upstream design.md
source or its examples/ here.
Validation Evidence
Every assets/template-*.md and examples/example-saas-dashboard.md lints at 0 errors, 0 warnings.
scripts/check-contrast --self-test reproduces all five WCAG known answers, exit 0; the same script exits 1 on examples/fixture-clean.md at outline on surface 1.72:1 — proof that a clean lint is not a clean file.
scripts/extract-palette on the 200x100 three-band PNG scripts/install.sh builds: 3 of 3 clusters … covering 100.0% of counted pixels, exit 0. Detail: references/linter-rules.md §20–§22; scripts/README.md §3 and §8.
bash scripts/tests/test_e2e.sh re-checks every claim above, 36 assertions, each anchored to a defect this skill has shipped once. It includes scripts/tests/check-fabrication, which fails a file asserting values its source cannot support — an unsourced hex, or typography declared over a capture whose text is redacted. scripts/tests/trigger-eval.sh asks the separate question of whether the description routes at all; it is manual and billed, never CI. Detail: scripts/README.md §8.
The Linter Checks Form, Never Quality
Never let a clean run stand in for a review. What it checks: eleven rule
descriptors emitting fourteen (rule, severity) rows — an unresolvable reference,
an unrecognized component sub-token, a missing primary, intra-component contrast
at AA 4.5:1, orphaned colour names, empty spacing/rounded/typography, H2
order, misspelled and token-shaped keys, the three omitted outcomes.
What no rule checks: whether the type sizes form a modular scale, the
neutrals share a hue, every element uses one radius, fontFamily is system-ui,
the values were copied from Tailwind or Bootstrap, the accent carries more than
one role, Do's and Don'ts names this product, or all eight body sections are
present. Zero findings on a file with every one of those defects is the normal
case. Two gates close the opening, both part of the procedure. Quality —
references/anti-slop.md: sixteen prohibitions, each with a detection command
and a pass condition, plus a sixteen-step self-audit; the fixture opening that
file lints at zero errors and fails twelve of them. Accessibility —
scripts/check-contrast: upstream's contrast-ratio rule inspects only
components declaring both backgroundColor and textColor, only at AA 4.5:1,
never the palette, while this script walks every intended text-on-surface pair at
AAA and the 3.0:1 non-text threshold too.
omitted Exists So an Unknown Never Becomes a Fabrication
This is the single most important idea in the format. No source contains
everything: a screenshot has no font names, a brief has tone and no numbers, a
codebase has values and no rationale. Plausible filler lints clean and hides
which values were observed.
omitted:
- section: components
reason: "The screenshot showed one screen; no component inventory was visible."
- spacing
The valid names are exactly five — colors, typography, spacing, rounded,
components — and they name token maps, not body sections. elevation,
layout, states, dark-theme raise unknown-omission and belong in body
prose; naming a populated section raises redundant-omission.
Route 1 — from a text description
Classify the product: surface class (application UI or reading surface), UI
language, density, named constraints (brand colour, required font, a11y target). With fewer than two of {palette, typography, density} given, ask.
Pick the base template below; copy it to the user's path and edit the copy.
Substitute values while preserving the relationships: neutral step count,
scale ratio, spacing base unit, radius progression, one accent in one role,
every contrast pair the template had cleared. Keep the MD3 token names.
Rewrite ## Overview and ## Do's and Don'ts for this brand. Delete any
sentence that would still be true of a different product.
Put everything the description did not decide into omitted, with a reason.
Run Route 4. Detail: references/extraction.md §1.
Route 2 — from a screenshot
Read the image. Record its character in one sentence: light or dark, dense or
airy, how many surface levels, borders or shadow. Start from the template that Template Selection names below: take its section order and token vocabulary from there, and author every frontmatter value from the measurements rather than from the template.
Measure the colours with the script, never by eye:
scripts/extract-palette <ABSOLUTE-IMAGE-PATH> --min-share 0.1. The default floor of 0.5 sits right on top of a one-control accent: on a 1440x900
dashboard capture #e25a3c measures 0.52%, and --ignore-edges 4 — 57 px off each side, a quarter of the 233 px rail — pushes it to
0.40% and out of the report. Add that flag for a browser screenshot with chrome to crop, never for a full-app capture.
Assign roles yourself: largest share is background, the neutral inset from
it is surface, the high-chroma small-share colour on one control type is
primary. Name everything in the MD3 vocabulary; a boundary named outline-variant is exempt from the 3:1 non-text gate in check-contrast, so that suffix asserts the element is decorative — a divider that is the only thing separating two regions is structural and takes outline, which stays gated.
Recovery: with no accent candidate row, lower --min-share again; with the light planes collapsed into one row, lower --merge-distance — at 3.0 the 71.70% #ffffff splits into #ffffff 58.51% and #f7f4ef 16.56%.
Count what the image supports (surface levels, radius steps, type sizes), not absolute pixels — a retina capture scales all of them by an unknown ratio.
Do not name a font. Where a letterform is legible, offer two or three candidates, write one as a labelled placeholder, and ask. With no legible letterform — small text, heavy compression, a logo-only capture — offer no candidates and declare typography in omitted with that reason (references/linter-rules.md §12): a placeholder with no letterform behind it reads as a measured fact forever after.
Write only the components actually visible: two visible controls means two components, not a plausible library of eight. Give every component that renders a label a textColor: with a backgroundColor alone no contrast rule fires anywhere and check-contrast names it under UNCHECKED FILLS, a silence that reads exactly like a pass. --pair FG,BG measures a declared pair the matrix never prints, such as tertiary on surface.
Everything absent goes to omitted with a reason, or — where no section name fits (states, dark theme, motion, breakpoints) — to prose.
Run Route 4, then hand back a hypothesis with MEASURED, INFERRED,
NEEDS CONFIRMATION and OMITTED separated. Hand-back template and
reliability table: references/extraction.md §2 (B.5); a worked output of
this route: examples/example-saas-dashboard.md.
Route 3 — from a codebase
Harvest what is used, not what is declared: hex literals, var(--…)
references, @theme blocks, tailwind config entries, utility classes,
dimensions — each with a count. Commands: references/extraction.md §3 (C.1).
Rank by frequency and state the threshold applied: a value used once is
probably an accident, not a token. Check each value against the AS-10 table in references/anti-slop.md before it enters the token map: a framework default is often the top-count hex in a tree, and frequency makes it measured, never chosen — name it in the hand-back and get a brand value decided.
Collapse near values into steps and report every collapse: silent rounding destroys deliberate optical adjustments. Rename bg/accent into MD3 families.
Verify the round trip with export --format json-tailwind from /tmp against
the project config: a value present only in the codebase is a token the
extraction missed (references/export-formats.md §5).
Run Route 4, and hand back in the Route 2 buckets — MEASURED, INFERRED, NEEDS CONFIRMATION, OMITTED: a codebase settles values and leaves intent undetermined, and an inferred value written flat is indistinguishable from a harvested one. Note that export never lints: it exits 0 on a file with errors.
Route 4 — lint, fix, re-run (mandatory)
A procedure that ends without a clean lint run has not completed; handing an
unlinted draft back is the failure this skill exists to prevent.
scripts/lint <ABSOLUTE-PATH> — add --strict for anything gate-shaped,
since warnings carry the substantive complaints. references/linter-rules.md
§1 resolves any finding in one table.
Fix every finding, not only the errors, and re-run after each fix round.
scripts/lint --list-rules prints all fourteen rows with a remedy, offline.
Read the token-summary counts back against what you wrote: a bad reference
in colors/spacing/rounded, or a unitless number, drops the token in
silence, and that count is the only trace of it.
Quote the final summary triple verbatim in the hand-back.
Template Selection
Copy one file out of assets/ and edit the copy. First match wins.
#
Condition
Template
R0
The product ships a Russian-language UI (override — beats every rule below)
R0 is correctness, not taste. Google Fonts serves Instrument Serif and Bodoni
Moda with latin and latin-ext subsets only, so Russian text in those families
falls back silently to a system serif. Families verified to carry Cyrillic: Golos
Text, Onest, Inter, IBM Plex Sans, JetBrains Mono, Manrope. Where R0 and R2 both
apply, base on template-cyrillic.md and import only the relationships from
template-editorial.md. Every template lints at 0 errors, holds the eight body
sections in canonical order, and names colours in MD3 families.
Traps to Know Before Writing a Line
Quote every hex — unquoted, #RRGGBB is a YAML comment and the value becomes an
error reading 'null' is not a valid color. Quote every {reference} —
unquoted, { opens a flow mapping and no rule fires at all. Write units: a bare
16 in a Dimension slot drops the token with no finding. Only ## headings reach
section-order, Do's and Don'ts MUST use the ASCII apostrophe, and a fenced
yaml block anywhere in a DESIGN.md body is merged into the token set — a
top-level key colliding with the frontmatter then halts every rule at exit 0.
Tables: references/spec-anatomy.md §11, references/export-formats.md §9.
References Map
File
Load it when
references/spec-anatomy.md
Writing or repairing frontmatter: the nine keys, value types, references, the closed eight component sub-tokens, omitted, the eight body sections, a verified-traps table.
references/linter-rules.md
A finding needs resolving: fourteen rule rows with verbatim messages and remedies, the exit-code contract, the rule-less parser and model findings, both fixtures quoted in full.
references/extraction.md
Executing Route 1, 2 or 3: full procedures, the screenshot reliability table, the hand-back template, the codebase harvest commands.
references/anti-slop.md
Before handing any file back: sixteen prohibitions, each with a detection command and a pass condition, plus the self-audit checklist.
references/export-formats.md
Exporting or comparing revisions: the five export formats and their coverage matrix, round-trip recipes, diff semantics, corrections to the upstream help text.
examples/example-saas-dashboard.md
A finished screenshot extraction with its reliability ledger, to read against your own draft.
The known-good and known-bad controls the documented lint output was measured on.
scripts/README.md
A script misbehaves, an exit code needs explaining, or the version pin needs bumping.
Quality Checklist
scripts/lint --strict FILE exits 0 — or every remaining finding is a contrast-ratio shortfall between two colours measured from the source, each named with its ratio and justified; no other rule is exempt, and no measured value is edited to clear a finding. The summary triple is quoted in the hand-back rather than summarised as "clean".
scripts/check-contrast FILE exits 0, or every failing pair is named and justified, and the token-summary counts equal the keys actually written in each map.
All eight body sections are present in canonical order at ## level, with the ASCII apostrophe in Do's and Don'ts (a house rule; no linter rule enforces it).
Colour tokens use MD3 family names, primary exists, and every non-baseline name is referenced by a component.
Every section the source did not settle is in omitted with a reason, no populated section appears there, and nothing anywhere is invented.
The references/anti-slop.md self-audit passes, or each miss is explained: no copied framework hexes, no system-ui first family, more than one radius referenced.
## Overview and ## Do's and Don'ts name this product and its own tokens; delete any rule that would read identically in another product's file, and confirm nothing under assets/, examples/ or references/ was modified.
For an extraction: measured, inferred and unknown are separated in the hand-back, and no font family is asserted as observed.
extract-palette reads non-interlaced PNG at bit depth 8/16 with its own stdlib
decoder — the shape of every macOS and Linux screenshot — so the screenshot route
runs with no install at all. bun is required nowhere and MUST not be added.
The version pin is deliberate: the format declares version: alpha and this
skill quotes upstream messages verbatim. Bumping it is a change to the skill —
follow references/export-formats.md §8.1 and scripts/README.md §5, moving the
pin in lint, check-contrast and install.sh together. This skill is
Apache-2.0 and carries no upstream source or examples.
1---2name: design-md3description: Use when authoring, auditing, or exporting a DESIGN.md design system — design tokens, tailwind config, deslopify a generic look, дизайн-система, фирменные цвета, единый стиль, извлеки палитру по скриншоту, derive one from a brief, or extract one from a codebase or its CSS.4---56# DESIGN.md Design Systems78## Purpose910DESIGN.md records a design system as machine-readable YAML tokens plus a prose11rationale, so an implementing agent stops using its own defaults. Three gaps12stand between an agent and a usable file:13141. **Syntactic** — schema, references, the eight body sections. Closed by `references/spec-anatomy.md`.152. **Procedural** — verifiable output handed back unverified. Closed by `scripts/lint` and Route 4.163. **Substantive** — a *valid* file is not a *useful* one. Closed by `references/anti-slop.md`, and by nothing else.1718The third bites hardest because nothing reports it: a file of framework defaults,19`system-ui`, one radius and four truisms lints at zero errors. Toolchain:20`@google/design.md@0.4.0` (Apache-2.0, Google LLC), run via `npx`.2122## When to Use This Skill2324Use this skill when the user:2526- asks for a DESIGN.md, a design system, a token file, or design tokens;27- asks to extract a palette from a screenshot, a mockup, or an image, or to28 derive a system from a brand brief, a codebase, or a tailwind config;29- asks to audit, repair or lint an existing DESIGN.md, export its tokens to30 Tailwind v3/v4, DTCG or CSS custom properties, or compare two revisions;31- says the output looks generic and asks to deslopify it, or writes any of32 дизайн-система, фирменные цвета, единый стиль, извлеки палитру, по скриншоту.3334Out of scope: images, logos, component implementation, palettes derived from one35seed colour. This skill produces the specification, not the interface.3637## Red Flags3839**STOP and READ if you are thinking:**4041- "The linter found no errors, so the file is finished." → **WRONG**. Only an42 unresolvable reference and an unparseable token value move the exit code.43 `examples/fixture-clean.md` lints `0 errors, 0 warnings, 1 info`, yet44 `scripts/check-contrast` exits 1 on it: `outline` measures 1.72:1 on `surface`,45 under the 3.0:1 non-text floor. Exit 0 is the entry requirement, not a verdict.46- "I recognise the typeface in this screenshot." → **WRONG**. Grotesques are47 indistinguishable at UI sizes, and a family name in `fontFamily` reads as a48 measured fact forever after. Offer candidates, label yours a placeholder, ask.49- "The brief said nothing about spacing, so a 4/8/16/24 ladder is safe." →50 **WRONG**. An invented value and a measured value are indistinguishable in51 YAML. Declare the section in `omitted` — that field exists for exactly this.52- "`ink` and `paper` describe this palette better than `primary` and `surface`."53 → **WRONG**. `orphaned-tokens` emits one warning per colour whose family sits54 outside the MD3 baseline and which no component references. Deleting55 `components` silences them — the rule returns early on an empty map — and56 leaves a token dump.5758## Rationalization Table5960| Agent Excuse | Reality |61| :--- | :--- |62| "Exit code 0 means the file passed." | Exactly one rule produces errors. A file with no `primary`, no typography, an out-of-order body and a misspelled key that `export` drops still exits 0. Report the `summary` triple, never the exit code alone. |63| "The linter said nothing about my palette, so the palette is fine." | No rule reads a hue ramp, a type scale, a radius set, or one word of prose. `references/anti-slop.md` is the only check that does. |64| "I will read the hex values off the screenshot myself." | Compression, antialiasing, gradients and the colour profile each move the value. A hex written from perception is a fabrication shaped like a measurement. `scripts/extract-palette` measures; you assign roles. |65| "`#3b82f6` is a safe accent to start from." | That is Tailwind blue-500. It measures 3.68:1 on white, below AA. Six of the nine most-copied framework accents fail 4.5:1 on white, and readers recognise them on sight. |66| "`token-summary` is noise; I skip the info lines." | It is the only trace of a silently dropped token: a `spacing` value written `16` with no unit vanishes and raises no finding at all. Read the counts back against what you wrote. |67| "I will add `elevation` to the component." | The sub-token set is closed at eight names. An invented key is a `broken-ref` warning and every exporter drops it. Depth belongs in `## Elevation & Depth` prose. |68| "One lint pass is enough." | `section-order` reports at most one inversion per run, and `orphaned-tokens` stays silent until the first component exists. Re-lint after every fix round. |69| "The template in `assets/` is close enough to ship." | `template-skeleton.md` lints clean carrying a conspicuous magenta accent and fifteen `PROVISIONAL` markers; a clean lint on an unedited copy proves only that the structure survived. |7071## Execution Mode7273- **Mode**: `hybrid`74- **Why this mode**: measurement is script work, judgement is not. Colour values,75 contrast ratios and lint findings come from `scripts/` and the upstream CLI,76 because each is a number an agent gets wrong by eye. Template choice, role77 assignment, naming and the prose body are the agent's alone, and every route78 below ends in a script run.7980## Script Contract8182Three executables in `scripts/`, all Python 3, all **read-only**: each writes a83report to stdout and modifies no file. Pass absolute paths.8485| Script | Command and flags | Output |86| :--- | :--- | :--- |87| `lint` | `scripts/lint FILE…` · `--strict` `--json` `--list-rules` `--version PIN` `--timeout SECONDS` | a `severity/rule/path/message` table per file, a `REMEDY` line per rule id that fired, and a `PASS`/`FAIL` verdict carrying the upstream `summary` triple |88| `check-contrast` | `scripts/check-contrast FILE` · `--level aa\|aaa\|both` `--min RATIO` `--pair FG,BG` `--matrix summary\|plausible\|full` `--strict-decorative` `--self-test` `--timeout SECONDS` `--json` | WCAG 2.x ratios for every intended text-on-surface pair, a typography table and an advisory matrix, at AA 4.5, AA-large 3.0, AAA 7.0 and the 3.0 non-text threshold; `--pair` answers one pair the matrix never prints, `UNCHECKED FILLS` every component fill with no `textColor` |89| `extract-palette` | `scripts/extract-palette IMAGE` · `--colors N` `--ignore-edges PCT` `--region X,Y,W,H` `--min-share PCT` `--merge-distance D` `--json` | dominant colours as `HEX`, `SHARE`, `BUCKETS`, `HUE`, `L*`, `C*` and a heuristic `HINT` |9091`--strict` fails `lint` on warnings, which is what makes a gate meaningful, and92`--list-rules` prints all fourteen rule rows offline. **Exit codes differ per script:**9394| Code | `lint` | `check-contrast` | `extract-palette` |95| :--- | :--- | :--- | :--- |96| 0 | no errors (and no warnings under `--strict`) | every gated intended pair meets the gate — under `--pair`, every queried pair | success |97| 1 | errors, or warnings under `--strict` | a gated intended pair fails — under `--pair`, a queried pair does — or `--self-test` mismatched | an out-of-range option value, or a `--region` malformed, outside the image, or combined with `--ignore-edges` |98| 2 | input problem, or a usage error | input problem, a usage error, or a `--pair` token name that is unknown or has no ratio | image missing, unreadable, or corrupt |99| 3 | npx or the CLI unavailable, the call timed out, or non-JSON output | the `export` call failed, or no colors defined | no available decoder handles the format |100101With several files `lint` returns the worst code, ordered `3 > 2 > 1 > 0`.102**Artifacts: none** — no script writes a file. Exit 3 means "the tool did not103run", never "the file is fine": report no verdict from such a run. The raw form,104for `diff`, `spec` and anything the wrappers omit:105106```text107cd /tmp && npx --yes @google/design.md@0.4.0 lint|diff|export|spec <ABSOLUTE-PATH>108```109110`cd /tmp` is load-bearing: a workspace declaring the same bin name shadows it.111`export` requires `--format` (`css-tailwind`, `json-tailwind`, `tailwind`,112`dtcg`, `css-vars`); `lint --format text` silently returns JSON. Full flags: each113`--help` and `scripts/README.md` §2 (`--version PIN` takes an argument).114115## Safety Boundaries116117- **Writable scope**: only the DESIGN.md the user asked for, at a path they named118 or in their working directory — nothing else in their tree.119- **Never overwrite the skill's own material**: `assets/`, `examples/` and120 `references/` are read-only, every template and fixture lint-verified as it121 stands. Copy a template out and edit the copy.122- **Network**: `lint` and `check-contrast` shell out to123 `npx --yes @google/design.md@0.4.0`, which reaches `registry.npmjs.org` on the first call124 — roughly 30 seconds, then the npx cache, and the skill's only network access. `lint`125 announces that wait on stderr; `check-contrast` prints nothing until its report, so a126 cold run of it looks hung. Offline with a cold cache both exit 3, never a clean verdict.127- **User-supplied images**: `extract-palette` decodes an image the user supplied,128 reading pixels and writing nothing; it spawns no process and makes no network129 call. Confirm the path, do not fetch an image from a URL unless asked.130- **Installs**: `bash scripts/install.sh` is optional and puts Pillow into131 `scripts/.venv/` only — nothing global. **Never** vendor upstream `design.md`132 source or its `examples/` here.133134## Validation Evidence135136- Every `assets/template-*.md` and `examples/example-saas-dashboard.md` lints at 0 errors, 0 warnings.137- `examples/fixture-clean.md` → exit 0, `{"errors": 0, "warnings": 0, "infos": 1}`; `examples/fixture-broken.md` → exit 1, `{"errors": 1, "warnings": 6, "infos": 1}`.138- `scripts/check-contrast --self-test` reproduces all five WCAG known answers, exit 0; the same script exits 1 on `examples/fixture-clean.md` at `outline on surface` 1.72:1 — proof that a clean lint is not a clean file.139- `scripts/extract-palette` on the 200x100 three-band PNG `scripts/install.sh` builds: `3 of 3 clusters … covering 100.0% of counted pixels`, exit 0. Detail: `references/linter-rules.md` §20–§22; `scripts/README.md` §3 and §8.140- `bash scripts/tests/test_e2e.sh` re-checks every claim above, 36 assertions, each anchored to a defect this skill has shipped once. It includes `scripts/tests/check-fabrication`, which fails a file asserting values its source cannot support — an unsourced hex, or `typography` declared over a capture whose text is redacted. `scripts/tests/trigger-eval.sh` asks the separate question of whether the description routes at all; it is manual and billed, never CI. Detail: `scripts/README.md` §8.141142## The Linter Checks Form, Never Quality143144Never let a clean run stand in for a review. **What it checks**: eleven rule145descriptors emitting fourteen `(rule, severity)` rows — an unresolvable reference,146an unrecognized component sub-token, a missing `primary`, intra-component contrast147at AA 4.5:1, orphaned colour names, empty `spacing`/`rounded`/`typography`, H2148order, misspelled and token-shaped keys, the three `omitted` outcomes.149150**What no rule checks**: whether the type sizes form a modular scale, the151neutrals share a hue, every element uses one radius, `fontFamily` is `system-ui`,152the values were copied from Tailwind or Bootstrap, the accent carries more than153one role, `Do's and Don'ts` names *this* product, or all eight body sections are154present. Zero findings on a file with every one of those defects is the normal155case. Two gates close the opening, both part of the procedure. **Quality** —156`references/anti-slop.md`: sixteen prohibitions, each with a detection command157and a pass condition, plus a sixteen-step self-audit; the fixture opening that158file lints at zero errors and fails twelve of them. **Accessibility** —159`scripts/check-contrast`: upstream's `contrast-ratio` rule inspects only160components declaring both `backgroundColor` and `textColor`, only at AA 4.5:1,161never the palette, while this script walks every intended text-on-surface pair at162AAA and the 3.0:1 non-text threshold too.163164## `omitted` Exists So an Unknown Never Becomes a Fabrication165166**This is the single most important idea in the format.** No source contains167everything: a screenshot has no font names, a brief has tone and no numbers, a168codebase has values and no rationale. Plausible filler lints clean and hides169which values were observed.170171```yaml172omitted:173 - section: components174 reason: "The screenshot showed one screen; no component inventory was visible."175 - spacing176```177178The valid names are exactly five — `colors`, `typography`, `spacing`, `rounded`,179`components` — and they name token maps, not body sections. `elevation`,180`layout`, `states`, `dark-theme` raise `unknown-omission` and belong in body181prose; naming a populated section raises `redundant-omission`.182183## Route 1 — from a text description1841851. Classify the product: surface class (application UI or reading surface), UI186 language, density, named constraints (brand colour, required font, a11y target). With fewer than two of {palette, typography, density} given, ask.1872. Pick the base template below; copy it to the user's path and edit the copy.1883. Substitute values while preserving the **relationships**: neutral step count,189 scale ratio, spacing base unit, radius progression, one accent in one role,190 every contrast pair the template had cleared. Keep the MD3 token names.1914. Rewrite `## Overview` and `## Do's and Don'ts` for this brand. Delete any192 sentence that would still be true of a different product.1935. Put everything the description did not decide into `omitted`, with a reason.1946. Run Route 4. Detail: `references/extraction.md` §1.195196## Route 2 — from a screenshot1971981. Read the image. Record its character in one sentence: light or dark, dense or199 airy, how many surface levels, borders or shadow. Start from the template that Template Selection names below: take its section order and token vocabulary from there, and author every frontmatter value from the measurements rather than from the template.2002. **Measure the colours with the script**, never by eye:201 `scripts/extract-palette <ABSOLUTE-IMAGE-PATH> --min-share 0.1`. The default floor of 0.5 sits right on top of a one-control accent: on a 1440x900202 dashboard capture `#e25a3c` measures 0.52%, and `--ignore-edges 4` — 57 px off each side, a quarter of the 233 px rail — pushes it to203 0.40% and out of the report. Add that flag for a browser screenshot with chrome to crop, never for a full-app capture.2043. Assign roles yourself: largest share is `background`, the neutral inset from205 it is `surface`, the high-chroma small-share colour on one control type is206 `primary`. Name everything in the MD3 vocabulary; a boundary named `outline-variant` is exempt from the 3:1 non-text gate in `check-contrast`, so that suffix asserts the element is decorative — a divider that is the only thing separating two regions is structural and takes `outline`, which stays gated.207 Recovery: with no `accent candidate` row, lower `--min-share` again; with the light planes collapsed into one row, lower `--merge-distance` — at 3.0 the 71.70% `#ffffff` splits into `#ffffff` 58.51% and `#f7f4ef` 16.56%.2084. Count what the image supports (surface levels, radius steps, type sizes), not absolute pixels — a retina capture scales all of them by an unknown ratio.2095. Do not name a font. Where a letterform is legible, offer two or three candidates, write one as a labelled placeholder, and ask. With no legible letterform — small text, heavy compression, a logo-only capture — offer no candidates and declare `typography` in `omitted` with that reason (`references/linter-rules.md` §12): a placeholder with no letterform behind it reads as a measured fact forever after.2106. Write only the components actually visible: two visible controls means two components, not a plausible library of eight. Give every component that renders a label a `textColor`: with a `backgroundColor` alone no contrast rule fires anywhere and `check-contrast` names it under `UNCHECKED FILLS`, a silence that reads exactly like a pass. `--pair FG,BG` measures a declared pair the matrix never prints, such as `tertiary` on `surface`.2117. Everything absent goes to `omitted` with a reason, or — where no section name fits (states, dark theme, motion, breakpoints) — to prose.2128. Run Route 4, then hand back a **hypothesis** with `MEASURED`, `INFERRED`,213 `NEEDS CONFIRMATION` and `OMITTED` separated. Hand-back template and214 reliability table: `references/extraction.md` §2 (B.5); a worked output of215 this route: `examples/example-saas-dashboard.md`.216217## Route 3 — from a codebase2182191. Harvest what is **used**, not what is declared: hex literals, `var(--…)`220 references, `@theme` blocks, tailwind config entries, utility classes,221 dimensions — each with a count. Commands: `references/extraction.md` §3 (C.1).2222. Rank by frequency and state the threshold applied: a value used once is223 probably an accident, not a token. Check each value against the AS-10 table in `references/anti-slop.md` before it enters the token map: a framework default is often the top-count hex in a tree, and frequency makes it measured, never chosen — name it in the hand-back and get a brand value decided.2243. Collapse near values into steps and **report every collapse**: silent rounding destroys deliberate optical adjustments. Rename `bg`/`accent` into MD3 families.2254. Verify the round trip with `export --format json-tailwind` from `/tmp` against226 the project config: a value present only in the codebase is a token the227 extraction missed (`references/export-formats.md` §5).2285. Run Route 4, and hand back in the Route 2 buckets — `MEASURED`, `INFERRED`, `NEEDS CONFIRMATION`, `OMITTED`: a codebase settles values and leaves intent undetermined, and an inferred value written flat is indistinguishable from a harvested one. Note that `export` never lints: it exits 0 on a file with errors.229230## Route 4 — lint, fix, re-run (mandatory)231232A procedure that ends without a clean lint run has not completed; handing an233unlinted draft back is the failure this skill exists to prevent.2342351. `scripts/lint <ABSOLUTE-PATH>` — add `--strict` for anything gate-shaped,236 since warnings carry the substantive complaints. `references/linter-rules.md`237 §1 resolves any finding in one table.2382. Fix **every** finding, not only the errors, and re-run after each fix round.239 `scripts/lint --list-rules` prints all fourteen rows with a remedy, offline.2403. Read the `token-summary` counts back against what you wrote: a bad reference241 in `colors`/`spacing`/`rounded`, or a unitless number, drops the token in242 silence, and that count is the only trace of it.2434. Quote the final `summary` triple verbatim in the hand-back.244245## Template Selection246247Copy one file out of `assets/` and edit the copy. First match wins.248249| # | Condition | Template |250| :--- | :--- | :--- |251| R0 | The product ships a Russian-language UI (override — beats every rule below) | `assets/template-cyrillic.md` |252| R1 | Application UI: dashboard, admin, console, CRM, IDE, analytics, SaaS | `assets/template-product-saas.md` |253| R2 | Reading surface: magazine, blog, docs, newsletter, essay, portfolio | `assets/template-editorial.md` |254| R3 | Outside R1 and R2 — game, print, hardware panel, embedded display | `assets/template-skeleton.md` |255| R4 | Too thin to classify, or authoring from zero | `assets/template-skeleton.md` |256257**R0 is correctness, not taste.** Google Fonts serves Instrument Serif and Bodoni258Moda with `latin` and `latin-ext` subsets only, so Russian text in those families259falls back silently to a system serif. Families verified to carry Cyrillic: Golos260Text, Onest, Inter, IBM Plex Sans, JetBrains Mono, Manrope. Where R0 and R2 both261apply, base on `template-cyrillic.md` and import only the *relationships* from262`template-editorial.md`. Every template lints at 0 errors, holds the eight body263sections in canonical order, and names colours in MD3 families.264265## Traps to Know Before Writing a Line266267Quote every hex — unquoted, `#RRGGBB` is a YAML comment and the value becomes an268**error** reading `'null' is not a valid color.` Quote every `{reference}` —269unquoted, `{` opens a flow mapping and no rule fires at all. Write units: a bare270`16` in a Dimension slot drops the token with no finding. Only `##` headings reach271`section-order`, `Do's and Don'ts` MUST use the ASCII apostrophe, and a fenced272`yaml` block anywhere in a DESIGN.md body is merged into the token set — a273top-level key colliding with the frontmatter then halts every rule at exit 0.274Tables: `references/spec-anatomy.md` §11, `references/export-formats.md` §9.275276## References Map277278| File | Load it when |279| :--- | :--- |280| `references/spec-anatomy.md` | Writing or repairing frontmatter: the nine keys, value types, references, the closed eight component sub-tokens, `omitted`, the eight body sections, a verified-traps table. |281| `references/linter-rules.md` | A finding needs resolving: fourteen rule rows with verbatim messages and remedies, the exit-code contract, the rule-less parser and model findings, both fixtures quoted in full. |282| `references/extraction.md` | Executing Route 1, 2 or 3: full procedures, the screenshot reliability table, the hand-back template, the codebase harvest commands. |283| `references/anti-slop.md` | Before handing any file back: sixteen prohibitions, each with a detection command and a pass condition, plus the self-audit checklist. |284| `references/export-formats.md` | Exporting or comparing revisions: the five `export` formats and their coverage matrix, round-trip recipes, `diff` semantics, corrections to the upstream help text. |285| `examples/example-saas-dashboard.md` | A finished screenshot extraction with its reliability ledger, to read against your own draft. |286| `examples/fixture-clean.md`, `examples/fixture-broken.md` | The known-good and known-bad controls the documented lint output was measured on. |287| `scripts/README.md` | A script misbehaves, an exit code needs explaining, or the version pin needs bumping. |288289## Quality Checklist290291- [ ] `scripts/lint --strict FILE` exits 0 — or every remaining finding is a `contrast-ratio` shortfall between two colours measured from the source, each named with its ratio and justified; no other rule is exempt, and no measured value is edited to clear a finding. The `summary` triple is quoted in the hand-back rather than summarised as "clean".292- [ ] `scripts/check-contrast FILE` exits 0, or every failing pair is named and justified, and the `token-summary` counts equal the keys actually written in each map.293- [ ] All eight body sections are present in canonical order at `##` level, with the ASCII apostrophe in `Do's and Don'ts` (a house rule; no linter rule enforces it).294- [ ] Colour tokens use MD3 family names, `primary` exists, and every non-baseline name is referenced by a component.295- [ ] Every section the source did not settle is in `omitted` with a `reason`, no populated section appears there, and nothing anywhere is invented.296- [ ] The `references/anti-slop.md` self-audit passes, or each miss is explained: no copied framework hexes, no `system-ui` first family, more than one radius referenced.297- [ ] `## Overview` and `## Do's and Don'ts` name *this* product and its own tokens; delete any rule that would read identically in another product's file, and confirm nothing under `assets/`, `examples/` or `references/` was modified.298- [ ] For an extraction: measured, inferred and unknown are separated in the hand-back, and no font family is asserted as observed.299300## Dependencies301302| Dependency | Required for | Install |303| :--- | :--- | :--- |304| Python 3 (stdlib only) | all three scripts | present on the host |305| Node.js 18+ | `npx`, used by `lint` and `check-contrast` | https://nodejs.org/ |306| `@google/design.md@0.4.0` | the linter, `export`, `diff`, `spec` | fetched by `npx --yes` on first use, then cached; **never vendored** |307| Pillow (optional) | `extract-palette` on JPEG, WebP, HEIF, interlaced PNG | `bash skills/design-md/scripts/install.sh` → `scripts/.venv/` |308309`extract-palette` reads non-interlaced PNG at bit depth 8/16 with its own stdlib310decoder — the shape of every macOS and Linux screenshot — so the screenshot route311runs with no install at all. `bun` is required nowhere and MUST not be added.312313The version pin is deliberate: the format declares `version: alpha` and this314skill quotes upstream messages verbatim. Bumping it is a change to the skill —315follow `references/export-formats.md` §8.1 and `scripts/README.md` §5, moving the316pin in `lint`, `check-contrast` and `install.sh` together. This skill is317Apache-2.0 and carries no upstream source or examples.
Run npx skillmds@latest add matrixfounder/design-md in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use when authoring, auditing, or exporting a DESIGN.md design system — design tokens, tailwind config, deslopify a generic look, дизайн-система, фирменные цвета, единый стиль, извлеки палитру по скриншоту, derive one from a brief, or extract one from a codebase or its CSS. It is listed under Design & Media on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
MatrixFounder (@matrixfounder) published this skill. Their other Agent Skills are listed on their SkillMD profile.