Design System Tokens skill for Claude Code

Semantic tokens, so a new theme is one file. Claude starts by grepping your codebase for the hex codes and pixel values already in use, clusters them into primitive scales, then layers semantic role tokens on top so components never reference raw colors. It picks a source format that fits your stack, wires Style Dictionary when you ship to several platforms, and adds lint rules that stop new hard-coded values.

  • namedesign-system-tokens
  • size239 lines · ≈2,150 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Builds and audits design token systems — primitive, semantic, and component tiers for color, type, space, radius, shadow, and motion — in DTCG JSON, CSS custom properties, or Tailwind v4 @theme, plus theming and component API conventions. Use when starting a design system, replacing hard-coded hex and pixel values, adding themes or brands, wiring Style Dictionary, or reviewing why components drift visually.

Try asking

  • We have 40 different grays in this codebase. Turn them into a proper token system
  • Set up DTCG tokens with Style Dictionary that output CSS variables and a dark theme
  • Review our Tailwind @theme block and tell me which tokens should be semantic

Use it when

  • Starting a design system or component library
  • Components drift because values are hard-coded
  • Adding a second theme, brand, or white-label variant
  • Syncing Figma Variables with code

Skip it when

  • You only need a palette with accessible contrast, use color-systems instead
  • The work is Tailwind configuration and utility discipline, use tailwind-css instead

How to check the result

Grep finds raw hex values only in the token source file, and toggling the theme attribute restyles every surface with all text pairs passing contrast.

The common mistake it prevents

Naming semantic tokens after their current appearance, like text-gray, which becomes a lie the moment a dark theme maps it to a light value.

~/.claude/skills/design-system-tokens/SKILL.md

--- name: design-system-tokens description: Builds and audits design token systems — primitive, semantic, and component tiers for color, type, space, radius, shadow, and motion — in… license: MIT ---

A token system is done when every visual value in product code comes from a semantic token, a new theme is a single file that remaps semantic tokens, and nobody needs to ask “which gray?” A token file full of blue-500 used directly in components is a palette, not a system.

Ground rules

  • Three tiers, strictly layered: primitive (raw values) → semantic (intent) → component (optional, only where a component needs its own knob). Components read semantic or component tokens, never primitives.
  • Name by role, not appearance: color.text.muted, not color.gray-600. Appearance names belong only in the primitive tier.
  • Every token has exactly one source of truth (one JSON tree or one CSS file); all other formats are generated.
  • Fewer tokens beat more. If two semantic tokens always resolve to the same value in every theme, merge them.
  • Never add a token for a one-off. A value used once is a local style, not a token.

Workflow

- [ ] 1. Inventory existing values (grep the codebase)
- [ ] 2. Cluster into primitive scales
- [ ] 3. Define semantic roles and map them per theme
- [ ] 4. Pick the source format and build pipeline
- [ ] 5. Migrate components tier by tier
- [ ] 6. Enforce with lint and document

1. Inventory before designing

Measure what exists; the drift tells you which scales are needed.

# distinct hex colors and how often each appears
grep -rhoE '#[0-9a-fA-F]{3,8}\b' src | tr 'A-F' 'a-f' | sort | uniq -c | sort -rn | head -40
# pixel values in CSS/TSX (spacing and radius candidates)
grep -rhoE '\b[0-9]+px\b' src | sort | uniq -c | sort -rn | head -40
# font sizes
grep -rhoE 'font-size:\s*[^;]+' src | sort | uniq -c | sort -rn

Typical finding: 30+ grays that should be 10–12, and spacing values like 13px, 15px, 18px that should snap to a scale. Record the mapping old → new for the migration.

CategoryDefault scaleNotes
Space4px base: 0, 2, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96, 128Name by step (space.4 = 16px) or t-shirt; never by pixel value in semantic tier
Radius0, 2, 4, 6, 8, 12, 16, 9999Pick 2–3 for product use; full for pills
Font sizeModular ratio 1.2 (UI) or 1.25 (marketing) from 16pxSee typography-scale
Line height1.1 (display), 1.25 (headings), 1.5 (body)Unitless values
Font weight400, 500, 600, 700Only weights actually loaded
Color11–12 steps per hue in OKLCH (50–950)See color-systems
Shadow3–4 elevations maxEach a full box-shadow value
Duration100, 150, 200, 300, 500 msSee ui-motion
Easingstandard, enter (decelerate), exit (accelerate)Cubic-bezier values
Z-indexbase 0, dropdown 100, sticky 200, overlay 300, modal 400, toast 500Named layers prevent 9999 wars

3. Semantic roles

Start with this minimal set and add only when a real component needs it:

color.bg.canvas        page background
color.bg.surface       cards, panels
color.bg.raised        popovers, menus
color.bg.sunken        inputs, wells
color.text.default     body text
color.text.muted       secondary text (must still hit 4.5:1)
color.text.inverse     text on accent fills
color.border.default   hairlines
color.border.strong    input borders (3:1 against bg for UI components)
color.accent.default   primary action fill
color.accent.hover
color.accent.text      accent-colored text on canvas (4.5:1)
color.focus.ring
color.status.{danger,warning,success,info}.{bg,text,border}

Themes remap semantic tokens only. Light and dark are two mappings of the same names.

4. Pick the source format

SituationSource of truthBuild
Tailwind v4 project, single platformCSS @theme blockNone needed
Web only, no Tailwindtokens.css with custom propertiesNone needed
Multiple platforms (web + iOS/Android) or Figma syncDTCG JSON ($value, $type)Style Dictionary v4+
Figma Variables are the sourceExport to DTCG JSON, then as abovePlugin/REST export + Style Dictionary

DTCG JSON (the W3C Design Tokens Community Group format; first stable spec 2025.10):

{
  "color": {
    "blue": {
      "600": { "$type": "color", "$value": "oklch(0.55 0.18 255)" }
    },
    "accent": {
      "default": { "$type": "color", "$value": "{color.blue.600}" }
    }
  },
  "space": {
    "4": { "$type": "dimension", "$value": "16px" }
  }
}

Style Dictionary build (v4 is ESM, reads DTCG natively):

// sd.config.mjs
export default {
  source: ["tokens/**/*.json"],
  platforms: {
    css: {
      transformGroup: "css",
      buildPath: "dist/",
      files: [{ destination: "tokens.css", format: "css/variables",
                options: { outputReferences: true } }],
    },
  },
};
npx style-dictionary build --config sd.config.mjs

outputReferences: true keeps var(--color-blue-600) references in the output so theme overrides cascade instead of being baked in.

Tailwind v4 CSS-first tokens:

@import "tailwindcss";

@theme {
  --color-blue-600: oklch(0.55 0.18 255);
  --spacing: 0.25rem;            /* drives p-4 = 1rem, gap-6 = 1.5rem */
  --radius-md: 0.375rem;
  --font-sans: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
}

@layer base {
  :root { --accent: var(--color-blue-600); --text-muted: oklch(0.45 0.02 255); }
  [data-theme="dark"] { --accent: oklch(0.72 0.15 255); --text-muted: oklch(0.72 0.02 255); }
}

Use @theme inline when a theme variable references another variable, so utilities resolve the reference at use time rather than at definition time.

5. Migrate

  1. Ship the token file alongside existing styles; change nothing visible.
  2. Replace values in shared primitives first (Button, Input, Card, Text), then pages.
  3. Snap off-scale values to the nearest step; record any intentional exceptions in a comment with the reason.
  4. Use visual regression screenshots per component to catch unintended shifts (see visual-regression).

6. Enforce

  • Stylelint: ban raw colors outside the token file.
{
  "rules": {
    "color-no-hex": true,
    "declaration-property-value-disallowed-list": {
      "/^(margin|padding|gap)/": ["/\\d+px/"]
    }
  },
  "overrides": [{ "files": ["src/styles/tokens.css"], "rules": { "color-no-hex": null } }]
}
  • Tailwind: forbid arbitrary values like p-[13px] and text-[#333] in review; grep for -\[ in class strings during CI.

Component API conventions

  • Variant props describe intent: variant="primary|secondary|ghost|danger", size="sm|md|lg". Never color="blue".
  • Sizes map to token steps, e.g. sm = height 32 / padding-x 12 / text 14; md = 40 / 16 / 14; lg = 48 / 20 / 16. Keep touch targets at least 24×24 CSS px (WCAG 2.2 AA) and 44×44 for primary mobile actions.
  • Expose a component token only when consumers legitimately vary it (--button-radius), not for every property.
  • Document each component with: props table, tokens it reads, do/don’t examples, accessibility notes.

Gotchas

  • Semantic names that encode a value (color.text.gray) break the moment dark mode makes it not gray.
  • Alpha-based tokens (rgba(0,0,0,.6) text) change contrast per background; prefer opaque colors for text.
  • Generated CSS without outputReferences hard-codes values, so [data-theme] overrides silently stop working for derived tokens.
  • Tokens for every Figma style produce 400-token files nobody can choose from. Cap semantic color tokens at roughly 30–40 for a product UI.
  • Renaming a published token is a breaking change; deprecate with an alias for one release cycle.

Verification

  • grep -rnE '#[0-9a-fA-F]{3,8}\b' src --include='*.{css,tsx,jsx,vue,svelte}' returns only the token source file.
  • Toggling data-theme on <html> changes every surface with no hard-coded leftovers (inspect a screenshot of each main page in both themes).
  • Every semantic text/background pair passes WCAG contrast in every theme (run the checker in color-systems).
  • The build output diff is reviewed whenever tokens change.

Output contract

When delivering a token system, provide:

tokens/
  primitives.json | primitives.css   raw scales
  semantic.light.json                role → primitive mapping
  semantic.dark.json
  README.md                          tiers, naming rules, how to add a token
dist/tokens.css                      generated (if a build exists)
MIGRATION.md                         old value → token map, exceptions list

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc