How to Debug Export Functionality Issues in the Design.md CLI

Export issues in the Design.md CLI typically stem from invalid token names, malformed dimension objects, or emitter validation failures in the Tailwind emitter handlers, which can be isolated by inspecting the intermediate DesignSystemState and toggling between the default and v4 emitters.

The Design.md CLI transforms DESIGN.md files into Tailwind-compatible CSS or JSON through a pure functional pipeline. When the export functionality fails or produces unexpected output, the issue usually originates in the Tailwind emitter handlers or the underlying model construction. Understanding the exact failure point—from token validation to dimension serialization—allows you to debug export functionality issues efficiently without guessing.

Understanding the Export Architecture

The export flow follows a strict sequence of pure, side-effect-free transformations:


read DESIGN.md → parse → model → emitter → formatOutput → stdout

Each layer has distinct responsibilities:

Failures surface only during model construction (invalid token syntax) or emitter validation (illegal CSS identifiers), making systematic isolation straightforward.

Common Export Failure Points and Solutions

"Token name … is not a valid CSS identifier"

This error originates in the Tailwind v4 emitter, which validates token names against strict CSS identifier rules.

Debugging steps:

  1. Run the CLI with the default (non-v4) emitter to confirm the validation is the blocker: design-md export --format json --emitter tailwind
  2. If the error disappears, inspect the offending token by printing the model: design-md lint --format json | jq '.findings[] | select(.message|contains("valid CSS"))'
  3. Check the state.colors, state.typography, or other maps in the intermediate output.

Missing Color or Spacing Entries

When the output JSON lacks expected design tokens, the ModelHandler likely filtered them due to parsing errors (e.g., malformed hex colors).

Debugging steps:

  1. Enable verbose logging: DEBUG=design-md:* design-md export
  2. Examine packages/cli/src/linter/model/handler.ts around line 271 where parseColor is called to see where entries might be dropped.

Empty Output {}

If formatOutput produces an empty object, the emitter returned an empty data object despite success:true.

Debugging steps:

  1. Add a temporary console.log in TailwindEmitterHandler.execute (line 23 in packages/cli/src/linter/tailwind/handler.ts) to dump the full state object before mapping occurs.
  2. Verify that state.colors and state.spacing contain entries after model construction.

Unexpected Unit Strings (e.g., 1pxpx)

Malformed dimension objects cause the dimToString serializer to produce invalid CSS values.

Debugging steps:

  1. Inspect mapDimensions in packages/cli/src/linter/tailwind/handler.ts (lines 72-77).
  2. Validate the source of state.spacing and state.rounded by printing these maps before the mapping loop executes.

Step-by-Step Debugging Guide

Follow this sequence to isolate export failures:

  1. Reproduce the issue locally – Run the exact export command reported: design-md export path/to/DESIGN.md --format json

  2. Enable internal diagnostics – Set the DEBUG environment variable to the design-md:* namespace to see model-level logs: DEBUG=design-md:* design-md export ...

  3. Inspect the parsed model – Dump the intermediate DesignSystemState as JSON to examine raw token values: design-md parse path/to/DESIGN.md --format json > state.json

  4. Validate token names – If using Tailwind v4, compare behavior against the legacy emitter: design-md export --emitter tailwind (non-v4) versus design-md export --emitter tailwind-v4

  5. Add temporary console statements – Edit packages/cli/src/linter/tailwind/handler.ts to log intermediate maps: console.log('Colors map:', state.colors);

  6. Run the unit tests – Execute the test suite to isolate regressions: npm test or bun test, paying attention to tailwind/handler.test.ts and tailwind/v4/fixture.test.ts

  7. Check version compatibility – Verify your CLI version matches the latest spec. The VERSION export in packages/cli/src/version.ts (line 31) and packages/cli/package.json define the expected schema support.

Practical Code Examples for Debugging

Minimal Reproduction Script

Print the raw model and final Tailwind JSON programmatically:

import { readInput } from './utils.js';
import { ModelHandler } from './linter/model/handler.js';
import { TailwindEmitterHandler } from './linter/tailwind/handler.js';
import { formatOutput } from './utils.js';

async function debugExport(file: string) {
  const src = await readInput(file);
  const model = new ModelHandler().execute(src);
  console.log('=== Model ===', model);
  const emit = new TailwindEmitterHandler().execute(model);
  console.log('=== Tailwind JSON ===', formatOutput(emit, { format: 'json' }));
}

debugExport('examples/totality-festival/DESIGN.md');

Quick Token Validation Check

Test the v4 emitter with explicit error handling:

import { TailwindV4EmitterHandler } from './linter/tailwind/v4/handler.js';

try {
  const result = new TailwindV4EmitterHandler().execute(state);
  console.log('Export succeeded', result);
} catch (e) {
  console.error('Export validation failed', e);
}

Force Non-v4 Emitter via CLI

Bypass strict validation when testing:

design-md export path/to/DESIGN.md --emitter tailwind      # default, permissive

design-md export path/to/DESIGN.md --emitter tailwind-v4   # strict validation

Key Source Files Reference

File Role
packages/cli/src/utils.ts Formats CLI output (JSON/Markdown) and reads input files; contains formatOutput
packages/cli/src/linter/model/spec.ts Defines DesignSystemState type and associated interfaces
packages/cli/src/linter/model/handler.ts Constructs DesignSystemState from parsed DESIGN.md; contains parseColor logic around line 271
packages/cli/src/linter/tailwind/spec.ts Declares TailwindEmitterSpec contract
packages/cli/src/linter/tailwind/handler.ts Core export implementation; TailwindEmitterHandler.execute at line 23, mapDimensions at lines 72-77
packages/cli/src/linter/tailwind/v4/handler.ts Strict v4 emitter with CSS identifier validation
packages/cli/src/version.ts Provides VERSION constant for compatibility checks

Summary

  • Export failures originate in either model construction (ModelHandler) or emitter validation (TailwindEmitterHandler/TailwindV4EmitterHandler).
  • Use DEBUG=design-md:* to enable verbose logging during the export process.
  • Toggle between --emitter tailwind and --emitter tailwind-v4 to isolate CSS identifier validation errors.
  • Inspect the intermediate DesignSystemState by dumping the model output before it reaches the emitter handlers.
  • Check packages/cli/src/linter/tailwind/handler.ts lines 23 and 72-77 when debugging empty outputs or malformed dimension strings.

Frequently Asked Questions

Why does my Tailwind v4 export fail with "Token name is not a valid CSS identifier"?

The TailwindV4EmitterHandler enforces strict naming rules against the regex /^[a-zA-Z0-9][a-zA-Z0-9-]*$/. Token names containing spaces, special characters, or leading hyphens trigger this validation error. Run the export with --emitter tailwind (non-v4) to bypass this check, or rename the offending tokens in your DESIGN.md file to match valid CSS identifier syntax.

How do I enable debug logging for the Design.md CLI?

Set the DEBUG environment variable to design-md:* before running your command: DEBUG=design-md:* design-md export path/to/DESIGN.md. This outputs internal logs from the model handler and emitter, revealing where tokens are parsed or dropped during the pipeline execution.

Why are my colors missing from the exported JSON output?

Missing entries usually indicate that parseColor or similar validation logic in packages/cli/src/linter/model/handler.ts filtered malformed values during model construction. Use design-md parse --format json to inspect the raw DesignSystemState and verify that your color values use valid hex or RGB syntax recognized by the parser.

What is the difference between the tailwind and tailwind-v4 emitters?

The default tailwind emitter focuses on compatibility and exports tokens without strict CSS identifier validation. The tailwind-v4 emitter adds a validation layer that ensures all token names conform to CSS grammar rules required by Tailwind CSS v4. If your design system uses legacy token names with spaces or special characters, use the default emitter or update your token names for v4 compatibility.

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 →