SuperAPI AI Directory

frontend-impl-design-tokens

Design & creation

View repository

Updated 10-15 22:18

About this skill

Frontend Impl : Design Tokens

This skill is the operational reference for designing a token system that follows the W3C Design Tokens Community Group Format Module 2025.10 draft and emits CSS custom properties under cascade layers. It covers the DTCG JSON shape, the seven base types and six composite types, the alias resolution rule, the three-tier brand-to-component chain, the @property registration for animatable tokens, and runtime theme switching. The skill does NOT cover OKLCH palette generation (see [[frontend-theming-color-palette-oklch]]), dark / light mode implementation details (see [[frontend-theming-dark-light-mode]]), or specific tooling deep-dives.

CRITICAL : DTCG draft status (2025.10)

The DTCG Format Module is a W3C Community Group draft preview dated 7 May 2026 and is explicitly NOT production-implementation-ready per the spec preamble. Source : designtokens.org : Format Module draft (verified 2026-05-19).

Implications for production work today :

  1. ALWAYS author tokens in the DTCG JSON shape so future migrations are mechanical, not destructive.
  2. ALWAYS transform DTCG JSON to CSS custom properties via a build pipeline (Style Dictionary, Tokens Studio export, or a thin custom transformer). Do NOT consume the raw JSON at runtime.
  3. ALWAYS pin to the 2025.10 draft revision in your tooling. NEVER expect the format to be stable across draft versions until the spec reaches Candidate Recommendation.
  4. Disclose the draft status in any internal documentation that mentions DTCG conformance.

Quick Reference

Floor rules

  • ALWAYS tier every token : primitive (raw brand atoms) -> semantic (intent like "action", "feedback success") -> component (button-specific override). NEVER skip the semantic tier and map primitive directly to component.
  • ALWAYS emit tokens as CSS custom properties under @layer tokens (or a similar dedicated layer). NEVER declare tokens in the default layer where any component rule can override them.
  • ALWAYS register a custom property via @property when it is transitioned, animated, or interpolated. NEVER transition a plain --var ; the property is <custom-ident> by default and only swaps discretely.
  • ALWAYS namespace token names with category and intent : --color-fg-action, --space-inline-md, --radius-control-sm. NEVER ship raw names like --my-bg, --blue, --big-radius.
  • ALWAYS use alias references inside DTCG JSON via {group.token}. NEVER copy-paste a primitive $value into the semantic tier ; the link breaks at the first brand update.
  • ALWAYS transform DTCG JSON to CSS at build time. NEVER consume the JSON at runtime from a non-DTCG-aware loader.
  • ALWAYS scope runtime theme overrides to a single cascade layer. NEVER toggle classes on body that override component CSS by specificity ; layers keep override math predictable.

Decision tree 1 : Primitive vs semantic vs component ?

Is the value a raw brand atom : an exact hex from the brand book, a brand
typeface, a specific brand radius ?
  -> PRIMITIVE tier. Name by appearance : --color-blue-500, --font-brand-sans,
     --radius-12. These rarely change ; they document what the brand actually is.

Is the value an intent : "the color that means action", "the surface that
holds the page", "the typeface for body copy" ?
  -> SEMANTIC tier. Name by role : --color-fg-action, --color-bg-surface,
     --font-body. Reference a primitive : --color-fg-action: var(--color-blue-500).

Is the value a component-specific override : a button's bg, a tab's underline ?
  -> COMPONENT tier. Name by component-and-role : --button-primary-bg,
     --tab-active-underline. Reference a semantic : --button-primary-bg: var(--color-bg-action).

Are you tempted to skip the semantic tier and map primitive directly to component ?
  -> Stop. The semantic tier exists so a brand-change PR touches one file
     (primitive) and the semantic-to-component links stay intact.

Decision tree 2 : Static var or @property registration ?

Is the token transitioned, animated, or interpolated (gradient angle,
hue rotation, animated chroma) ?
  -> @property registration is REQUIRED. Without it the property is treated
     as <custom-ident> and only swaps discretely.

Is the token used only as a value passed to other rules (color lookup,
spacing scale, font family) ?
  -> Regular custom property. No @property needed.

Is the token used in calc() or a CSS function that requires a known type ?
  -> @property registration is RECOMMENDED. Type-safe substitution prevents
     silent fallback to invalid-value when an upstream reference is wrong.

Does the token need a guaranteed initial value if a parent never sets it ?
  -> @property with `initial-value`. Without registration the initial value
     is the guaranteed-invalid value.

Decision tree 3 : Runtime theme switching strategy ?

Theme follows OS preference only (light / dark) ?
  -> color-scheme: light dark on :root AND token values built with
     light-dark(L, D). No JS required. Auto-respects the OS setting.

User can override the OS preference within the app ?
  -> Toggle a data-theme attribute on :root or a region. Token layer
     contains [data-theme="dark"] { ... } and [data-theme="light"] { ... }
     overrides. Combine with light-dark() inside each override.

Multiple coexisting themes (per-tenant, per-section, per-customer brand) ?
  -> Scope the data-theme attribute to a region container instead of :root.
     Token overrides cascade from that region down. Combine with cascade
     layers so component CSS cannot win specificity wars.

Need to prevent flash-of-wrong-theme on first paint ?
  -> Set the data-theme attribute on the server-rendered HTML based on
     a cookie / header. Render the chosen theme inline before the page
     paints. Then hydrate the toggle in JS.

DTCG JSON shape (binding 2025.10)

Every leaf node :

{
  "$value": "...",
  "$type": "color" | "dimension" | "fontFamily" | "fontWeight" | "duration" | "cubicBezier" | "number" | "border" | "shadow" | "transition" | "strokeStyle" | "gradient" | "typography",
  "$description": "Optional human prose.",
  "$extensions": { "com.example.tool": { ... } }
}

Groups :

  • Organize tokens hierarchically without a $value.
  • May declare $type to set the inherited type for descendant tokens.
  • Reserved name $root represents a group's own value when needed.

Aliases :

  • Form : {group.subgroup.token} ; resolves to the target's $value.
  • For nested-property access, JSON Pointer : { "$ref": "#/group/token/$value/property" }.

Source : designtokens.org : Format Module draft (verified 2026-05-19).

Base token types

Type Example $value Notes
color { "colorSpace": "oklch", "components": [0.60, 0.18, 250] } Modern form uses colorSpace + components. Hex string also accepted for sRGB.
dimension "16px" or "1rem" Numeric + unit.
fontFamily "Inter" or ["Inter", "system-ui", "sans-serif"] Single string OR array of fallbacks.
fontWeight 400 or "bold" 1 to 1000 integer OR named (normal, bold, lighter, bolder).
duration "200ms" ms or s.
cubicBezier [0.2, 0, 0, 1] Four-element array.
number 1.5 Unitless.

Composite token types

Type Shape
border { "color": <color>, "width": <dimension>, "style": <strokeStyle> }
shadow { "offsetX": <dimension>, "offsetY": <dimension>, "blur": <dimension>, "spread": <dimension>, "color": <color>, "inset": <boolean> }
transition { "duration": <duration>, "delay": <duration>, "timingFunction": <cubicBezier> }
strokeStyle string : "solid", "dashed", "dotted", etc.
gradient array of { "color": <color>, "position": <number 0..1> }
typography { "fontFamily": <fontFamily>, "fontSize": <dimension>, "fontWeight": <fontWeight>, "letterSpacing": <dimension>, "lineHeight": <number> }

CSS emission rules

@layer tokens, theme, base, components, utilities;

@layer tokens {
  :root {
    /* Primitive tier : raw brand atoms */
    --color-blue-500: oklch(0.60 0.18 250);
    --color-blue-700: oklch(0.40 0.18 250);
    --space-4: 16px;
    --font-sans: "Inter", system-ui, sans-serif;

    /* Semantic tier : intent */
    --color-fg-action: var(--color-blue-500);
    --color-fg-action-hover: var(--color-blue-700);
    --space-inline-md: var(--space-4);

    /* Component tier : component-specific override */
    --button-primary-bg: var(--color-fg-action);
    --button-primary-bg-hover: var(--color-fg-action-hover);
  }
}

@layer theme {
  [data-theme="dark"] {
    --color-fg-action: var(--color-blue-300);
  }
}

@layer components {
  .button-primary {
    background: var(--button-primary-bg);
  }
  .button-primary:hover {
    background: var(--button-primary-bg-hover);
  }
}

The cascade-layer order ensures token declarations always lose to theme overrides, theme overrides always lose to component rules that consume them. See [[frontend-syntax-css-cascade-layers-scope]] for the layering surface.

@property for animatable tokens

@property --gradient-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.banner {
  background: conic-gradient(from var(--gradient-angle), var(--color-blue-500), var(--color-blue-700));
  transition: --gradient-angle 600ms;
}

.banner:hover { --gradient-angle: 360deg; }

Without @property, the transition does nothing because --gradient-angle is treated as a <custom-ident> and only swaps discretely.

Common syntaxes : <color>, <length>, <percentage>, <length-percentage>, <number>, <angle>, <time>, <integer>. Source : MDN : @property (verified 2026-05-19).

Token namespace conventions

Pattern : --<category>-<role>-<variant> (kebab-case).

Category Examples
color --color-fg-action, --color-bg-surface, --color-border-subtle
space --space-inline-md, --space-block-lg
radius --radius-control-sm, --radius-surface-lg
font --font-family-sans, --font-size-body, --font-weight-strong
motion --motion-duration-quick, --motion-easing-emphasized
shadow --shadow-elevation-1, --shadow-elevation-2
border --border-width-thin, --border-style-default

Primitive layer keeps the raw appearance name : --color-blue-500, --space-4. Semantic and component layers use the role-based names above.

Cross-references

  • [[frontend-theming-color-palette-oklch]] : OKLCH palette generation, perceptually-uniform shade ladders that supply the primitive tier.
  • [[frontend-theming-dark-light-mode]] : light-dark(), color-scheme, full dark-mode implementation patterns.
  • [[frontend-syntax-css-cascade-layers-scope]] : @layer declaration order, scope rules, override math.
  • [[frontend-syntax-css-color-modern]] : oklch(), color-mix(), light-dark(), color(display-p3 ...).
  • [[frontend-impl-typography-system]] : type-scale tokens, font-loading strategy.

Reference Links

  • references/methods.md : full DTCG JSON shape, all base + composite types with examples, alias-resolution rules, @property syntax surface.
  • references/examples.md : renderable HTML demo with DTCG JSON snippet, layered CSS emission, theme switch, and @property-animated gradient.
  • references/anti-patterns.md : eight anti-patterns with symptom, root cause, and fix.

Authoritative sources