# Detecting Token Regressions Between DESIGN.md Versions Using the diff Command

> Easily detect token regressions in DESIGN.md versions with the design CLI diff command. Spot lint errors and warnings with non-zero exit codes.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-29

---

**The `design` CLI provides a `diff` sub-command that compares two DESIGN.md files, reports token-level changes, and exits with a non-zero code when lint errors or warnings increase.**

The [`design.md`](https://github.com/google-labs-code/design.md/blob/main/design.md) repository ships a specialized command-line tool for tracking design system evolution. By comparing token maps and component definitions across file versions, the **diff command** surfaces exactly what changed—and whether those changes represent a regression in design system quality.

## How the diff Command Works

The diff command follows a predictable pipeline that transforms raw DESIGN.md files into a structured regression report. Understanding this flow helps you interpret results and extend the tool for custom workflows.

### Reading and Validating Inputs

The process begins with **`readInput`**, a safe file wrapper located in [`src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/src/utils.ts) that accepts either file paths or stdin streams. When you pass `-` as an argument, the utility streams `process.stdin` into a string, while invalid paths trigger a JSON error response and graceful exit. This helper ensures the subsequent linting stage always receives valid markdown content.

### Linting DESIGN.md Files

Both input files are processed by the shared **`lint`** function from [`src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/src/linter/index.ts). This produces a **design-system report** containing typed `Map` objects for colors, typography, spacing, rounded corners, and component definitions. These maps serve as the canonical data structures for comparison, as seen in [`src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/src/commands/diff.ts) where the linter is invoked for both the "before" and "after" states.

### Normalizing Component Data

Component definitions require special handling before comparison. The **`serializeComponents`** function (defined in [`src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/src/commands/diff.ts)) converts component maps into plain objects by extracting `properties` entries. This normalization ensures that complex component structures become comparable key-value pairs that the generic diff algorithm can process.

### Computing Token Differences

The core comparison logic lives in **`diffMaps`** within [`src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/src/utils.ts). This utility walks two `Map` instances and produces three arrays: `added`, `removed`, and `modified`. The command invokes this helper for each token category—colors, typography, spacing, and rounded corners—generating a comprehensive delta of the design system.

### Regression Detection Logic

After computing token differences, the command compares lint summaries. If the *after* report contains more errors or warnings than the *before* report, the **`regression`** flag is set to `true` and the process exits with code `1`. This behavior, implemented in [`src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/src/commands/diff.ts), makes the command suitable for automated CI gates that must block pull requests introducing new design violations.

## CLI Usage Examples

### Basic File Comparison

Compare two DESIGN.md files and receive structured JSON output:

```bash
design diff path/to/before/DESIGN.md path/to/after/DESIGN.md

```

The default JSON format includes the complete token delta and regression status.

### Processing Stdin Streams

Use the `-` shorthand to pipe content directly into the comparison:

```bash
cat before.md | design diff - path/to/after/DESIGN.md

```

This leverages the `readInput` utility's stdin handling without creating temporary files.

### Human-Readable Markdown Output

Generate a formatted markdown report for code reviews:

```bash
design diff path/to/before/DESIGN.md path/to/after/DESIGN.md --format markdown

```

The `--format` flag routes through `formatOutput` in [`src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/src/utils.ts), which supports both JSON and markdown serializers.

## Programmatic Integration

Embed the diff logic directly in Node.js scripts by importing the underlying utilities:

```javascript
import { readInput, diffMaps } from './packages/cli/src/utils.js';
import { lint } from './packages/cli/src/linter/index.js';

// Load design files
const before = await readInput('before/DESIGN.md');
const after = await readInput('after/DESIGN.md');

// Generate lint reports with token maps
const beforeReport = lint(before);
const afterReport = lint(after);

// Normalize components for comparison
function serializeComponents(components) {
  const out = new Map();
  for (const [name, comp] of components) {
    out.set(name, Object.fromEntries(comp.properties));
  }
  return out;
}

// Compute specific token diffs
const colorDiff = diffMaps(
  beforeReport.designSystem.colors,
  afterReport.designSystem.colors
);

console.log(JSON.stringify(colorDiff, null, 2));

```

This approach gives you granular control over which token categories to compare while using the same battle-tested `diffMaps` implementation that powers the CLI.

## CI/CD Integration

The diff command's exit code behavior makes it ideal for GitHub Actions workflows that prevent design regressions:

```yaml
name: Design Token Regression Check
on: [pull_request]

jobs:
  diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install dependencies
        run: bun install
      - name: Check for token regressions
        run: |
          bun run design diff ${{ github.event.pull_request.base.sha }}:DESIGN.md \
                            ${{ github.event.pull_request.head.sha }}:DESIGN.md \
                            --format json

```

When new lint errors or warnings appear in the pull request's DESIGN.md, the command exits with code `1`, automatically failing the check and blocking the merge.

## Summary

- The **`design diff`** command compares two DESIGN.md files and reports token-level changes across colors, typography, spacing, and components.
- **Regression detection** triggers when the after-state contains more lint errors or warnings, causing a non-zero exit code suitable for CI pipelines.
- The architecture relies on isolated utilities—`readInput` for file handling, `lint` for parsing, `serializeComponents` for normalization, and `diffMaps` for comparison—making the codebase extensible.
- Output formats include machine-readable JSON (default) and human-readable markdown via the `--format` flag.
- Source files live in [`src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/src/commands/diff.ts) (orchestration), [`src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/src/utils.ts) (generic helpers), and [`src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/src/linter/index.ts) (parsing logic).

## Frequently Asked Questions

### How does the diff command determine if a change is a regression?

The command considers a regression to have occurred when the *after* DESIGN.md file produces more lint errors or warnings than the *before* file during the linting phase. This comparison happens in [`src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/src/commands/diff.ts) and sets the `regression` flag to `true` while exiting with code `1`, distinguishing statistical token changes from actual quality degradation.

### Can I compare DESIGN.md files from different git commits without checking them out?

Yes. When using the CLI in a git repository, you can pass commit references with the filename prefix syntax (e.g., `abc123:DESIGN.md`). The `readInput` utility handles these paths, allowing you to compare the base branch against the head branch directly in CI environments without intermediate file operations.

### What token categories does the diff command analyze?

The command inspects five categories: **colors**, **typography**, **spacing**, **rounded corners**, and **component definitions**. Each category is compared using the shared `diffMaps` utility, which identifies added, removed, and modified entries within the respective `Map` structures.

### Why does the diff command use Map objects instead of plain objects for comparison?

The [`design.md`](https://github.com/google-labs-code/design.md/blob/main/design.md) linter parses DESIGN.md into typed `Map` instances to preserve insertion order and support complex key types. The `diffMaps` function in [`src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/src/utils.ts) specifically handles Map iteration, while `serializeComponents` normalizes component data into plain objects only where necessary for deep equality checks of nested properties.