Skip to content

feat(cli): ship @google/design.md CLI toolkit#5

Merged
davideast merged 1 commit into
mainfrom
ship/cli
Apr 13, 2026
Merged

feat(cli): ship @google/design.md CLI toolkit#5
davideast merged 1 commit into
mainfrom
ship/cli

Conversation

@davideast

Copy link
Copy Markdown
Collaborator

The PR introduces a CLI package to be published as @google/design.md and specification changes for precision and extensibility.

Specification changes (docs/spec.md)

Generated from source

The spec is no longer hand-edited. It's now generated from spec.mdx + spec-config.yaml via a compiler, and the output carries a header comment:

<!-- Generated from spec.mdx + spec-config.ts | version: alpha -->
<!-- Do not edit directly. Run `bun run spec:gen` to regenerate. -->

This means that values like supported units, section order, typography properties, and component sub-tokens are defined once in spec-config.yaml and compiled into the spec. The linter also reads from this same config, so the spec and the tooling read from the same source of truth to prevent drift across changes.

New sections

Recommended Token Names (Non-Normative)

This section provides suggested names for typography (headline-display, body-md, label-sm, etc.), spacing (unit, gutter, xsxl), and rounded (none, smfull) tokens. These are recommendations, not requirements. They give agents and humans a shared vocabulary without mandating it.

Consumer Behavior for Unknown Content

A table specifying what a compliant consumer (agent or tool) should do when it encounters content not defined by the spec. For example: unknown section headings should be preserved without error, unknown color token names should be accepted if the value is valid hex, but duplicate section headings should be rejected. This section is written to support forward compatibility, as it tells consumers how to be lenient in the right ways.

Schema changes

Change Before After
Version field Not present version: <string> (optional, current: "alpha")
Spacing value type <Dimension> <Dimension | number> (allows unitless values like column counts)
Component sub-tokens backgroundColor, textColor, typography, rounded, padding Added size, height, width
Description field Implicit Explicitly marked optional in schema
Schema placeholders token-name, level <token-name>, <scale-level> (angle-bracket notation)

Section changes

Section What changed
Design Tokens (intro) "loosely based on" → "inspired by" the W3C spec, with specifics: "we adopt the concept of typed token groups and the {path.to.token} reference syntax." Added explicit front matter parsing rules.
Token References New paragraph explaining that component references can point to composite values (e.g., {typography.label-md}), while other sections require primitive references.
Elevation Renamed from "Elevation" to "Elevation & Depth" (with "Elevation" as alias). Footnote reference removed.
Shapes Added a prose example block (previously only had token examples).
Components Rewired from a prescriptive list ("Buttons, Chips, Lists...") to a flexible definition: "Design systems are encouraged to define additional components relevant to their domain." Added a note that the components spec is "actively evolving."
Layout Added a full prose example block showing a fluid grid layout description. Spacing tokens expanded from 2 examples (gutter-s, gutter-l) to 8 (base, xsxl, gutter, margin).

Units

em was added as a supported unit alongside px and rem. This is relevant for letterSpacing values (e.g., -0.02em) which are conventionally relative to font size. Unit-less values such as '1.6' | 1.6 were also added to support lineHeight conventions.


The CLI

npx @google/design.md lint DESIGN.md
npx @google/design.md spec --rules

The CLI is built for agents and humans. It allows the consumer to linit, diff, and export to W3C DTCG and Tailwind formats.

This PR collapses everything into a single @google/design.md package with a clear contract: every command writes structured JSON to stdout, errors are closed-enum objects on stderr, and exit codes are meaningful. The goal is a CLI that an LLM can call as a tool without any prompt engineering beyond "here's the --help output."

One package, two entry points

Everything now lives under packages/cli and ships as @google/design.md with:

  • @google/design.md — The CLI binary (npx @google/design.md <command>)
  • @google/design.md/linter — The programmatic API (import { lint } from '@google/design.md/linter')

Both entry points are bundled separately by Bun (dist/index.js and dist/linter/index.js) so consumers who only need the library don't pull in the CLI framework.

Four CLI commands

Command What it does Key output
lint <file> Validates a DESIGN.md file { findings, summary }
diff <before> <after> Compares two DESIGN.md files { tokens, findings, regression }
export --format <fmt> <file> Converts tokens to another format Format-specific JSON
spec Outputs the DESIGN.md format specification Markdown or JSON

Every command reads from a file path or stdin (-), outputs JSON by default, and uses exit code 1 for errors.

Pluggable lint rules

The monolithic linter was decomposed into individual rule modules, each a pure function (DesignSystemState) → Finding[]:

  • broken-ref — unresolved {section.token} references
  • missing-primary — colors defined but no primary
  • contrast-ratio — WCAG AA contrast warnings
  • orphaned-tokens — colors not referenced by any component
  • missing-sections — expected sections absent
  • section-order — sections out of canonical order
  • token-summary — informational token counts

Rules are registered via RuleDescriptor objects that carry metadata (name, severity, description), making them self-documenting for the spec --rules output.

W3C DTCG token export

The export command now supports --format dtcg, producing a tokens.json file compliant with the W3C Design Tokens Format Module (2025.10). The mapping:

  • Colors → sRGB float components (0–1 range), not hex strings
  • Dimensions{ value, unit } objects
  • Typography → Composite token type with nested dimensions

A conformance test pipes the DTCG output into Terrazzo (@terrazzo/cli) and verifies it produces valid CSS. This ensures our output stays compatible with the real-world DTCG ecosystem, not just our own schema.

Self-documenting specification

The spec command bundles and serves the DESIGN.md format specification itself. The spec is authored as MDX (spec.mdx) with a YAML config file (spec-config.yaml) as the single source of truth for version, supported units, section order, typography properties, and component sub-tokens. A compiler evaluates the MDX expressions against the config to produce the final spec.md.

This means an agent can run npx @google/design.md spec --format json and get the entire format specification in its context window, including what rules are active.

Terminology: Diagnostic → Finding

The public API previously used Diagnostic (a term overloaded with IDE and compiler meanings). This PR renames it to Finding across the entire public surface — types, fields, and variable names. The internal model layer retains Diagnostic where it represents parser-level concerns, but everything a consumer touches says Finding.

Structure

src/
├── index.ts                    # CLI entry (citty command router)
├── commands/                   # One file per subcommand
│   ├── lint.ts
│   ├── diff.ts
│   ├── export.ts
│   └── spec.ts
├── utils.ts                    # stdin reader, JSON/text formatter, diff helpers
└── linter/                     # The engine (also the programmatic API)
    ├── index.ts                # Public API barrel
    ├── lint.ts                 # Orchestrator: parse → model → lint → emit
    ├── parser/                 # Markdown + YAML → ParsedDesignSystem
    ├── model/                  # Resolve tokens, references, contrasts → DesignSystemState
    ├── linter/                 # Rule engine
    │   ├── runner.ts           # Executes rules, aggregates findings
    │   ├── spec.ts             # Finding, Severity types
    │   └── rules/              # Individual rule modules
    ├── tailwind/               # DesignSystemState → Tailwind theme
    ├── dtcg/                   # DesignSystemState → W3C DTCG tokens.json
    ├── fixer/                  # Section reordering
    ├── spec-gen/               # MDX compiler for the spec itself
    └── spec-config.yaml        # Single source of truth for the format

The data flows in one direction: raw markdown → parsed AST → resolved model → findings + emitted output. Each layer is a stateless handler that takes input and returns a result, with no shared mutable state.

Packaging and verification

The check-package.ts script runs 24 checks across 4 phases to catch packaging bugs before they reach consumers:

  1. Package.json validation — exports, types, main, bin fields
  2. Clean build — no test files or fixtures leak into dist
  3. Pack audit — tarball contents match expectations
  4. Consumer smoke tests — ESM import, runtime sanity, CLI --help, and spec command all run successfully against the built dist/

Consolidates packages/linter into packages/cli as a single @google/design.md package with two entry points: a CLI binary and a programmatic linter API.

Adds four commands: lint, diff, export (--format tailwind|dtcg), and spec. Implements W3C DTCG token export with Terrazzo conformance testing. Decomposes linter into pluggable rule modules. Renames Diagnostic→Finding across the public API. Includes 24-check packaging verification script.
@davideast
davideast requested a review from xkxx April 13, 2026 18:51
Comment thread docs/spec.md

**Typography:** `headline-display`, `headline-lg`, `headline-md`, `body-lg`, `body-md`, `body-sm`, `label-lg`, `label-md`, `label-sm`

**Spacing:** `unit`, `gutter`, `margin`, `xs`, `sm`, `md`, `lg`, `xl`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if I'm ready to make recommendations for spacing yet, we need more testing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On review, I agree. I'll address this in the follow up.

Comment thread docs/spec.md
# Recommended Token Names (Non-Normative)

The following names are commonly used across design systems. They are not required but are provided as guidance for consistency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add recommended color naming here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I'll also address this in the follow up.

@davideast
davideast merged commit 061d820 into main Apr 13, 2026
6 checks passed
@davideast
davideast deleted the ship/cli branch April 13, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants