@darronz/designmd-gen
v0.2.0
Published
Export DESIGN.md from existing design token sources
Downloads
329
Maintainers
Readme
designmd-gen
Reads design tokens from your existing CSS, Tailwind, or JSON files and writes a standard DESIGN.md file.
Every project names its tokens differently. --bg-primary, --color-brand, --accent — they're all colors, but no parser can know that from the name alone. So instead of trying to handle every convention, designmd-gen lets you generate a custom mapper for your project using an LLM. The LLM reads your code once, produces a plain TypeScript file, and from there everything runs without it.
Why?
Design tokens live in CSS files, Tailwind configs, JSON files — whatever the team picked when the project started. DESIGN.md is a specification that gives these tokens a shared format: a YAML frontmatter block with sections for colors, typography, spacing, and more.
The problem is getting from one to the other. A CSS file with --bg-primary: #08060d and --space-md: 1rem doesn't tell you which variables are colors and which are spacing — at least not in a way a deterministic parser can reliably figure out. Naming conventions vary wildly between projects, and writing a custom parser by hand for each one is tedious.
designmd-gen solves this in two layers:
- Built-in parsers for projects that follow common conventions (
--color-*,--spacing-*, Tailwind'sthemeobject, W3C DTCG JSON) - Custom mappers for everything else — generated by pointing an LLM at your codebase
The LLM does the one-time judgment work (which variables are colors? which are spacing?) and produces a TypeScript file that designmd-gen runs deterministically from there.
Install
npm install @darronz/designmd-genQuick start
# From a CSS file with standard naming conventions
designmd-gen tokens.css --name "My System"
# From a Tailwind config
designmd-gen tailwind.config.ts --name "My System"
# Merge multiple sources (later files win conflicts)
designmd-gen tokens.css brand-colors.json --name "My System"
# Preview without writing
designmd-gen tokens.css --name "My System" --dry-run --no-lintGenerate a mapper with an LLM
Most real projects don't follow the naming conventions that the built-in parsers expect. That's fine — point an LLM at your project and have it generate a mapper.
Give your LLM access to your codebase and use this prompt:
Look at the CSS custom properties in this project. I need a custom mapper
for designmd-gen that categorizes these tokens into a DESIGN.md file.
Find all CSS files that define custom properties (in :root or html selectors)
and generate a TypeScript file that exports a ParserPlugin.
Classify each variable by examining its name, value, and surrounding comments:
- Color values (hex, rgb, rgba, hsl, named colors) go in `colors`
- Font family stacks and font size/weight/line-height go in `typography`
- Spacing scale values go in `spacing`
- Border radius values go in `rounded`
- Use the variable name (minus the -- prefix) as the token key
- Normalize hex values to lowercase 6-digit format
- Handle rgba() by including the alpha as 8-digit hex
The file should be self-contained (inline types, use postcss for parsing)
and export the parser as a named export.
Here is the ParserPlugin interface the file must implement:
type ParserPlugin = {
name: string;
extensions: string[];
detect(filePath: string): boolean;
parse(filePath: string): DesignTokenSet;
};
type DesignTokenSet = {
name: string;
description?: string;
colors?: Record<string, string>;
typography?: Record<string, TypographyToken>;
rounded?: Record<string, string>;
spacing?: Record<string, string | number>;
components?: Record<string, Record<string, string>>;
};
type TypographyToken = {
fontFamily?: string;
fontSize?: string;
fontWeight?: number;
lineHeight?: string | number;
letterSpacing?: string;
};
Save the mapper to src/tokens/mapper.ts and create a designmd-gen.config.ts
in the project root that imports it.The LLM reads your codebase, finds the token definitions, classifies them, and generates both the mapper and the config file. From there the mapper runs without an LLM — it's just a TypeScript file. If your tokens change, regenerate the mapper.
Config file
A designmd-gen.config.ts in your project root sets defaults and registers your mapper:
import { myParser } from './src/tokens/mapper.ts';
export default {
name: 'My Design System',
parser: myParser,
input: ['src/styles/global.css'],
out: 'DESIGN.md',
};With a config file in place, run with no arguments:
designmd-gen --dry-runCLI flags override config values when both are provided.
Built-in parsers
These work out of the box for projects that follow their expected naming conventions:
| Parser | Detects | What it maps |
|--------|---------|--------------|
| css | .css files | --color-* to colors, --font-{group}-{prop} to typography, --spacing-* to spacing, --radius-* to rounded |
| dtcg | .json with $value keys | W3C DTCG format. color to colors, dimension to spacing/rounded/typography (by ancestor group), typography and fontFamily to typography, component-tier dimensions to components. Resolves aliases and nested three-tier structures (reference/system/component). |
| style-dictionary | .json with value keys (no $ prefix) | Style Dictionary's older value/type format |
| tailwind | tailwind.config.* | theme.colors, theme.fontSize, theme.fontFamily, theme.spacing, theme.borderRadius |
Force a specific parser with --parser:
designmd-gen tokens.json --parser dtcg --name "My System"CI
Add designmd-gen to your build pipeline to keep DESIGN.md in sync with your tokens. If someone updates a token but forgets to regenerate the file, CI catches it.
GitHub Actions
- name: Check DESIGN.md is up to date
run: |
npx designmd-gen
git diff --exit-code DESIGN.mdThis regenerates DESIGN.md and fails if the output differs from what's committed. Works with both built-in parsers and custom mappers — as long as the config file and mapper are checked in.
Other CI systems
The same pattern works anywhere:
npx designmd-gen
git diff --exit-code DESIGN.mdExit code 0 means the file is current. Non-zero means tokens changed but DESIGN.md wasn't updated.
Programmatic API
The CLI is a thin wrapper. Everything is importable:
import { parse, serialize, mergeTokenSets } from '@darronz/designmd-gen';
const tokens = parse('tokens.css');
const yaml = serialize(tokens);
// Merge multiple sources
const css = parse('tokens.css');
const brand = parse('brand.json', 'dtcg');
const merged = mergeTokenSets([css, brand]);
const output = serialize(merged);CLI reference
designmd-gen [options] [files...]
Options:
-c, --config <path> Path to config file
-p, --parser <name> Force a specific parser (css, dtcg, tailwind, style-dictionary)
-n, --name <name> Set the design system name
-o, --out <path> Output path (default: DESIGN.md)
--no-lint Skip validation against @google/design.md linter
--dry-run Print to stdout without writing a fileToken merging
When multiple input files are provided, their tokens are merged into a single DESIGN.md. Later files override earlier ones for conflicting token names, with a warning printed to stderr.
designmd-gen base-tokens.css brand-overrides.css --name "My System"Validation
Output is validated against the @google/design.md linter by default. Warnings print but don't block. Errors block and exit non-zero. Skip with --no-lint.
License
MIT
