Skip to content
← All posts

Notes on building a design system with Tailwind v4

·1 min read·

Tailwind v4 quietly does something that, after a few months of using it, has changed how I structure design systems: it moves the theme into CSS. No more tailwind.config.ts with sprawling token objects. Tokens are declared in your stylesheet, and the framework treats them as the source of truth.

Tokens belong in CSS

The @theme directive turns CSS custom properties into Tailwind tokens. That means every color, font, and spacing value can be a real CSS variable — usable from Tailwind utilities and directly in custom CSS, without duplication.

@theme inline {
  --color-brand-500: rgb(99 102 224);
  --font-sans: var(--font-geist-sans), system-ui, sans-serif;
}

A component author no longer has to choose between "use a utility class" and "reach for a CSS variable" — they map to the same value.

Glass utilities are first-class CSS

For effects that span more than a single declaration — backdrop-filter combined with a translucent background and a hairline border, for instance — I’ve stopped trying to express them with strings of utilities. A named utility in @layer utilities is more honest:

@layer utilities {
  .glass {
    background-color: rgb(var(--glass-bg) / var(--glass-bg-opacity));
    backdrop-filter: blur(18px) saturate(160%);
    border: 1px solid rgb(var(--glass-border) / 0.2);
  }
}

The utility carries the design intent. Components stay free of low-level magic numbers.

Dark mode is just another set of token values

With tokens as CSS variables, dark mode is one selector flip:

.dark {
  --color-bg: 8 8 24;
  --color-fg: 233 234 250;
}

Every utility that referenced those tokens — every background, every text color — updates automatically. No dark: prefix sprinkled across the codebase.

What I’d still keep in JS

Not everything fits in CSS. Per-route metadata, build-time content lookups, computed layouts — those still belong in code. But the visual vocabulary belongs in the stylesheet, where designers and engineers can read it without context-switching.

Three months in, I haven’t missed tailwind.config.ts once.

About the author

Kevin Shelley

Engineering Manager bridging people and technology — leading teams that ship production e-commerce while driving adoption of the tools that keep them ahead. See the portfolio · Get in touch.