How Design MD Data Is Processed Before Exporting: A Complete Pipeline Guide

Design MD data is processed before exporting through a four-phase pipeline—input parsing, model resolution, linting validation, and format emission—that transforms raw Markdown into validated, fully-typed design tokens ready for Tailwind CSS or DTCG consumption.

The @google/design.md CLI converts specification-compliant .md files into production-ready design systems. Understanding exactly how Design MD data is processed before exporting enables developers to troubleshoot token resolution failures, optimize validation rules, and select the correct emitter for their target platform. The pipeline guarantees that all token references are resolved and color values validated before any output file is written.

Phase 1: Input Parsing and AST Generation

The pipeline begins by ingesting the source Design MD file or STDIN stream. In packages/cli/src/utils.ts, the readInput() function handles file system access or stream consumption, returning the raw Markdown content.

The CLI then transforms this Markdown into an MDX Abstract Syntax Tree (AST) using compileMdx located in packages/cli/src/linter/spec-gen/compiler.ts. This transformation preserves the semantic structure of design-token sections while enabling programmatic traversal.

Once the AST exists, the ParserHandler class in packages/cli/src/linter/parser/handler.ts walks the tree to extract design-token declarations. The parser identifies token definitions, descriptions, and metadata, building a plain-object representation known as ParsedDesignSystem. This intermediate structure contains raw token values that have not yet been validated or had references resolved.

Phase 2: Model Resolution and Token Validation

After parsing, the ParsedDesignSystem object feeds into the ModelHandler class in packages/cli/src/linter/model/handler.ts. This phase resolves all token references (e.g., {colors.primary}) and validates lexical correctness.

Key validation functions include:

The ModelHandler constructs a fully-typed DesignSystemState object (defined in packages/cli/src/linter/model/spec.ts). This state contains resolved token values, normalized typography scales, and validated color palettes—ready for linting and export.

Phase 3: Linting and Rule Validation

Before emission, the DesignSystemState passes through the LinterSpec class in packages/cli/src/linter/linter/spec.ts. The linter executes a configurable set of rule checks against the token model, including:

  • Token naming convention enforcement (kebab-case, camelCase restrictions)
  • Nesting depth limits for token categories
  • Duplicate token name detection

The linter collects findings as Finding objects. Errors abort the export pipeline immediately, while warnings surface in the CLI output but allow processing to continue. This stage ensures that only valid, semantically correct design systems reach the emission phase.

Phase 4: Export Emission and Format Serialization

The final phase selects an emitter based on the --format flag. The CLI supports multiple export targets, each handled by specialized emitter classes:

Each emitter receives the validated DesignSystemState and performs format-specific transformations, ensuring that color values, dimensions, and references match the expectations of the target consumer.

End-to-End Processing Flow

The complete data processing sequence operates as follows:

  1. ReadreadInput() ingests the Design MD file or STDIN stream.
  2. ParsecompileMdx() generates an AST, then ParserHandler extracts the raw token map into ParsedDesignSystem.
  3. ModelModelHandler resolves references, validates colors and dimensions, and produces DesignSystemState.
  4. LintLinterSpec runs rule checks; fatal errors abort the pipeline.
  5. Emit – The appropriate emitter (TailwindEmitterHandler, TailwindV4EmitterHandler, or DTCG serializer) converts the model to the requested output format.
  6. Write – The CLI writes the serialized output to stdout or a file path.

Practical Export Examples

Run the following commands to observe the processing pipeline in action:


# Export as Tailwind v3 JSON (theme.extend object)

npx @google/design.md export --format json-tailwind DESIGN.md > tailwind.theme.json

# Export as Tailwind v4 CSS (@theme block with CSS variables)

npx @google/design.md export --format css-tailwind DESIGN.md > theme.css

# Export to W3C Design Tokens format (tokens.json)

npx @google/design.md export --format dtcg DESIGN.md > tokens.json

Each command executes the full pipeline, guaranteeing that data is parsed, validated, and fully resolved before reaching the emitter.

Summary

Frequently Asked Questions

What happens if a token reference cannot be resolved during model resolution?

The ModelHandler in packages/cli/src/linter/model/handler.ts throws a resolution error that propagates to the LinterSpec findings collection. Because unresolved references constitute a fatal error, the export pipeline aborts before reaching the emission phase, preventing broken or incomplete design tokens from reaching production files.

Which export format should I use for Tailwind CSS v4 projects?

Use the css-tailwind format handled by TailwindV4EmitterHandler in packages/cli/src/linter/tailwind/v4/handler.ts. This emitter utilizes serializeToCss from packages/cli/src/linter/tailwind/v4/serialize.ts to generate an @theme block containing CSS custom properties, which aligns with Tailwind CSS v4's native theming architecture.

How does the linter distinguish between errors and warnings?

The LinterSpec class in packages/cli/src/linter/linter/spec.ts categorizes each rule violation as either a Finding with severity "error" or "warning". Errors trigger an immediate pipeline halt, while warnings are reported to stderr but allow the DesignSystemState to proceed to the emitter. This distinction enables strict validation for breaking issues while accommodating stylistic preferences.

Where is the design token state stored during processing?

The token state exists as a ParsedDesignSystem plain object immediately after the ParserHandler phase, then transforms into a fully-typed DesignSystemState object (defined in packages/cli/src/linter/model/spec.ts) following the ModelHandler resolution step. This immutable state serves as the single source of truth for both linting and export emitters.

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 →