Appearance
Inject DESIGN.md context into a session
Goal: load a project's committed visual identity (its
DESIGN.md) plus the format's lint rules into agent context, so UI work stays on-brand without re-deriving the design system each session. Concepts and the decisions behind this surface live in the DESIGN.md explanation.
Prerequisites
- The project has a committed
DESIGN.mdat its root (or you want the clean no-op guidance for adopting one). - The
design-md-contextregistry skill is provisioned to the project (cdprov add skill design-md-context), or you invoke the script directly. jq; network for the firstnpxfetch of the pinned CLI (offline runs degrade gracefully — see below).
From a Claude Code session
Provisioned projects load the skill automatically; ask for the design system ("load the design tokens", "apply the design system") or run the script as the skill instructs. The skill treats the emitted envelope as authoritative design context for all subsequent UI generation.
From a regular terminal
The script is a plain executable with no session dependency — there is no global bin/ entrypoint because the skill is provisioned per-project (web apps only), while bin/ is reserved for universal tooling:
bash
~/.claude/registry/skills/design-md-context/scripts/design-context.sh /path/to/projectWorked example
A project with this DESIGN.md at its root:
md
---
name: Heritage
colors:
primary: "#1A1C1E"
neutral: "#F7F5F2"
typography:
body-md:
fontFamily: Public Sans
fontSize: 1rem
rounded:
md: 8px
spacing:
md: 16px
---
## Overview
Architectural minimalism — deep ink on warm limestone.Running the script:
bash
$ ~/.claude/registry/skills/design-md-context/scripts/design-context.sh ~/projects/heritage-site
{
"present": true,
"path": "/Users/you/projects/heritage-site/DESIGN.md",
"design_md": "---\nname: Heritage\ncolors:\n primary: \"#1A1C1E\"\n ...",
"rules": {
"rules": [
{ "name": "broken-ref", "severity": "error", "description": "Broken/circular references and unknown component sub-tokens." },
{ "name": "contrast-ratio", "severity": "warning", "description": "WCAG contrast ratio — warns when component backgroundColor/textColor pairs fall below the AA minimum of 4.5:1." }
]
},
"rules_source": "spec-rules-only",
"cli": "@google/design.md@0.3.0"
}design_md is the verbatim file — inject tokens and prose. rules is what the lint gate (#903) will enforce, so the agent designs within the rules rather than discovering them at commit time.
On a project without a DESIGN.md:
bash
$ ~/.claude/registry/skills/design-md-context/scripts/design-context.sh /tmp/some-cli-repo
{
"present": false,
"root": "/tmp/some-cli-repo",
"guidance": "No DESIGN.md at the project root. To adopt one: author it by hand (YAML front-matter tokens + prose rationale), extract it from existing code, or generate it upstream via Google Stitch. See docs/explanation/design-md.md in the BoB source repo."
}Exit code is 0 in both cases — absence is a legitimate state, not an error.
Degraded modes
| Condition | Behavior |
|---|---|
No DESIGN.md | present: false + adoption guidance, exit 0. |
npx unreachable (offline) | Envelope still emitted; rules: null, rules_source: "unavailable"; the skill's built-in format essentials cover the gap. |
Missing jq | Hard error, exit 1 — the only real failure mode. |
Output is deterministic for a given DESIGN.md — no timestamps, safe to cache and diff.
Enable the lint gate
The same skill ships a lint gate with two wirings — session hook and CI check — both delegating to scripts/design-lint-check.sh (exit 0 pass or no-DESIGN.md no-op, 1 findings at threshold, 2 environment failure).
Session hook (lint on every agent edit)
Add to the project's .claude/settings.json. The command is bare — the hook reads stdin JSON per the BoB hook convention:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "~/.claude/registry/skills/design-md-context/hooks/design-md-lint-hook.sh"
}
]
}
]
}
}Edits to any other file pass silently; a DESIGN.md edit that fails lint feeds the findings straight back to the agent (exit-2 stderr), so broken token references or WCAG contrast failures are fixed in the same turn they are introduced.
CI check
Run the check script as a CI step (or a checks entry in dev.json so warp-drive's check-runner picks it up):
yaml
# .github/workflows/ci.yml
- name: DESIGN.md lint
run: ~/.claude/registry/skills/design-md-context/scripts/design-lint-check.sh . --fail-on warningjson
// dev.json
{ "checks": [ { "name": "design-md-lint", "kind": "lint",
"command": "~/.claude/registry/skills/design-md-context/scripts/design-lint-check.sh ." } ] }Findings are structured JSON on stdout (severity, path, message, summary counts), so downstream tooling parses results instead of scraping text.
Windows: the CLI binary design.md breaks Windows cmd shims, so upstream also ships a designmd alias. Both wirings invoke the package pin via npx @google/design.md@0.3.0, which resolves the correct per-platform shim — no OS branching needed in project config.
Enable the diff check (PR regression gate)
scripts/design-diff-check.sh diffs the base-branch DESIGN.md against the head and fails on regression — wire it as a PR check:
yaml
# .github/workflows/ci.yml — PRs need the base ref available
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: DESIGN.md diff gate
run: ~/.claude/registry/skills/design-md-context/scripts/design-diff-check.sh . --gate removedPick the gate by how strict the design system is:
--gate | Fails when | Use for |
|---|---|---|
removed (default) | A token is removed or the file deleted | Most projects — additive change stays cheap. |
modified | Removed or any token value changed | Locked-down brand systems. |
any | Any token change, including additions | Systems where every token change needs design sign-off. |
none | Never (advisory) | Annotate-only: parse the JSON and comment on the PR. |
A DESIGN.md that is new in the branch passes (nothing to regress against); one deleted in the branch fails any non-advisory gate. --base <ref> overrides the default merge-base comparison.
Export tokens for downstream consumers
design.md export derives build-ready token artifacts from the committed DESIGN.md — it owns the token→Tailwind path wherever a DESIGN.md exists. (scaffold-tokens.sh from the design-systems skill remains only for projects with a standalone tokens.json and no DESIGN.md — never run both, per the reconciliation.)
--format | Output | Consumer |
|---|---|---|
css-tailwind | Tailwind v4 CSS @theme block of custom properties | Tailwind v4 projects — import the CSS. |
json-tailwind (alias tailwind) | Tailwind v3 theme.extend JSON | Tailwind v3 tailwind.config.js. |
dtcg | W3C Design Tokens (2025.10 schema) tokens.json | Style Dictionary, Figma token plugins, any DTCG-aware pipeline. |
Worked example
Generating the Tailwind v4 theme for the Heritage project from the earlier example, committed as a derived artifact:
bash
$ cd ~/projects/heritage-site
$ npx @google/design.md@0.3.0 export DESIGN.md --format css-tailwind > src/styles/design-tokens.css
$ cat src/styles/design-tokens.css
@theme {
--color-primary: #1a1c1e;
--color-neutral: #f7f5f2;
--font-body-md: "Public Sans";
--text-body-md: 1rem;
--radius-md: 8px;
--spacing-md: 16px;
}The same source emits a Tailwind v3 config fragment —
bash
$ npx @google/design.md@0.3.0 export DESIGN.md --format json-tailwind
{
"theme": {
"extend": {
"colors": { "primary": "#1a1c1e", "neutral": "#f7f5f2" },
"fontFamily": { "body-md": ["Public Sans"] },
...
}
}
}— or a DTCG tokens.json (--format dtcg) for design-tool pipelines. Re-export in CI after any DESIGN.md change (or in the same commit), so the derived artifacts never drift from the owned source.