# Can the DESIGN.md Linter Be Used Programmatically? Complete TypeScript API Guide

> Integrate the DESIGN.md linter programmatically with its TypeScript API. Validate design tokens, get lint findings, and generate Tailwind config synchronously. Learn how now.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: api-guide
- Published: 2026-07-04

---

**Yes, the DESIGN.md linter exposes a TypeScript API that allows you to validate design tokens, retrieve structured lint findings, and generate Tailwind configuration objects synchronously without invoking the CLI.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) package provides a programmatic interface that mirrors the functionality of the command-line tool. Whether you need to validate DESIGN.md content in a CI pipeline, build a custom IDE extension, or generate themes dynamically, the library exports in [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts) provide direct access to the parsing, validation, and emission engine.

## How the DESIGN.md Linter Works Programmatically

The DESIGN.md linter is architected as a library first, with the CLI acting as a thin wrapper around core functions.

### Layered Architecture

The implementation follows a clear pipeline from raw markdown to validated output:

- **Public API Layer**: Re-exports `lint`, `runLinter`, and `preEvaluate` from [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts), serving as the stable entry point for consumers
- **Core Engine**: The `lint()` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts) orchestrates parsing, model building, and rule execution
- **Parser**: `ParserHandler` in [`packages/cli/src/linter/parser/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/parser/handler.ts) extracts front-matter and section headings
- **Model Builder**: `ModelHandler` in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts) resolves token references and validates types into a `DesignSystemState`
- **Rule Runner**: `runLinter()` in [`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts) executes validation rules like `brokenRef` and `contrastCheck`
- **Tailwind Emitter**: Optional generation of Tailwind v3 or v4 configurations via [`packages/cli/src/linter/tailwind/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/tailwind/handler.ts)

This design means the [`design.md`](https://github.com/google-labs-code/design.md/blob/main/design.md) binary simply forwards file contents to the library's `lint` function, making the programmatic API identical to the CLI behavior.

## Core API Methods

When you use the DESIGN.md linter programmatically, you work with three primary functions that offer different levels of control.

### lint(content, options?)

The `lint` function is the high-level entry point defined in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts). It accepts a string of DESIGN.md content and returns a `LintReport` object containing:

- `summary`: Error, warning, and info counts
- `findings`: Array of structured `Finding` objects with location and severity data
- `designSystem`: The resolved `DesignSystemState` for further processing

### runLinter(state, rules?)

For advanced use cases, `runLinter` in [`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts) allows you to execute specific rule sets against an existing `DesignSystemState`. This is useful when you need to:

- Apply custom validation rules alongside defaults
- Re-run validation after programmatically modifying the design system
- Build incremental linting workflows

### preEvaluate(designSystem)

Also located in [`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts), `preEvaluate` groups findings by severity into `fixes`, `improvements`, and `suggestions`. This grading system is ideal for UI-driven "fix suggestion" panels or automated remediation workflows.

## Implementation Examples

### Basic Programmatic Linting

Import the `lint` function and validate a DESIGN.md string without touching the filesystem:

```typescript
import { lint } from '@google/design.md/linter';

const designMd = `
---
name: Demo
colors:
  primary: "#1a1c1e"
---

## Overview

Simple design.
`;

const report = lint(designMd);
console.log(report.summary);  // { errors: 0, warnings: 0, infos: 1 }
console.log(report.findings); // Array of Finding objects

```

*The `lint` function operates synchronously with no I/O dependencies, making it safe for server-side rendering or build-time validation.*

### Custom Rule Integration

Extend the default rule set with custom validation logic using `runLinter` and the `DEFAULT_RULES` export:

```typescript
import { lint, runLinter, DEFAULT_RULES } from '@google/design.md/linter';
import { brokenRef } from '@google/design.md/linter';

const customRule = (state) => {
  const findings = brokenRef(state);
  console.log('Broken references detected:', findings);
  return findings;
};

const report = lint(designMd, { 
  rules: [...DEFAULT_RULES, customRule] 
});

```

*Custom rules follow the `LintRule` type signature and receive the fully resolved `DesignSystemState`.*

### Pre-Evaluation and Graded Fixes

Use `preEvaluate` to categorize findings for user interfaces:

```typescript
import { lint, preEvaluate } from '@google/design.md/linter';

const { designSystem } = lint(designMd);
const edits = preEvaluate(designSystem);
// Returns: { fixes: [...], improvements: [...], suggestions: [...] }

```

### Programmatic Tailwind Generation

Generate Tailwind configurations without writing intermediate files:

```typescript
import { lint } from '@google/design.md/linter';
import { TailwindEmitterHandler } from '@google/design.md/linter';

const { designSystem } = lint(designMd);
const emitter = new TailwindEmitterHandler();
const tailwindConfig = emitter.execute(designSystem);
// JSON ready for tailwind.config.js

```

*The emitter supports both Tailwind v3 (`json-tailwind`) and v4 (`css-tailwind`) output formats via [`packages/cli/src/linter/tailwind/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/tailwind/handler.ts).*

### Async File Processing in Node.js

Combine the API with Node.js file system operations for CLI-like behavior:

```typescript
import { readFile } from 'node:fs/promises';
import { lint } from '@google/design.md/linter';

async function validateDesignFile(path: string) {
  const content = await readFile(path, 'utf-8');
  const report = lint(content);
  
  if (report.summary.errors > 0) {
    console.error('Validation failed:', report.findings);
    process.exit(1);
  }
  console.log('Design system valid');
}

validateDesignFile('./DESIGN.md');

```

## Key Source Files

Understanding the source structure helps when extending the programmatic API:

- **[`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts)**: Public exports and type definitions
- **[`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts)**: Core `lint()` implementation
- **[`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts)**: `runLinter()` and `preEvaluate()` logic
- **[`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts)**: CLI wrapper demonstrating library usage
- **[`packages/cli/src/linter/tailwind/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/tailwind/handler.ts)**: Tailwind configuration generation

## Summary

- **The DESIGN.md linter is built as a library**, with the CLI merely forwarding content to the `lint()` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts)
- **Import from `@google/design.md/linter`** to access `lint`, `runLinter`, and `preEvaluate` without subprocess overhead
- **Synchronous execution** allows integration in build tools, test suites, and server environments
- **Custom rule support** via the `rules` option enables domain-specific validation logic
- **Tailwind generation** is accessible programmatically through `TailwindEmitterHandler` in [`packages/cli/src/linter/tailwind/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/tailwind/handler.ts)

## Frequently Asked Questions

### Can I use the DESIGN.md linter in a browser environment?

Yes, the core linting engine is platform-agnostic JavaScript/TypeScript. Since the `lint` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts) operates synchronously on strings without file system I/O, you can bundle it for browser use cases such as live preview editors or in-browser design system validation.

### What is the performance overhead of using the API versus the CLI?

There is no additional overhead because the CLI itself calls the same `lint()` function. Using the programmatic API actually eliminates the cost of spawning a subprocess, making it slightly more efficient for batch processing or watch modes.

### How do I access the parsed design tokens after linting?

The `lint()` function returns a `designSystem` property containing the fully resolved `DesignSystemState`. This object includes all parsed tokens, colors, and typography definitions from your DESIGN.md content, accessible immediately after validation without additional parsing steps.

### Can I disable specific rules when calling lint programmatically?

Yes, the `lint` function accepts an options object where you can pass a custom `rules` array. Import `DEFAULT_RULES` and filter or extend the array to control which validations run. This is processed by `runLinter()` in [`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts).