Design Tokens as APIs

Design Tokens as APIs
Photo by Luke Jones / Unsplash

tl;dr: Tokens aren’t “variables.” They’re contracts. Treat them like public APIs—version them, deprecate them, ship them, and observe them. Do that, and you’ll unlock multi-brand theming, cross-platform parity, and safer velocity.


1) First principles (a.k.a. what’s real, not vibes)

  • What a token is: a named design decision (name + value, plus optional type/description) expressed in a portable format. That isn’t my definition—that’s the Design Tokens Community Group (DTCG) working draft, which also covers references (aliases) and composite tokens (like “typography” made of font, size, line-height, etc.). (designtokens.org)
  • Why JSON: DTCG defines a JSON interchange so tools can round-trip between design apps, translators (build systems), and docs without bespoke glue. (designtokens.org)
  • Where teams actually live: designers author & theme in Figma (Tokens Studio), devs transform to platform code with Style Dictionary, and everyone speaks the same names. That toolchain is mainstream, not niche. (docs.tokens.studio)
If you remember one thing: a token is a cross-tool, cross-platform contract. Not a hex code.

2) Model tokens like you model APIs

a) Typed, aliasable, composable

DTCG formalizes token types (color, dimension, duration…), aliases ($value can reference another token), and composites (shadow/border/typography objects). These are the primitives that let you express intent, not just values. (designtokens.org)

{
  "brand": {
    "color": {
      "primary": { "$type": "color", "$value": "#4f46e5" },
      "primary-contrast": { "$type": "color", "$value": "#ffffff" }
    }
  },
  "component": {
    "button": {
      "bg": { "$type": "color", "$value": "{brand.color.primary}" },
      "text": { "$type": "color", "$value": "{brand.color.primary-contrast}" }
    }
  }
}

b) Deprecate and evolve

The DTCG draft includes a $deprecated property. Use it to mark old names and ship migration guides—exactly how you would with public APIs. (designtokens.org)

"brand": {
  "color": {
    "accent": {
      "$type": "color",
      "$value": "#22c55e",
      "$deprecated": "Use brand.color.primary"
    }
  }
}

c) Version and distribute

Real systems ship tokens as packages (CSS variables/ESM/JSON) with SemVer. GitHub’s Primer publishes @primer/primitives; Shopify publishes @shopify/polaris-tokens and documents breaking changes (e.g., v11 token overhauls). That’s what “tokens as APIs” looks like in the wild. (primer.style)


3) Build pipeline (the boring part that saves you)

Source of truth: DTCG-flavored JSON in your repo. Translator: Style Dictionary (actively maintained, DTCG-friendly). Outputs: CSS Custom Properties, iOS/Android assets, JS/TS constants, etc. (Style Dictionary)

style-dictionary.config.js (minimal idea):

export default {
  source: ["tokens/**/*.json"],
  platforms: {
    css: {
      transformGroup: "css",
      buildPath: "dist/css/",
      files: [{ destination: "tokens.css", format: "css/variables" }]
    },
    js: {
      transformGroup: "js",
      buildPath: "dist/js/",
      files: [{ destination: "tokens.js", format: "javascript/es6" }]
    }
  }
};

Style Dictionary exists to translate tokens into platform code—this is not hand-rolled magic. Alternatives like Salesforce’s Theo do similar transforms; SD just has the momentum today. (Style Dictionary)


4) Modern CSS makes tokens sing

  • Custom properties are the delivery vehicle (--token-name + var()), and MDN has the canonical usage guide. (MDN Web Docs)
  • Cascade layers @layer let you control precedence between “tokens → components → apps” without specificity hacks—hugely helpful when tokens are your base layer. (MDN Web Docs)
  • OKLCH colors give perceptually uniform palettes; browser support is now solid (check MDN and Can I Use). Tie token values to OKLCH and generate consistent ramps. (MDN Web Docs)
  • light-dark() and relative color syntax mean less theming boilerplate—set two colors or derive variants from a base token directly in CSS. (MDN Web Docs)

Example (tokens → CSS variables → modern color):

/* dist/tokens.css (generated) */
:root {
  --brand-primary: oklch(62% 0.2 275);
  --text-on-primary: #fff;
}

/* app.css (you write this) */
@layer tokens, components, app;

@layer components {
  .btn {
    color: var(--text-on-primary);
    background: light-dark(var(--brand-primary), oklch(65% 0.18 275));
    /* derive a hover by nudging from the base */
    --btn-bg-hover: color(from var(--brand-primary) oklch l c h / 100%);
    --btn-bg-hover: oklch(calc(l - 5%) c h / 100%); /* relative color syntax */
  }
}

Those features are real and shipping—not “future CSS.” (MDN Web Docs)


5) Themes, brands, and platforms

Multiple brands & modes
Model brands/themes as sets that swap values but keep names stable. Tokens Studio explicitly supports token sets + theme switching, and SD can output per-theme bundles. (docs.tokens.studio)

Cross-platform parity
Your tokens compile to iOS/Android/Web without drift—that’s the entire premise of translators (SD/Theo). Salesforce, Shopify, and GitHub all run token packages for this reason. (GitHub)


6) Governance: treat tokens like public APIs

  • SemVer for the token package. Breaking name changes = major. Value tweaks with same semantics (e.g., a brand hue small shift) can be minor. Document how you judge “semantic change” (accessibility thresholds, contrast ratios, spacing scale invariants). Real design systems already communicate this (see Primer and Polaris release notes). (primer.style)
  • Deprecations with sunset dates. Add $deprecated in the source, generate a deprecation report in CI, and publish a migration map (old → new). DTCG gives you the metadata hook; use it. (designtokens.org)
  • Docs: Publish token docs like you would API docs (searchable, examples, “do/don’t”). DTCG explicitly calls out documentation tools (Storybook, Zeroheight, etc.). (designtokens.org)
  • Observability: Add a lint/check step that fails builds on unknown or deprecated tokens and exports usage stats (where each token is imported). If Primer/Polaris can ship tokens on npm, you can instrument usage in your repos, too. (primer.style)

7) The “good defaults” playbook

  1. Start with primitives (color/space/type) and write a naming scheme you can explain in one slide. Primer’s naming guidance is a great reference. (primer.style)
  2. Author DTCG JSON from day one—future-you will want the portability. (designtokens.org)
  3. Transform with SD → emit CSS variables + platform bundles in CI. (Style Dictionary)
  4. Ship a token package (CSS/ESM/JSON) with SemVer. Put a changelog next to it. (primer.style)
  5. Theme as sets, not forks (brands, light/dark). Keep the names. Swap values. (docs.tokens.studio)
  6. Use modern CSS for less code: @layer, oklch(), light-dark(), relative colors. (MDN Web Docs)
  7. Deprecate ruthlessly with $deprecated and migration docs; run a usage report in CI. (designtokens.org)

8) Common failure modes (and fixes)

  • “Dumping ground” tokens: If a token name describes a component (primaryButtonBlue), it’s not a token—it’s a smell. Split into semantic + primitive, then alias. (designtokens.org)
  • Hidden breaking changes: Changing space.2 from 8px → 10px is a major if the spacing scale is part of your public contract. Put contracts in docs; follow SemVer like a grown-up. (primer.style)
  • CSS specificity wars: Fix with @layer and a layering policy: tokens < components < app (and ban !important). (MDN Web Docs)
  • Color jank: Move to OKLCH for ramps; support is there, and your designers will thank you. (MDN Web Docs)

9) Example end-to-end (tiny but real)

/tokens/color.json

{
  "brand": { "primary": { "$type": "color", "$value": "oklch(62% 0.2 275)" } },
  "text": { "on-primary": { "$type": "color", "$value": "#fff" } }
}

Build with Style Dictionary/dist/tokens.css

:root {
  --brand-primary: oklch(62% 0.2 275);
  --text-on-primary: #fff;
}

Consume in app

@layer tokens, components, app;

@layer components {
  .btn {
    background: light-dark(var(--brand-primary), oklch(65% 0.18 275));
    color: var(--text-on-primary);
  }
}

Everything above leans on standards-track CSS and the DTCG + toolchain ecosystem. None of this is speculative. (MDN Web Docs)


10) Close

The fastest way to make your design system trustworthy is to treat tokens like public APIs:

  • Contract first (names/types/semver)
  • Tooling second (DTCG JSON → Style Dictionary → packages)
  • Modern CSS to reduce theme code and improve accessibility
  • Governance (deprecations, docs, observability)

Do that, and you get the real multipliers: fewer regressions, faster theming, better accessibility, and one source of truth feeding every platform.