From 132b59910528417b58267f760fe2ab6ff3bf15cd Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 18:53:32 +0200 Subject: [PATCH 1/6] feat: add deterministic DESIGN.md token converter --- lib/__tests__/design-md.test.mjs | 122 +++++++++++++++++++++++++++++++ lib/design-md.mjs | 83 +++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 lib/__tests__/design-md.test.mjs create mode 100644 lib/design-md.mjs diff --git a/lib/__tests__/design-md.test.mjs b/lib/__tests__/design-md.test.mjs new file mode 100644 index 0000000..a7cb8bf --- /dev/null +++ b/lib/__tests__/design-md.test.mjs @@ -0,0 +1,122 @@ +import { describe, it, expect } from 'vitest' +import { convertTokensToDesignMd } from '../design-md.mjs' + +describe('convertTokensToDesignMd', () => { + it('falls back to "Design System" title when brand is empty/missing', () => { + expect(convertTokensToDesignMd({})).toBe('# Design System') + expect(convertTokensToDesignMd({ brand: '' })).toBe('# Design System') + expect(convertTokensToDesignMd({ brand: ' ' })).toBe('# Design System') + }) + + it('uses the brand in the title when present', () => { + expect(convertTokensToDesignMd({ brand: 'Acme' })).toBe( + '# Acme Design System' + ) + }) + + it('renders a per-color subsection with a Step | Value table', () => { + const md = convertTokensToDesignMd({ + colors: [{ name: 'primary', scale: { 50: '#eef', 500: '#123456' } }], + }) + expect(md).toContain('## Colors') + expect(md).toContain('### primary') + expect(md).toContain('| Step | Value |') + expect(md).toContain('| 50 | #eef |') + expect(md).toContain('| 500 | #123456 |') + }) + + it('renders a color description as an emphasized note only when non-empty', () => { + const withDesc = convertTokensToDesignMd({ + colors: [ + { + name: 'primary', + scale: { 500: '#123' }, + description: 'Primary actions', + }, + ], + }) + expect(withDesc).toContain('_Primary actions_') + + const withoutDesc = convertTokensToDesignMd({ + colors: [{ name: 'primary', scale: { 500: '#123' }, description: '' }], + }) + expect(withoutDesc).not.toContain('_') + }) + + it('skips colors that have no scale', () => { + const md = convertTokensToDesignMd({ colors: [{ name: 'ghost' }] }) + expect(md).not.toContain('### ghost') + expect(md).not.toContain('## Colors') + }) + + it('renders typography sub-maps as Token | Value tables', () => { + const md = convertTokensToDesignMd({ + typography: { + fontFamilies: { body: 'Inter' }, + fontSizes: { base: '16px' }, + fontWeights: { bold: 700 }, + lineHeights: { tight: 1.2 }, + }, + }) + expect(md).toContain('## Typography') + expect(md).toContain('### Font families') + expect(md).toContain('| body | Inter |') + expect(md).toContain('### Font sizes') + expect(md).toContain('| base | 16px |') + expect(md).toContain('### Font weights') + expect(md).toContain('| bold | 700 |') + expect(md).toContain('### Line heights') + expect(md).toContain('| tight | 1.2 |') + }) + + it('renders motion (from typography.motion) as its own section', () => { + const md = convertTokensToDesignMd({ + typography: { + motion: { + durations: { fast: '150ms' }, + easings: { standard: 'cubic-bezier(0.4, 0, 0.2, 1)' }, + }, + }, + }) + expect(md).toContain('## Motion') + expect(md).toContain('### Durations') + expect(md).toContain('| fast | 150ms |') + expect(md).toContain('### Easings') + expect(md).toContain('| standard | cubic-bezier(0.4, 0, 0.2, 1) |') + }) + + it('renders spacing, border radius and shadows as Token | Value tables', () => { + const md = convertTokensToDesignMd({ + spacing: { 1: '4px' }, + borderRadius: { sm: '4px' }, + shadows: { sm: '0 1px 2px rgba(0,0,0,0.05)' }, + }) + expect(md).toContain('## Spacing') + expect(md).toContain('| 1 | 4px |') + expect(md).toContain('## Border radius') + expect(md).toContain('| sm | 4px |') + expect(md).toContain('## Shadows') + expect(md).toContain('| sm | 0 1px 2px rgba(0,0,0,0.05) |') + }) + + it('omits empty categories entirely (no heading, no empty table)', () => { + const md = convertTokensToDesignMd({ + brand: 'Min', + typography: { fontSizes: {}, lineHeights: {} }, + spacing: {}, + shadows: {}, + }) + expect(md).toBe('# Min Design System') + }) + + it('is deterministic — identical input yields byte-identical output', () => { + const tokens = { + brand: 'Acme', + colors: [{ name: 'primary', scale: { 500: '#123' } }], + spacing: { 1: '4px', 2: '8px' }, + } + expect(convertTokensToDesignMd(tokens)).toBe( + convertTokensToDesignMd(tokens) + ) + }) +}) diff --git a/lib/design-md.mjs b/lib/design-md.mjs new file mode 100644 index 0000000..10c6321 --- /dev/null +++ b/lib/design-md.mjs @@ -0,0 +1,83 @@ +/** + * DESIGN.md generator — deterministic converter from mint-ds.tokens.json + * to a human-readable Markdown design-system summary. + * + * Consumed as context for AI-assisted design workflows (e.g. Claude Design). + * No LLM, no I/O. Same input → byte-identical output. + * + * Mirrors the deterministic approach of lib/dtcg-exporter.mjs. + */ + +function isNonEmptyObject(v) { + return ( + v && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0 + ) +} + +/** Render a flat map as a `Token | Value` Markdown table. */ +function kvTable(map) { + const lines = ['| Token | Value |', '|---|---|'] + for (const [k, v] of Object.entries(map)) { + lines.push(`| ${k} | ${v} |`) + } + return lines.join('\n') +} + +export function convertTokensToDesignMd(tokens) { + const t = tokens && typeof tokens === 'object' ? tokens : {} + const out = [] + + // Title — brand from the resolved tokens; the schema allows an empty string. + const brand = + typeof t.brand === 'string' && t.brand.trim() !== '' ? t.brand.trim() : null + out.push(`# ${brand ? brand + ' ' : ''}Design System`) + + // Colors — one subsection per color, Step | Value over the scale. + if (Array.isArray(t.colors) && t.colors.length > 0) { + const blocks = [] + for (const c of t.colors) { + if (!c || typeof c !== 'object' || !isNonEmptyObject(c.scale)) continue + const block = [`### ${c.name}`, '', '| Step | Value |', '|---|---|'] + for (const [step, hex] of Object.entries(c.scale)) { + block.push(`| ${step} | ${hex} |`) + } + if (typeof c.description === 'string' && c.description.trim() !== '') { + block.push('', `_${c.description.trim()}_`) + } + blocks.push(block.join('\n')) + } + if (blocks.length > 0) out.push('## Colors', blocks.join('\n\n')) + } + + // Typography — one Token | Value table per present sub-map. + const typo = isNonEmptyObject(t.typography) ? t.typography : {} + const typoSections = [] + if (isNonEmptyObject(typo.fontFamilies)) + typoSections.push(`### Font families\n\n${kvTable(typo.fontFamilies)}`) + if (isNonEmptyObject(typo.fontSizes)) + typoSections.push(`### Font sizes\n\n${kvTable(typo.fontSizes)}`) + if (isNonEmptyObject(typo.fontWeights)) + typoSections.push(`### Font weights\n\n${kvTable(typo.fontWeights)}`) + if (isNonEmptyObject(typo.lineHeights)) + typoSections.push(`### Line heights\n\n${kvTable(typo.lineHeights)}`) + if (typoSections.length > 0) + out.push('## Typography', typoSections.join('\n\n')) + + // Motion — sourced from typography.motion, rendered as its own section. + const motion = isNonEmptyObject(typo.motion) ? typo.motion : {} + const motionSections = [] + if (isNonEmptyObject(motion.durations)) + motionSections.push(`### Durations\n\n${kvTable(motion.durations)}`) + if (isNonEmptyObject(motion.easings)) + motionSections.push(`### Easings\n\n${kvTable(motion.easings)}`) + if (motionSections.length > 0) + out.push('## Motion', motionSections.join('\n\n')) + + // Flat scales. + if (isNonEmptyObject(t.spacing)) out.push('## Spacing', kvTable(t.spacing)) + if (isNonEmptyObject(t.borderRadius)) + out.push('## Border radius', kvTable(t.borderRadius)) + if (isNonEmptyObject(t.shadows)) out.push('## Shadows', kvTable(t.shadows)) + + return out.join('\n\n') +} From 994168314575a9c5554c0549e7ea6a2a4978b302 Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 18:58:37 +0200 Subject: [PATCH 2/6] fix: escape pipe characters in DESIGN.md table cells --- lib/__tests__/design-md.test.mjs | 16 ++++++++++++++++ lib/design-md.mjs | 9 +++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/__tests__/design-md.test.mjs b/lib/__tests__/design-md.test.mjs index a7cb8bf..6b4cf9f 100644 --- a/lib/__tests__/design-md.test.mjs +++ b/lib/__tests__/design-md.test.mjs @@ -109,6 +109,22 @@ describe('convertTokensToDesignMd', () => { expect(md).toBe('# Min Design System') }) + it('escapes pipe characters in table cell values', () => { + const md = convertTokensToDesignMd({ spacing: { 1: '4 | 8px' } }) + expect(md).toContain('| 1 | 4 \\| 8px |') + }) + + it('renders multiple color subsections', () => { + const md = convertTokensToDesignMd({ + colors: [ + { name: 'primary', scale: { 500: '#111' } }, + { name: 'error', scale: { 500: '#222' } }, + ], + }) + expect(md).toContain('### primary') + expect(md).toContain('### error') + }) + it('is deterministic — identical input yields byte-identical output', () => { const tokens = { brand: 'Acme', diff --git a/lib/design-md.mjs b/lib/design-md.mjs index 10c6321..257fe90 100644 --- a/lib/design-md.mjs +++ b/lib/design-md.mjs @@ -14,11 +14,16 @@ function isNonEmptyObject(v) { ) } +/** Escape `|` in a Markdown table cell so values don't break the table. */ +function cell(v) { + return String(v).replace(/\|/g, '\\|') +} + /** Render a flat map as a `Token | Value` Markdown table. */ function kvTable(map) { const lines = ['| Token | Value |', '|---|---|'] for (const [k, v] of Object.entries(map)) { - lines.push(`| ${k} | ${v} |`) + lines.push(`| ${cell(k)} | ${cell(v)} |`) } return lines.join('\n') } @@ -39,7 +44,7 @@ export function convertTokensToDesignMd(tokens) { if (!c || typeof c !== 'object' || !isNonEmptyObject(c.scale)) continue const block = [`### ${c.name}`, '', '| Step | Value |', '|---|---|'] for (const [step, hex] of Object.entries(c.scale)) { - block.push(`| ${step} | ${hex} |`) + block.push(`| ${cell(step)} | ${cell(hex)} |`) } if (typeof c.description === 'string' && c.description.trim() !== '') { block.push('', `_${c.description.trim()}_`) From 34d8b4b828b45488723abb4647c08d5137711988 Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 19:01:15 +0200 Subject: [PATCH 3/6] test: lock DESIGN.md golden output for frankenstein fixture --- examples/frankenstein/DESIGN.md | 146 +++++++++++++++++++++++++++++++ lib/__tests__/design-md.test.mjs | 18 ++++ 2 files changed, 164 insertions(+) create mode 100644 examples/frankenstein/DESIGN.md diff --git a/examples/frankenstein/DESIGN.md b/examples/frankenstein/DESIGN.md new file mode 100644 index 0000000..9783610 --- /dev/null +++ b/examples/frankenstein/DESIGN.md @@ -0,0 +1,146 @@ +# frankenstein Design System + +## Colors + +### primary + +| Step | Value | +|---|---| +| 50 | #e3f2fd | +| 100 | #bbdefb | +| 200 | #90caf9 | +| 300 | #64b5f6 | +| 400 | #42a5f5 | +| 500 | #1976d2 | +| 600 | #1565c0 | +| 700 | #0d47a1 | +| 800 | #0a3f8f | +| 900 | #08357d | + +### error + +| Step | Value | +|---|---| +| 50 | #ffebee | +| 100 | #ffcdd2 | +| 200 | #ef9a9a | +| 300 | #e57373 | +| 400 | #ef5350 | +| 500 | #e53935 | +| 600 | #d32f2f | +| 700 | #c62828 | +| 800 | #b71c1c | +| 900 | #a01818 | + +### background + +| Step | Value | +|---|---| +| 50 | #fefefe | +| 100 | #fdfdfd | +| 200 | #fafafa | +| 300 | #f7f7f7 | +| 400 | #f6f6f6 | +| 500 | #f5f5f5 | +| 600 | #e8e8e8 | +| 700 | #d4d4d4 | +| 800 | #c0c0c0 | +| 900 | #a8a8a8 | + +### text + +| Step | Value | +|---|---| +| 50 | #f5f5f5 | +| 100 | #e0e0e0 | +| 200 | #bdbdbd | +| 300 | #9e9e9e | +| 400 | #757575 | +| 500 | #212121 | +| 600 | #1e1e1e | +| 700 | #1a1a1a | +| 800 | #171717 | +| 900 | #141414 | + +### border + +| Step | Value | +|---|---| +| 50 | #f9f9f9 | +| 100 | #f4f4f4 | +| 200 | #eeeeee | +| 300 | #e7e7e7 | +| 400 | #e2e2e2 | +| 500 | #dddddd | +| 600 | #c7c7c7 | +| 700 | #a8a8a8 | +| 800 | #8a8a8a | +| 900 | #6c6c6c | + +### surface + +| Step | Value | +|---|---| +| 50 | #ffffff | +| 100 | #ffffff | +| 200 | #ffffff | +| 300 | #ffffff | +| 400 | #ffffff | +| 500 | #ffffff | +| 600 | #e6e6e6 | +| 700 | #cccccc | +| 800 | #b3b3b3 | +| 900 | #999999 | + +### muted + +| Step | Value | +|---|---| +| 50 | #f2f2f2 | +| 100 | #e0e0e0 | +| 200 | #cccccc | +| 300 | #b3b3b3 | +| 400 | #8c8c8c | +| 500 | #666666 | +| 600 | #5c5c5c | +| 700 | #4d4d4d | +| 800 | #404040 | +| 900 | #333333 | + +## Typography + +### Font families + +| Token | Value | +|---|---| +| body | Helvetica Neue | + +### Font weights + +| Token | Value | +|---|---| +| bold | 700 | +| extrabold | 800 | + +## Spacing + +| Token | Value | +|---|---| +| 1 | 4px | +| 2 | 8px | +| 3 | 12px | +| 5 | 20px | +| 6 | 24px | + +## Border radius + +| Token | Value | +|---|---| +| sm | 4px | +| md | 8px | + +## Shadows + +| Token | Value | +|---|---| +| sm | 0 2px 4px rgba(0,0,0,0.1) | diff --git a/lib/__tests__/design-md.test.mjs b/lib/__tests__/design-md.test.mjs index 6b4cf9f..57aa0f0 100644 --- a/lib/__tests__/design-md.test.mjs +++ b/lib/__tests__/design-md.test.mjs @@ -1,4 +1,6 @@ import { describe, it, expect } from 'vitest' +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' import { convertTokensToDesignMd } from '../design-md.mjs' describe('convertTokensToDesignMd', () => { @@ -136,3 +138,19 @@ describe('convertTokensToDesignMd', () => { ) }) }) + +describe('golden output', () => { + const FIXTURE_DIR = resolve( + import.meta.dirname, + '../../examples/frankenstein' + ) + + it('matches the committed frankenstein DESIGN.md exactly', () => { + const tokens = JSON.parse( + readFileSync(resolve(FIXTURE_DIR, 'mint-ds.tokens.json'), 'utf8') + ) + const output = convertTokensToDesignMd(tokens) + const expected = readFileSync(resolve(FIXTURE_DIR, 'DESIGN.md'), 'utf8') + expect(output).toBe(expected.trimEnd()) + }) +}) From ef7b6faa7698ad9ec94a711ae9af1c05cf278560 Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 19:03:21 +0200 Subject: [PATCH 4/6] feat: register design-md export target and alias --- lib/__tests__/prompts.test.mjs | 8 ++++++++ lib/prompts.mjs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/lib/__tests__/prompts.test.mjs b/lib/__tests__/prompts.test.mjs index 305caf1..e4e1fb8 100644 --- a/lib/__tests__/prompts.test.mjs +++ b/lib/__tests__/prompts.test.mjs @@ -49,6 +49,14 @@ describe('resolveTarget', () => { expect(resolveTarget('ve')).toBe('vanilla-extract') }) + it('resolves design-md target directly', () => { + expect(resolveTarget('design-md')).toBe('design-md') + }) + + it('resolves the "design" alias to design-md', () => { + expect(resolveTarget('design')).toBe('design-md') + }) + it('returns null for an unknown target', () => { expect(resolveTarget('unknown-xyz')).toBeNull() }) diff --git a/lib/prompts.mjs b/lib/prompts.mjs index 130236a..8c85b1a 100644 --- a/lib/prompts.mjs +++ b/lib/prompts.mjs @@ -694,6 +694,7 @@ export const EXPORT_OUTPUT = { 'qwik-component': { filename: 'components', ext: 'tsx' }, 'vanilla-extract': { filename: 'theme.css', ext: 'ts' }, dtcg: { filename: 'mint-ds.tokens', ext: 'dtcg.json' }, + 'design-md': { filename: 'DESIGN', ext: 'md' }, } // Short aliases the CLI accepts for --target. @@ -723,6 +724,7 @@ export const TARGET_ALIASES = { 'vanilla-extract': 'vanilla-extract', ve: 'vanilla-extract', dtcg: 'dtcg', + design: 'design-md', } // Curated short list shown in --help and validation errors. Keeps the public @@ -746,6 +748,7 @@ export const ADVERTISED_TARGETS = [ 'qwik', 'vanilla-extract', 'dtcg', + 'design-md', ] export function resolveTarget(input) { From b5ad3e1fff956da28b9771db1e9a22c252ae9768 Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 19:06:09 +0200 Subject: [PATCH 5/6] feat: wire design-md export branch into cmdExport --- bin/__tests__/export-design-md.test.mjs | 45 +++++++++++++++++++++++++ bin/mint-ds.mjs | 4 +++ package.json | 1 + 3 files changed, 50 insertions(+) create mode 100644 bin/__tests__/export-design-md.test.mjs diff --git a/bin/__tests__/export-design-md.test.mjs b/bin/__tests__/export-design-md.test.mjs new file mode 100644 index 0000000..0d9edeb --- /dev/null +++ b/bin/__tests__/export-design-md.test.mjs @@ -0,0 +1,45 @@ +import { describe, it, expect } from 'vitest' +import { spawnSync } from 'node:child_process' +import { fileURLToPath } from 'node:url' +import { resolve } from 'node:path' + +// The design-md export is fully deterministic (no network, no LLM), so we can +// drive the real CLI end-to-end and assert on stdout. +const CLI = fileURLToPath(new URL('../mint-ds.mjs', import.meta.url)) +const REPO_ROOT = fileURLToPath(new URL('../../', import.meta.url)) +const TOKENS = resolve(REPO_ROOT, 'examples/frankenstein/mint-ds.tokens.json') + +function runExport(args) { + return spawnSync('node', [CLI, 'export', ...args], { + cwd: REPO_ROOT, + encoding: 'utf8', + }) +} + +describe('mint-ds export --target design-md', () => { + it('prints the generated DESIGN.md to stdout and exits 0', () => { + const result = runExport([ + '--target', + 'design-md', + '--tokens', + TOKENS, + '--stdout', + ]) + expect(result.status).toBe(0) + expect(result.stdout).toContain('# frankenstein Design System') + expect(result.stdout).toContain('## Colors') + expect(result.stdout).toContain('### primary') + }) + + it('resolves the "design" alias too', () => { + const result = runExport([ + '--target', + 'design', + '--tokens', + TOKENS, + '--stdout', + ]) + expect(result.status).toBe(0) + expect(result.stdout).toContain('# frankenstein Design System') + }) +}) diff --git a/bin/mint-ds.mjs b/bin/mint-ds.mjs index 6095238..f521567 100755 --- a/bin/mint-ds.mjs +++ b/bin/mint-ds.mjs @@ -20,6 +20,7 @@ import { getCssAuditor } from '../lib/css-auditor.mjs' import { validateFile } from '../lib/dtcg-validator.mjs' import { diffFiles } from '../lib/token-diff.mjs' import { convertTokensToDTCG, serializeDTCG } from '../lib/dtcg-exporter.mjs' +import { convertTokensToDesignMd } from '../lib/design-md.mjs' import { formatLintSummary } from '../lib/audit-summary.mjs' import { checkCompat } from '../lib/css-compat-data.mjs' import { lintCss, lintGapDecorationAdoption } from '../lib/css-lint-rules.mjs' @@ -592,6 +593,9 @@ async function cmdExport(argv) { // Deterministic conversion — no LLM const dtcg = convertTokensToDTCG(tokens) code = serializeDTCG(dtcg) + } else if (target === 'design-md') { + // Deterministic conversion — no LLM + code = convertTokensToDesignMd(tokens) } else { const cssAuditor = getCssAuditor(flags) code = await cssAuditor.export(buildExportPrompt(tokens, target)) diff --git a/package.json b/package.json index 50e54dc..76c0188 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "lib/config.mjs", "lib/dtcg-validator.mjs", "lib/dtcg-exporter.mjs", + "lib/design-md.mjs", "lib/audit-summary.mjs", "lib/css-compat-data.mjs", "lib/css-lint-rules.mjs", From 69795987735294803ab24fd99c48895af6154df8 Mon Sep 17 00:00:00 2001 From: Nadia Nujovich Date: Mon, 20 Jul 2026 19:10:07 +0200 Subject: [PATCH 6/6] docs: document the design-md export target --- README.md | 21 +++++++++++---------- bin/mint-ds.mjs | 1 + 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 679e92b..d4a37bc 100644 --- a/README.md +++ b/README.md @@ -548,16 +548,16 @@ Add `mint-ds.cache.json` to `.gitignore` if you don't want to commit it. ### Export options -| Flag | Description | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--target ` | **Required.** Accepts: `tailwind`, `unocss`, `react`, `vue`, `svelte`, `astro`, `css`, `scss`, `ts`, `css-modules`, `styled`, `emotion`, `vanilla-extract`, `angular`, `angular-legacy`, `solidjs`, `qwik`, `dtcg` (full names like `tailwind-config`, `react-component` also work) | -| `--tokens ` | Tokens input path (default: `mint-ds.tokens.json`) | -| `--out ` | Override the default output filename | -| `--provider ` | LLM backend: `anthropic` (default), `ollama`, or `openrouter` | -| `--api-key ` | LLM provider API key (overrides all API key env vars) | -| `--model ` | Model name (overrides all model env vars) | -| `--url ` | API endpoint URL (overrides all URL env vars) | -| `--stdout` | Print to stdout instead of writing a file | +| Flag | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `--target ` | **Required.** Accepts: `tailwind`, `unocss`, `react`, `vue`, `svelte`, `astro`, `css`, `scss`, `ts`, `css-modules`, `styled`, `emotion`, `vanilla-extract`, `angular`, `angular-legacy`, `solidjs`, `qwik`, `dtcg`, `design-md` (full names like `tailwind-config`, `react-component` also work) | +| `--tokens ` | Tokens input path (default: `mint-ds.tokens.json`) | +| `--out ` | Override the default output filename | +| `--provider ` | LLM backend: `anthropic` (default), `ollama`, or `openrouter` | +| `--api-key ` | LLM provider API key (overrides all API key env vars) | +| `--model ` | Model name (overrides all model env vars) | +| `--url ` | API endpoint URL (overrides all URL env vars) | +| `--stdout` | Print to stdout instead of writing a file | ### Local development without publishing @@ -581,6 +581,7 @@ node bin/mint-ds.mjs audit ./examples/site --provider ollama | Frameworks | Tailwind Config, Styled Components, Emotion Theme, CSS Modules, Vanilla Extract | | Components | React + TypeScript, Vue 3 SFC, Svelte, Astro, Angular, Angular (Legacy), SolidJS, Qwik | | Interop | DTCG (Design Tokens Format Module v1) — for Penpot & Tokens Studio | +| Docs | DESIGN.md — human-readable design system summary for AI-assisted workflows | ## Penpot & DTCG interop diff --git a/bin/mint-ds.mjs b/bin/mint-ds.mjs index f521567..4e85338 100755 --- a/bin/mint-ds.mjs +++ b/bin/mint-ds.mjs @@ -174,6 +174,7 @@ ${styles.bold('EXAMPLES')} npx mint-ds export --target tailwind npx mint-ds export --target react --out ui/Components.tsx npx mint-ds export --target css --stdout > variables.css + npx mint-ds export --target design-md > DESIGN.md npx mint-ds validate tokens.json --spec dtcg npx mint-ds validate tokens.json --spec dtcg --json npx mint-ds diff old.tokens.json mint-ds.tokens.json