Token schema
.claude/d2c/design-tokens.json is the contract. d2c-init writes it,
d2c-build reads it, d2c-audit scores adherence to it, and
d2c-guard enforces it on every edit. This page is the canonical
shape: what fields exist, what they mean, what the skills do with them.
The authoritative schema file lives upstream at
skills/d2c-init/references/design-tokens.schema.json.
This page is a human reading guide; the JSON Schema is the validator.
Top-level shape
{
"d2c_schema_version": 1,
"framework": "react",
"meta_framework": "next",
"component_file_extension": ".tsx",
"styling_approach": "tailwind",
"split_files": false,
"colors": { "primary": "#2563eb", "surface": "#ffffff" },
"spacing": { "xs": "4px", "sm": "8px", "md": "16px" },
"typography": {
"heading-1": { "font-family": "Inter", "font-size": "2rem", "font-weight": 700 }
},
"breakpoints": { "md": "768px", "lg": "1024px" },
"shadows": { "card": "0 1px 3px rgb(0 0 0 / 0.1)" },
"borders": { "radius-base": "6px" },
"components": [
{ "name": "Button", "path": "src/components/ui/Button.tsx", "variants": ["primary", "ghost"] }
],
"conventions": {
"declaration_style": { "value": "arrow", "confidence": 0.9 }
},
"preferred_libraries": {
"data_fetching": { "selected": "@tanstack/react-query", "installed": ["@tanstack/react-query", "swr"] }
},
"api": { "base_url": "process.env.NEXT_PUBLIC_API_URL" },
"hooks": [
{ "name": "useAuth", "path": "src/hooks/useAuth.ts" }
],
"meta": { "generated_at": "2026-03-01T12:00:00Z" }
}
Required fields
Every design-tokens.json must include at least these top-level keys.
d2c_schema_version — number (required)
Always 1 as of this doc. Must be the first field in the file.
Every skill checks this before reading anything else; a missing or
older version triggers a STOP AND ASK so you can re-run
/d2c-init --force to regenerate.
framework — string (required)
One of: react, vue, svelte, angular, solid, astro. Drives
which reference file d2c-build loads from
skills/d2c-build/references/framework-<name>.md.
meta_framework — string | null
One of: next, nuxt, sveltekit, solidstart, astro, or null
if the project is vanilla. Detected first; takes precedence over the
framework alone when choosing conventions (e.g. Nuxt auto-imports vs
plain Vue).
Token categories
All token values are flat primitives (string or number). Nested
objects like { value: "#2563eb", css_var: "--primary" } are rejected
by the validator; if the extractor produces that shape, flatten before
writing.
colors — Record<string, string>
CSS color values keyed by semantic name. Example:
"colors": {
"primary": "#2563eb",
"primary-hover": "#1d4ed8",
"surface": "#ffffff",
"surface-subtle": "#f9fafb",
"fg": "#111827",
"fg-muted": "#4b5563"
}
d2c-audit flags any hardcoded color in src/ that doesn't resolve
to one of these entries. d2c-guard blocks the save.
spacing — Record<string, string>
Spacing scale in CSS units. Accepts px, rem, em, or numeric
multipliers of a base. Example:
"spacing": { "xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "32px" }
typography — Record<string, { font-family?: string; font-size?: string; font-weight?: number; line-height?: string; letter-spacing?: string }>
Named text styles. Example:
"typography": {
"heading-1": { "font-family": "Inter", "font-size": "2rem", "font-weight": 700 },
"body": { "font-family": "Inter", "font-size": "1rem", "line-height": "1.5" }
}
breakpoints — Record<string, string>
Responsive breakpoints. Example:
"breakpoints": { "sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px" }
shadows — Record<string, string>
Box-shadow definitions.
"shadows": {
"card": "0 1px 3px rgb(0 0 0 / 0.1)",
"modal": "0 10px 15px rgb(0 0 0 / 0.1)"
}
borders — Record<string, string>
Border primitives, including radii. Radius values live here — not in
spacing. The validator re-categorizes if it catches spacing.radius-md
or similar.
"borders": { "radius-none": "0", "radius-base": "6px", "radius-full": "9999px" }
Inventory fields
components — array
Every reusable component detected in the codebase. d2c-build reads
this before creating anything new; reuse beats regeneration.
"components": [
{
"name": "Button",
"path": "src/components/ui/Button.tsx",
"variants": ["primary", "secondary", "ghost"],
"props": ["variant", "size", "disabled"]
}
]
hooks — array
Detected hooks / composables / services worth reusing.
"hooks": [
{ "name": "useAuth", "path": "src/hooks/useAuth.ts" }
]
Conventions
conventions — object
What the scanner inferred about how the codebase writes code. Each
field is { value, confidence } where confidence is 0.0–1.0.
d2c-build applies the convention when confidence > 0.6 and
value !== "mixed".
"conventions": {
"declaration_style": { "value": "arrow", "confidence": 0.9 },
"export_style": { "value": "named", "confidence": 1.0 },
"type_definitions": { "value": "interface", "confidence": 0.85 },
"import_ordering": { "value": "eslint:ordered", "confidence": 0.95 },
"file_naming": { "value": "kebab-case", "confidence": 0.8 },
"css_wrapper": { "value": "tailwind-class-list", "confidence": 1.0 },
"barrel_exports": { "value": false, "confidence": 0.9 },
"props_pattern": { "value": "inline-destructure", "confidence": 0.7 }
}
preferred_libraries — object
Per-capability, which library the project has chosen. d2c-build and
d2c-guard refuse to substitute a library that's installed but not
selected.
"preferred_libraries": {
"data_fetching": {
"selected": "@tanstack/react-query",
"installed": ["@tanstack/react-query", "swr"]
},
"forms": { "selected": "react-hook-form" }
}
api — object
Project-level API configuration (base URL, auth pattern). Omitted when no data-fetching library is detected.
Operational fields
component_file_extension — string
The extension d2c-build writes new components with. Derived from
framework — e.g. .tsx for React, .vue for Vue, .svelte for
Svelte, .component.ts for Angular.
styling_approach — string
One of: tailwind, css-modules, styled-components, emotion,
vanilla-css, panda-css, stitches. Controls how d2c-build emits
token references.
split_files — boolean
When true, the other fields live in companion files
(tokens-core.json, tokens-colors.json, tokens-components.json,
tokens-conventions.json) instead of this monolith. d2c-init
auto-splits when the file exceeds ~400 lines or ~20,000 tokens; the
monolith then becomes a thin pointer that only carries
d2c_schema_version, split_files: true, framework, and
meta_framework.
figma_variables — object
Optional. Figma Variables imported during d2c-init Step 5g; maps
Figma variable IDs to token paths so design-side changes round-trip
back into the schema.
meta — object
Freeform metadata; typically generated_at (ISO timestamp) and
generator_version. Not read by any skill at decision time —
informational only.
Incremental updates
d2c-init is incremental by default. On re-run, it compares the
current design-tokens.json against the codebase and only re-scans
sections whose sources changed. Pass --force to skip the incremental
path and re-scan everything.
Related
- Skill flags — every
--flagaccepted by the four skills. - Plugin manifest — the
.claude-plugin/plugin.jsonthat registers the skills. - Troubleshooting — what to do when the schema validation fails or tokens feel wrong.