Skip to content

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.md at its root (or you want the clean no-op guidance for adopting one).
  • The design-md-context registry skill is provisioned to the project (cdprov add skill design-md-context), or you invoke the script directly.
  • jq; network for the first npx fetch 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/project

Worked 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

ConditionBehavior
No DESIGN.mdpresent: 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 jqHard 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 warning
json
// 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 removed

Pick the gate by how strict the design system is:

--gateFails whenUse for
removed (default)A token is removed or the file deletedMost projects — additive change stays cheap.
modifiedRemoved or any token value changedLocked-down brand systems.
anyAny token change, including additionsSystems where every token change needs design sign-off.
noneNever (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.mdit 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.)

--formatOutputConsumer
css-tailwindTailwind v4 CSS @theme block of custom propertiesTailwind v4 projects — import the CSS.
json-tailwind (alias tailwind)Tailwind v3 theme.extend JSONTailwind v3 tailwind.config.js.
dtcgW3C Design Tokens (2025.10 schema) tokens.jsonStyle 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.