Tailwind CSS Advanced: Systems, Plugins and Production
Design Systems and Tokens
In this lesson you will
- Define primitive and semantic design tokens as theme variables
- Restrict the palette and scales to what the design allows
- Document conventions that keep a team consistent
Introduction
Tailwind gives you a toolbox, but a toolbox does not guarantee consistent products. A design system is the set of decisions (colours, type, spacing, components and rules) that keeps an interface coherent as many people build it. Tailwind v4's CSS-based theme is an excellent home for these decisions. All the CSS behaviour below was compiled with Tailwind 4.3.3.
Design tokens
A token is a named design decision, such as "the brand colour" or "the standard card radius". Tokens come in two layers:
- Primitive tokens: raw values with no meaning attached.
--palette-violet-600: #5b4bf5. - Semantic tokens: names that describe purpose, which point to primitives.
--surface,--text-muted,--accent.
Components should use semantic tokens only. When the brand colour changes or a dark theme is added, you change what the semantic token points to, and no component needs editing.
Tokens as theme variables
CSS
@import "tailwindcss";
/* Primitives */
:root {
--palette-violet-600: #5b4bf5;
--palette-ink-900: #0b1020;
--palette-ink-50: #f5f6fb;
--palette-white: #ffffff;
}
/* Semantic tokens, themeable */
:root {
--surface: var(--palette-white);
--surface-alt: var(--palette-ink-50);
--text: var(--palette-ink-900);
--accent: var(--palette-violet-600);
}
.dark {
--surface: var(--palette-ink-900);
--surface-alt: #141b33;
--text: var(--palette-ink-50);
}
/* Expose them to Tailwind as utilities */
@theme inline {
--color-surface: var(--surface);
--color-surface-alt: var(--surface-alt);
--color-text: var(--text);
--color-accent: var(--accent);
}Now components use bg-surface, text-text and bg-accent, and both themes work with no dark: classes. The inline keyword matters: when I compiled bg-primary from an inline theme variable, the result was background-color: var(--brand), so the utility reads your changing variable directly.
Quick quiz
Why should components use semantic tokens such as --surface instead of raw palette values?
Make the palette strict
A system is only consistent if developers cannot reach for any colour. Reset the default palette and define only yours:
CSS
@theme {
--color-*: initial;
--color-accent: #5b4bf5;
--color-ink: #0b1020;
--color-paper: #ffffff;
--color-danger: #d92d20;
}I verified that after --color-*: initial, a custom colour still generates and a default one such as bg-blue-500 does not. You can reset other scales the same way, such as --font-*, --text-* or --radius-*, and define a short list. A small scale (for example four radii and five shadows) is easier to keep consistent than the full default one.
Scales and rhythm
- Spacing: keep the 4px-based scale (
--spacing: 0.25rem) and agree which steps are normal, such as 2, 4, 6, 8, 12 and 16. - Type: define a few text sizes with their line heights, for example
--text-body,--text-leadand--text-displaythrough the--text-*namespace. - Radius and shadows: pick a small set and name them by role.
- Motion: shared easing and durations through
--ease-*and--animate-*.
Fewer choices mean fewer inconsistencies.
Components encode the rules
Tokens do not prevent bad layouts alone. Build a small library of components (Button, Card, Field, Dialog) with variants that encode your rules, as the previous lesson showed with cva. Product code then uses those components instead of rebuilding buttons.
Guidance that works well:
- Components for repeated patterns, utilities for one-off layout.
- No arbitrary values in product code without review. If a value is needed twice, promote it to a token.
- Document each component with its variants and accessibility notes.
- Version the system and write migration notes when tokens change.
Accessibility is part of the system
Bake accessibility into tokens and components:
- Check colour contrast for each text and surface pairing, in every theme. WCAG asks for at least 4.5 to 1 for normal text and 3 to 1 for large text and UI components.
- Provide visible focus styles as a token or component rule.
- Respect reduced motion with
motion-reduce:in animated components. - Set minimum touch target sizes, around 44 by 44 CSS pixels as a common guideline.
Governance
A design system is people as much as code:
- One or two owners review changes to tokens.
- A changelog records what changed and why.
- A linting or code-review rule flags off-system colours and arbitrary values.
- Examples pages (a living style guide) show every component in every theme.
Try it yourself
Define primitives and semantic tokens for a small site, expose them with @theme inline, reset the colour namespace, and build a Card and Button that use only semantic classes. Switch themes by toggling a class and check the contrast of each pairing.
Summary
- Design tokens separate primitives from semantic names, and components should use semantic tokens only.
- Expose tokens with
@theme inline, and reset namespaces like--color-*to keep the palette strict. - Encode rules in components, include accessibility checks in the system and review changes with clear ownership.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading