All insights
Design6 min read

Design Tokens: The Foundation of a Scalable Design System

Design tokens are the atoms of your design system — single source-of-truth values that make rebrandable, consistent, maintainable UI possible at scale.

NC

Nextcraft Engineering Team

What Design Tokens Actually Are

A design token is a named design decision stored in a technology-agnostic format. Instead of hardcoding #3B82F6 as your primary blue in every component, you define:

code
{
  "color": {
    "brand": {
      "primary": { "value": "#3B82F6" }
    }
  }
}

And reference color.brand.primary everywhere. When the primary blue changes — new brand refresh, white-label requirement, accessibility fix — you change it once, in one place.

This sounds simple. The implications are profound at scale.

The Three Tiers of Tokens

A well-structured token system has three tiers, each building on the previous:

Tier 1: Primitive Tokens (Raw Values)

code
{
  "color": {
    "blue-500": "#3B82F6",
    "blue-600": "#2563EB",
    "gray-100": "#F3F4F6",
    "gray-900": "#111827"
  },
  "space": {
    "4": "16px",
    "8": "32px"
  }
}

These are just named values with no semantic meaning. Never use these directly in components.

Tier 2: Semantic Tokens (Intent)

code
{
  "color": {
    "text": {
      "primary": "{color.gray-900}",
      "secondary": "{color.gray-600}",
      "inverse": "{color.white}"
    },
    "surface": {
      "brand": "{color.blue-500}",
      "brand-hover": "{color.blue-600}"
    }
  }
}

Semantic tokens describe the intent, not the value. This is what your components reference.

Tier 3: Component Tokens (Specific Overrides)

code
{
  "button": {
    "primary": {
      "background": "{color.surface.brand}",
      "background-hover": "{color.surface.brand-hover}",
      "text": "{color.text.inverse}"
    }
  }
}

Component tokens let specific components deviate from semantic defaults without hardcoding values.

Why This Structure Enables Dark Mode

Dark mode becomes trivial when your components only reference semantic tokens. You just swap the semantic token values:

code
{
  "light": {
    "color.text.primary": "{color.gray-900}",
    "color.surface.default": "{color.white}"
  },
  "dark": {
    "color.text.primary": "{color.gray-100}",
    "color.surface.default": "{color.gray-900}"
  }
}

Every component that uses color.text.primary automatically adapts. No component-level dark mode logic needed.

Implementing Tokens in a Next.js + Tailwind Project

With Tailwind, the most practical approach is to extend the theme with your semantic tokens:

code
// tailwind.config.ts
export default {
  theme: {
    extend: {
      colors: {
        brand: {
          DEFAULT: 'hsl(var(--color-brand))',
          hover: 'hsl(var(--color-brand-hover))',
        },
        surface: {
          DEFAULT: 'hsl(var(--color-surface))',
          elevated: 'hsl(var(--color-surface-elevated))',
        },
        text: {
          primary: 'hsl(var(--color-text-primary))',
          secondary: 'hsl(var(--color-text-secondary))',
        },
      },
    },
  },
};

Then define the actual values as CSS custom properties, swapped per theme:

code
/* globals.css */
:root {
  --color-brand: 217 91% 60%;
  --color-brand-hover: 221 83% 53%;
  --color-surface: 0 0% 100%;
  --color-surface-elevated: 210 40% 98%;
  --color-text-primary: 222 47% 11%;
  --color-text-secondary: 215 16% 47%;
}

.dark {
  --color-surface: 222 47% 11%;
  --color-surface-elevated: 217 33% 17%;
  --color-text-primary: 210 40% 98%;
  --color-text-secondary: 215 20% 65%;
}

Components use Tailwind classes like bg-surface, text-text-primary, bg-brand — and the theme switch is handled entirely at the CSS layer.

The White-Label Superpowe

Once you have a token system, white-labeling (providing your product under a client's brand) becomes a configuration problem, not an engineering problem.

For each client, you provide a different set of primitive and semantic token values. The component library doesn't change. Only the tokens do.

This is how enterprise SaaS companies like Intercom and Linear power-white-label offerings without maintaining parallel codebases.

Token Management at Scale

For larger teams, manage tokens with dedicated tooling:

  • Style Dictionary (Amazon) — transforms tokens from JSON into CSS, SCSS, JS, Swift, and Android formats
  • Tokens Studio (Figma plugin) — syncs tokens between Figma and code
  • Theo (Salesforce) — an alternative to Style Dictionary with different transformation capabilities

The key workflow: designers own the token values in Figma, engineers consume them in code, and a CI step ensures they stay in sync. Any drift between design and code is caught before it ships.

Tokens Are a Team Protocol

The real value of design tokens is not technical — it's organizational. They create a shared vocabulary between designers and engineers.

When a designer says "this uses the surface-elevated token," engineers know exactly which CSS variable applies. When an engineer sees bg-surface-elevated in code, they can look up the token and understand the design intent.

This shared language reduces the back-and-forth, the "this doesn't match the mockup," and the "which blue is that?" conversations. At scale, that efficiency compounds significantly.

Start with 20 tokens covering your core colors, spacing scale, and typography. Add tokens when you find yourself hardcoding a value more than twice. Resist the urge to tokenize everything immediately — over-tokenized systems are as hard to maintain as no tokens at all.

Stay Informed.

Join 1,200+ founders and engineers receiving our monthly deep dives on product engineering, design, and growth.

Insights once a month. No spam. Unsubscribe anytime.