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.

colorsRecord<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.

spacingRecord<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" }

typographyRecord<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" }
}

breakpointsRecord<string, string>

Responsive breakpoints. Example:

"breakpoints": { "sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px" }

shadowsRecord<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)"
}

bordersRecord<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.

  • Skill flags — every --flag accepted by the four skills.
  • Plugin manifest — the .claude-plugin/plugin.json that registers the skills.
  • Troubleshooting — what to do when the schema validation fails or tokens feel wrong.