I've spent years building design systems, and I've found that the secret sauce isn't just the components—it's the tokens. Design tokens are the smallest atoms of your visual language, and using CSS variables is the most efficient way to bring them to life.

The Tiered Token Structure

I personally love using a three-tier system: Global, Alias, and Component tokens to keep things manageable and scalable.

/* Global (Primitive) Tokens */
:root {
  --brand-blue-500: #0070f3;
  --brand-blue-600: #005bc1;
  --grey-100: #f5f5f5;
  --grey-900: #111111;
  
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-4: 1rem;
}

Global tokens define your raw palette, but you shouldn't use them directly in your components; instead, we map them to Alias tokens.

/* Alias (Semantic) Tokens */
:root {
  --color-text-primary: var(--grey-900);
  --color-bg-canvas: var(--grey-100);
  --color-action-primary: var(--brand-blue-500);
  --color-action-hover: var(--brand-blue-600);
  
  --radius-interactive: 8px;
  --shadow-card: 0 4px 6px rgba(0, 0, 0, 0.1);
}

Naming Conventions That Actually Work

Naming is hard, but I've found that a `category-property-concept-state` pattern works wonders for keeping your CSS predictable.

/* The pattern: --[category]-[property]-[concept]-[state] */
.button-primary {
  --btn-bg: var(--color-action-primary);
  --btn-text: white;
  --btn-border-radius: var(--radius-interactive);
  
  background-color: var(--btn-bg);
  color: var(--btn-text);
  border-radius: var(--btn-border-radius);
}

This approach allows you to override specific component values without breaking the global theme or affecting other elements.

.button-primary:hover {
  --btn-bg: var(--color-action-hover);
  box-shadow: var(--shadow-card);
}

.button-primary:disabled {
  --btn-bg: var(--grey-100);
  cursor: not-allowed;
}

Mastering Dark Mode with Semantic Tokens

What I find fascinating about semantic tokens is how they make dark mode almost trivial to implement by simply swapping values.

/* Light Mode (Default) */
:root {
  --color-bg-main: #ffffff;
  --color-text-body: #222222;
  --color-border-subtle: #e5e7eb;
}

/* Dark Mode Override */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg-main: #0f172a;
    --color-text-body: #f8fafc;
    --color-border-subtle: #334155;
  }
}

I usually suggest adding a data-attribute selector as well, so users can manually toggle the theme regardless of system settings.

[data-theme='dark'] {
  --color-bg-main: #1a1a1a;
  --color-text-body: #eeeeee;
  --color-border-subtle: #444444;
}

body {
  background-color: var(--color-bg-main);
  color: var(--color-text-body);
  transition: background-color 0.3s ease;
}

Spacing and Layout Tokens

Don't stop at colours; I use tokens for everything, especially spacing, to ensure the UI feels balanced and consistent.

:root {
  --size-base: 1rem;
  --space-xs: calc(var(--size-base) * 0.5);
  --space-sm: calc(var(--size-base) * 0.75);
  --space-md: var(--size-base);
  --space-lg: calc(var(--size-base) * 1.5);
  --space-xl: calc(var(--size-base) * 2);
}

Using these tokens in a layout makes it incredibly easy to adjust the 'density' of your entire application by changing a single variable.

.card {
  padding: var(--space-md);
  margin-bottom: var(--space-lg);
  display: flex;
  gap: var(--space-sm);
}

.container {
  max-width: 1200px;
  padding-inline: var(--space-xl);
}

Type Systems and Fluid Scales

Here's a trick I use for responsive typography: using `clamp()` within tokens to create fluid text that scales without media queries.

:root {
  --text-xs: 0.75rem;
  --text-base: 1rem;
  /* Fluid scale: min 1.5rem, max 3rem */
  --text-display: clamp(1.5rem, 5vw, 3rem);
  
  --font-weight-bold: 700;
  --font-family-sans: 'Inter', system-ui, sans-serif;
}

Applying these tokens ensures your hierarchy remains intact across all devices without messy CSS overrides.

h1 {
  font-size: var(--text-display);
  font-weight: var(--font-weight-bold);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

p {
  font-size: var(--text-base);
  line-height: 1.5;
}

Wrapping Up

Design tokens are the backbone of a professional design system. By using CSS variables effectively, you create a codebase that is easy to theme, maintain, and scale.

  • Use a tiered system (Global -> Alias -> Component) to manage complexity.
  • Stick to a strict naming convention to make tokens self-documenting.
  • Leverage semantic tokens to make dark mode and theming a breeze.
  • Tokenise spacing and typography for perfect UI consistency.

I encourage you to try refactoring just one small component using this token strategy—you'll see the benefits immediately! If you found this helpful, I'd love to connect! Follow me on Twitter/X or LinkedIn for more CSS and design system tips.