# How to Programmatically Use the Linter API in Node.js with @google/design.md

> Learn to programmatically use the linter API in Node.js with @google/design.md. Parse DESIGN.md content and generate Tailwind CSS configurations effortlessly.

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

---

**The `@google/design.md` package exposes a pure-JavaScript linter API that parses DESIGN.md documents and generates Tailwind CSS configurations via the `lint(content, options?)` function.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository provides a framework-agnostic linting pipeline for design system documentation. By importing the **linter API in Node.js**, you can programmatically validate DESIGN.md files, resolve design tokens into a typed state, and emit Tailwind configurations without shelling out to a CLI process.

## Importing the Linter Entry Point

The package exposes its programmatic interface through the `./linter` subpath export defined in [`packages/cli/package.json`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/package.json) (lines 31-34). This entry point re-exports the core `lint` function and type definitions from [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts).

Install the package via npm:

```bash
npm i @google/design.md

```

Import the linter using ES modules (recommended) or CommonJS:

```javascript
// ES Modules
import { lint } from '@google/design.md/linter';

// CommonJS
const { lint } = require('@google/design.md/linter');

```

## Architecture of the Linting Pipeline

The **linter API** orchestrates four distinct layers to transform raw markdown into a validated design system model. According to the source code in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts) (lines 56-71), the `lint` function executes the following flow:

1. **ParserHandler** ([`packages/cli/src/linter/parser/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/parser/handler.ts)) – Parses markdown and extracts YAML front-matter, sections, and raw headings.
2. **ModelHandler** ([`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts)) – Transforms parsed tokens into a typed `DesignSystemState`.
3. **Rule Runner** ([`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts), lines 30-40) – Executes `runLinter(state, rules?)` against the default rule set (`DEFAULT_RULES`) or custom rules you provide.
4. **TailwindEmitterHandler** ([`packages/cli/src/linter/tailwind/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/tailwind/handler.ts)) – Converts the resolved design system into a ready-to-use Tailwind CSS configuration object.

The result is a `LintReport` object (defined in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts), lines 30-44) containing the `designSystem` model, an array of `findings`, a severity `summary`, and the generated `tailwindConfig`.

## Running the Linter Programmatically

To lint a DESIGN.md file, read the content into memory and pass it to the `lint` function. The API has **no runtime dependencies** beyond peer dependencies like `yaml` and `unified`, making it safe for CI pipelines and server-side services.

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

// Load the DESIGN.md content
const designMd = await readFile('path/to/DESIGN.md', 'utf8');

// Run the linter with default rules
const report = lint(designMd);

```

You can optionally pass a custom rules array via the `options` parameter:

```javascript
import { myCustomRule } from './my-rules.js';

const report = lint(designMd, { rules: [myCustomRule] });

```

The main execution flow (lines 91-107 in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts)) handles markdown parsing, model resolution, rule execution, and Tailwind config generation in a single synchronous call.

## Handling the LintReport Output

The `LintReport` returned by the API provides structured data for validation results and theme extraction. Access the severity summary and individual findings to enforce quality gates:

```javascript
console.log(`Errors: ${report.summary.errors}`);
console.log(`Warnings: ${report.summary.warnings}`);

if (report.findings.length) {
  for (const f of report.findings) {
    console.log(`[${f.severity}] ${f.path ?? '(global)'} – ${f.message}`);
  }
}

```

To integrate with a build pipeline, write the generated Tailwind configuration to a file that the Tailwind CLI can consume:

```javascript
import { writeFile } from 'node:fs/promises';

const tailwindConfig = `module.exports = ${JSON.stringify(report.tailwindConfig, null, 2)};`;
await writeFile('tailwind.config.cjs', tailwindConfig);

```

The `tailwindConfig` property contains the complete theme object derived from your DESIGN.md tokens, ready for immediate use by Tailwind CSS.

## Summary

- Import the **linter API** from `@google/design.md/linter` to access the `lint` function.
- The pipeline parses markdown via **ParserHandler**, resolves tokens via **ModelHandler**, validates via `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), and emits configs via **TailwindEmitterHandler**.
- Pass a string of DESIGN.md content to `lint()` and receive a `LintReport` with `designSystem`, `findings`, `summary`, and `tailwindConfig`.
- The API is framework-agnostic, runs synchronously, and requires no external CLI invocation.

## Frequently Asked Questions

### Can I use the linter API with CommonJS?

Yes. While ES modules are recommended, the package supports CommonJS via `require('@google/design.md/linter')`. The `./linter` export in [`packages/cli/package.json`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/package.json) provides the same surface for both module systems.

### What Node.js version is required?

The package requires Node.js 18 or higher. The **linter API** uses native Node.js APIs like `node:fs/promises` and modern JavaScript features without additional polyfills.

### How do I add custom lint rules?

Pass a `rules` array in the options object to the `lint` function. Each rule should conform to the rule interface used 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) (lines 30-40). Custom rules execute alongside the `DEFAULT_RULES` set unless explicitly disabled.

### Does the API require Tailwind CSS to be installed?

No. The **TailwindEmitterHandler** generates a configuration object compatible with Tailwind CSS, but the linter itself has no runtime dependency on Tailwind. You can use the API to validate DESIGN.md files without ever generating CSS, or extract the `tailwindConfig` to use with your own Tailwind installation separately.