How to Contribute to the Export Functionality in design.md

Contributing to the export functionality involves extending existing emitter handlers or adding new format support by implementing the *EmitterSpec contract in the CLI package under packages/cli.

The export feature in google-labs-code/design.md converts DESIGN.md tokens into various developer-ready formats like Tailwind JSON, CSS custom properties, and W3C Design Tokens. If you want to contribute to the export functionality, you will work primarily within the CLI package architecture that emphasizes side-effect-free, pure functions. This guide explains the exact file locations, handler patterns, and testing requirements for extending export capabilities.

Export Architecture Overview

The export command is deliberately side-effect-free apart from console I/O, making it easy to test and extend. The entry point lives in packages/cli/src/commands/export.ts, where the command uses citty (defineCommand) to declare arguments at lines 15-44. The actual transformation logic resides in dedicated emitter handlers located under packages/cli/src/linter/, each implementing a pure execute(state) method that accepts a DesignSystemState object and returns either {success:true, data:...} or an error object.

The Export Command Flow

Understanding the execution flow helps you identify where to inject new functionality.

Argument Parsing and Validation

When a user runs design export <file> --format=<fmt> [--prefix=<pfx>], the CLI first validates inputs against a closed enum (FORMATS) at lines 49-57 in export.ts. If the supplied --format is not in the allowed list, the command prints a JSON error to stderr and exits with code 1.

Input Processing and Linting

The command calls readInput (lines 59-68) to load the DESIGN.md source or stdin, propagating any FileReadError with exit code 2. The raw content then passes through the lint function (lines 70-71) to produce a DesignSystemState object representing the design tokens.

Handler Dispatch and Output

Depending on the selected format, the command constructs an emitter handler:

  • TailwindV4EmitterHandler → CSS @theme for Tailwind v4 (css-tailwind) at lines 72-82
  • TailwindEmitterHandler → JSON theme extension for Tailwind v3 (json-tailwind/tailwind) at lines 83-94
  • DtcgEmitterHandler → W3C Design Tokens JSON (dtcg) at lines 94-104
  • CssVarsEmitterHandler → CSS custom properties (css-vars) with optional prefix at lines 105-115

Each handler's execute method returns data that the command writes to stdout using process.stdout.write for CSS or console.log for JSON. Errors print to stderr as JSON and trigger non-zero exit codes.

Adding a New Export Format

To contribute a new export format, follow these three steps:

  1. Register the format – Add your format string to the FORMATS tuple at line 20 in export.ts.

  2. Implement the handler – Create a new handler class in packages/cli/src/linter/<your-format>/handler.ts that follows the *EmitterSpec contract. The class must expose an execute(state) method returning {success:true, data:string} or an error object.

  3. Wire up dispatch – Add a branch in the run method (around line 72-115) that constructs your handler and writes the result to stdout.

  4. Write tests – Add unit tests in packages/cli/src/commands/export.test.ts verifying both successful output and error conditions.

Extending Existing Handlers

You can also contribute by improving current emitters:

  • Tailwind JSON handler (packages/cli/src/linter/tailwind/handler.ts lines 22-36) – Maps colors, font families, font sizes, and dimensions to the Tailwind theme.extend shape.

  • DTCG handler (packages/cli/src/linter/dtcg/handler.ts lines 24-46) – Builds a token file adhering to the Design Tokens 2025.10 schema, converting colors, dimensions, and typography into appropriate DTCG structures.

  • CSS-vars handler (export.ts lines 115-116) – Serializes token declarations into a :root { … } block, applying the optional prefix if given.

When extending these, maintain the existing pure function pattern and update the corresponding test file.

Testing Your Changes

The test suite in packages/cli/src/commands/export.test.ts provides the verification framework. Key test patterns include:

  • Format validation – Verify that invalid formats trigger JSON errors and exit code 1 (lines 58-70).
  • Output verification – For handlers like css-vars, assert that output starts with :root { and contains prefixed custom properties when the --prefix flag is used (lines 37-55).

Run the full suite with bun test to ensure no regressions before submitting your contribution.

Summary

  • The export functionality lives in packages/cli and uses citty for argument parsing at packages/cli/src/commands/export.ts.
  • Four built-in handlers support css-tailwind, json-tailwind, dtcg, and css-vars formats, each implementing a pure execute(state) method.
  • Adding a new format requires updating the FORMATS tuple, creating a handler class following the *EmitterSpec contract, and adding dispatch logic.
  • Extend existing handlers by modifying the specific files under packages/cli/src/linter/ while maintaining strict typing and pure function patterns.
  • Verify changes using packages/cli/src/commands/export.test.ts and run bun test before submitting.

Frequently Asked Questions

Where is the export command defined?

The export command is defined in packages/cli/src/commands/export.ts using citty's defineCommand function at lines 15-44. This file handles argument parsing, format validation against the FORMATS enum, and dispatch to the appropriate emitter handler.

What interface must new handlers implement?

New handlers must implement the *EmitterSpec contract by exposing an execute(state) method that accepts a DesignSystemState object and returns either {success:true, data:string} for successful exports or an error object. This pattern ensures consistency across all emitters including TailwindV4EmitterHandler, DtcgEmitterHandler, and CssVarsEmitterHandler.

How do I test a new export format locally?

Add tests to packages/cli/src/commands/export.test.ts following the existing patterns. Verify that your format accepts valid input and produces the expected output structure, and confirm that invalid inputs return JSON errors with appropriate exit codes. Run bun test from the CLI package directory to execute the suite.

Which file handles CSS custom property generation?

CSS custom property generation is handled by the CssVarsEmitterHandler in packages/cli/src/linter/css-vars/handler.ts (implementation referenced in export.ts lines 105-115). This handler serializes tokens into a :root block and respects the optional --prefix argument for namespacing variables.

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 →