Building a Design System

Tokens + Primitives + Recipes

Building a Design System

Combine @theme tokens, primitive components, and variant recipes into a coherent, multi-brand design system.

4 min read Level 3/5 #tailwind#design-system#theme
What you'll learn
  • Define semantic tokens, not just raw colors
  • Build primitives with cva
  • Document and share a theme as a package

A design system in Tailwind v4 is three layers: semantic tokens defined in @theme, primitive components that consume them, and variant recipes built with cva.

Semantic Tokens

Do not scatter blue-500 everywhere. Define meaning-named tokens so a rebrand is one file:

@import "tailwindcss";

@theme {
  --color-surface: oklch(1 0 0);
  --color-fg: oklch(0.21 0.02 256);
  --color-accent: oklch(0.62 0.19 256);
}

These generate bg-surface, text-fg, bg-accent, and so on — utilities that describe role, not appearance.

Primitives Consume Tokens

Primitive components reference only semantic tokens, never raw palette values:

const card = cva('rounded-lg bg-surface text-fg p-6 shadow-sm');
export const Card = (props) => <div className={card()} {...props} />;

Multi-Brand by Token Override

Because primitives only read tokens, a second brand is just a scoped override of those custom properties:

[data-brand="acme"] {
  --color-accent: oklch(0.55 0.21 28);
}

Ship It as a Package

Bundle theme.css plus the primitive components into an internal package. Every app imports the same tokens and recipes, so visual consistency is enforced by the dependency, not by code review.

Accessibility with Tailwind →