DESIGN.md CLI Linting Rules: Complete Configuration Guide

The DESIGN.md CLI executes ten fixed linting rules—including checks for broken references, contrast ratios, and orphaned tokens—that are defined in packages/cli/src/linter/rules/index.ts and can only be customized programmatically via the lint(content, options?) function, not through CLI flags.

The google-labs-code/design.md repository provides a TypeScript-based CLI tool for validating DESIGN.md files against design system specifications. While the command-line interface runs a hardcoded set of DESIGN.md CLI linting rules, the underlying API exposes configuration hooks that allow you to override the default rule set or implement custom validation logic.

Default Linting Rules in DESIGN.md CLI

The linter processes ten rules in a specific order determined by the DEFAULT_RULE_DESCRIPTORS constant in packages/cli/src/linter/rules/index.ts. These descriptors are combined into the runnable DEFAULT_RULES array at line 54, which the CLI invokes when you run design-md lint.

Each rule implementation resides in packages/cli/src/linter/linter/rules/ and performs a specific validation:

Rule Description Implementation
brokenRefRule Detects broken references to tokens, colors, and other design system elements broken-ref.ts
missingPrimaryRule Ensures a primary color token is defined missing-primary.ts
contrastCheckRule Validates contrast ratios for accessibility compliance contrast-ratio.ts
orphanedTokensRule Flags tokens that are defined but never used orphaned-tokens.ts
tokenSummaryRule Generates token usage summaries and warns on unexpected token types token-summary.ts
missingSectionsRule Validates presence of mandatory sections (Overview, Colors, Typography, etc.) missing-sections.ts
missingTypographyRule Guarantees every referenced typography token exists missing-typography.ts
sectionOrderRule Enforces canonical ordering of sections and allowed aliases section-order.ts
unknownKeyRule Flags unknown top-level keys in the DESIGN.md file unknown-key.ts
tokenLikeIgnoredRule Ignores intentionally placeholder tokens without values token-like-ignored.ts

How to Configure DESIGN.md CLI Linting Rules

The CLI command defined in packages/cli/src/commands/lint.ts calls the lint() function without passing an options object, meaning it always runs the full DEFAULT_RULES array. To customize which rules execute, you must use the programmatic API.

Programmatic Configuration via the Lint API

The lint() function exported from packages/cli/src/linter/lint.ts accepts an optional LintOptions object containing a rules array. Pass a filtered subset of DEFAULT_RULES or a custom array to override the default behavior.

import { lint, DEFAULT_RULES } from './packages/cli/src/linter/index.js';
import { missingPrimary } from './packages/cli/src/linter/linter/rules/missing-primary.js';

// Run with the full default set
const report1 = lint(designMdContent);

// Run with a custom subset (e.g., drop the contrast check)
const customRules = DEFAULT_RULES.filter(r => r !== missingPrimary);
const report2 = lint(designMdContent, { rules: customRules });

Creating Custom CLI Wrappers

If you require a bespoke rule set for regular command-line use, create a wrapper script that imports the lint function and passes your own rules array:

#!/usr/bin/env node
import { lint, DEFAULT_RULES } from './packages/cli/src/linter/index.js';
import { unknownKey } from './packages/cli/src/linter/linter/rules/unknown-key.js';

// Only run the "unknown key" rule
const myRules = [unknownKey];
const report = lint(process.argv[2], { rules: myRules });

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

Implementing Custom Lint Rules

Custom rules must implement the LintRule type, receiving a DesignSystemState object and returning an array of Finding objects:

// my-rule.ts
import type { LintRule, DesignSystemState } from '../model/spec.js';
import type { Finding } from '../../linter/spec.js';

export const myRule: LintRule = (state: DesignSystemState): Finding[] => {
  // Example: flag any token whose name contains a space
  return Object.entries(state.tokens)
    .filter(([k]) => k.includes(' '))
    .map(([k]) => ({
      severity: 'warning',
      path: `tokens.${k}`,
      message: `Token names should not contain spaces (found "${k}")`,
    }));
};

// Usage in your script
import { myRule } from './my-rule.js';
const report = lint(content, { rules: [...DEFAULT_RULES, myRule] });

Running the Built-in CLI

For standard validation using the complete default rule set, invoke the CLI directly:

design-md lint examples/totality-festival/DESIGN.md --format json

This outputs a JSON object containing findings and a summary field with counts of errors, warnings, and infos.

Key Source Files for Linting

Summary

  • The DESIGN.md CLI runs ten fixed linting rules defined in packages/cli/src/linter/rules/index.ts as DEFAULT_RULE_DESCRIPTORS.
  • No CLI flags exist for disabling or configuring individual rules; the CLI always runs the full DEFAULT_RULES array.
  • Programmatic customization requires importing the lint() function from packages/cli/src/linter/lint.ts and passing a rules array via LintOptions.
  • Custom rules can be implemented by conforming to the LintRule type and processing the DesignSystemState object.
  • Rule implementations are located in packages/cli/src/linter/linter/rules/ with filenames matching their functionality (e.g., contrast-ratio.ts).

Frequently Asked Questions

Can I disable specific rules via CLI flags?

No. The CLI command in packages/cli/src/commands/lint.ts does not expose flags for rule configuration. It always invokes lint(content) with the default options. To run a subset of rules, you must create a custom script that imports the lint function and passes a filtered rules array in the LintOptions object.

How do I add a custom rule to the DESIGN.md linter?

Create a TypeScript file that exports a function conforming to the LintRule type, which accepts a DesignSystemState and returns a Finding[] array. Import this rule into your custom script and pass it to the lint() function via the options.rules array alongside or instead of the default rules.

What is the difference between DEFAULT_RULE_DESCRIPTORS and DEFAULT_RULES?

DEFAULT_RULE_DESCRIPTORS in packages/cli/src/linter/rules/index.ts contains metadata about each rule, while DEFAULT_RULES (constructed at line 54) is the runnable array of rule functions that the linter actually executes. The CLI uses DEFAULT_RULES when you run the lint command.

Where does the lint command output its results?

By default, the CLI outputs validation results to stdout. You can specify the output format using the --format flag (e.g., --format json), which returns a structured JSON object containing a findings array and a summary object with error, warning, and information counts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →