Tight Token Strategy
The Problem
Tailwind CSS ships with an enormous default token set. The spacing scale alone includes values like 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96 — that is over 30 numeric steps for spacing. Multiply this by colors, font sizes, border radii, and other categories, and the available utility space becomes massive.
In practice, this means any developer on the team can reach for any value at any time. One person writes p-4, another uses p-5, and a third picks p-6 — all for "medium padding." There is no wrong answer because every value is valid, but the result is an inconsistent, drifting UI. Design reviews turn into debates about which numeric step is "correct," and refactoring spacing later requires auditing hundreds of utility classes scattered across the codebase.
The root cause is that Tailwind's default tokens are generic numeric scales, not semantic design decisions. They tell you how much but not why.
The Solution
Replace all Tailwind defaults with a small, intentional set of semantic tokens. Tailwind CSS v4's @theme directive makes this possible by allowing you to reset every built-in token with wildcard patterns and then define only the tokens your project actually needs.
The strategy has two key ideas:
-
Reset everything — Use
--spacing-*: initial;,--color-*: initial;, and similar wildcards to remove all default values. After this, utilities likep-4orbg-gray-500no longer exist. Attempting to use them causes a build error, which is exactly the point: invalid tokens are caught at build time, not in code review. -
Define semantic axes — Instead of a single numeric spacing scale, define separate scales for different purposes. A production approach is to split spacing into two axes:
- hsp (horizontal spacing): for inline gaps, horizontal padding, and horizontal margins
- vsp (vertical spacing): for vertical gaps between sections, vertical padding, and block-level margins
Each axis gets a limited scale from 2xs to 2xl, giving the team exactly 7 choices per axis. Combined with 0 and 1px utility values, this is the entire spacing vocabulary of the project.
Token Table
Horizontal spacing (hsp):
| Token | Value | Usage |
|---|---|---|
hsp-2xs | 5px | Tight inline spacing |
hsp-xs | 12px | Small padding |
hsp-sm | 20px | Default horizontal padding |
hsp-md | 40px | Medium sections |
hsp-lg | 60px | Large sections |
hsp-xl | 100px | Extra large spacing |
hsp-2xl | 250px | Hero / feature spacing |
Vertical spacing (vsp):
| Token | Value | Usage |
|---|---|---|
vsp-2xs | 4px | Minimal gaps |
vsp-xs | 8px | Tight component gaps |
vsp-sm | 20px | Default vertical gaps |
vsp-md | 35px | Section gaps |
vsp-lg | 50px | Large section gaps |
vsp-xl | 65px | Page section gaps |
vsp-2xl | 80px | Hero / major section gaps |
Code Examples
The @theme Reset and Redefinition
This is the core of the strategy. Place this in your project's main CSS file (e.g., app.css):
@theme {
/* ========================================
* Reset ALL Tailwind defaults
* ======================================== */
--spacing-*: initial;
--color-*: initial;
--font-size-*: initial;
--font-family-*: initial;
--font-weight-*: initial;
--line-height-*: initial;
--letter-spacing-*: initial;
--border-radius-*: initial;
--shadow-*: initial;
--inset-shadow-*: initial;
--drop-shadow-*: initial;
--breakpoint-*: initial;
/* ========================================
* Define ONLY project tokens — Spacing
* ======================================== */
--spacing-0: 0;
--spacing-1px: 1px;
/* Horizontal spacing */
--spacing-hsp-2xs: 5px;
--spacing-hsp-xs: 12px;
--spacing-hsp-sm: 20px;
--spacing-hsp-md: 40px;
--spacing-hsp-lg: 60px;
--spacing-hsp-xl: 100px;
--spacing-hsp-2xl: 250px;
/* Vertical spacing */
--spacing-vsp-2xs: 4px;
--spacing-vsp-xs: 8px;
--spacing-vsp-sm: 20px;
--spacing-vsp-md: 35px;
--spacing-vsp-lg: 50px;
--spacing-vsp-xl: 65px;
--spacing-vsp-2xl: 80px;
}
After this configuration:
p-4— build error (no--spacing-4token exists)bg-gray-500— build error (no--color-gray-500token exists)px-hsp-sm— works (resolves topadding-inline: 20px)py-vsp-md— works (resolves topadding-block: 35px)
Usage in Components
With the tight token set, Tailwind classes become self-documenting. You can read the intent directly from the class name:
<section class="px-hsp-sm py-vsp-lg">
<h1 class="pb-vsp-xs">Page Title</h1>
<p class="pb-vsp-sm">Introductory paragraph with standard vertical spacing below.</p>
<div class="flex gap-x-hsp-xs gap-y-vsp-xs">
<div class="px-hsp-xs py-vsp-2xs">Card A</div>
<div class="px-hsp-xs py-vsp-2xs">Card B</div>
</div>
</section>
Every spacing value communicates its axis (horizontal vs vertical) and its relative size within the scale.
Demo: Semantic Tokens vs Arbitrary Values
The following demo shows the same card layout built two ways. The left card uses the tight semantic token approach; the right card uses arbitrary numeric spacing values, simulating how inconsistency creeps in when any value is allowed.
In the "Semantic tokens" card, every section uses values from the token table: vsp-xs (8px) for header/footer vertical padding, hsp-sm (20px) for horizontal padding, vsp-sm (20px) for body vertical padding, hsp-xs (12px) for tag horizontal padding. The result is a visually consistent card with a clear rhythm.
In the "Arbitrary values" card, three different developers each picked slightly different padding values — 10px, 14px, 12px vertically and 16px, 24px, 18px horizontally. Tags have mismatched internal padding. The overall card looks subtly off-balance. This drift compounds across dozens of components in a real project.
Demo: Page Layout with Semantic Spacing
Every spacing value in this layout maps directly to a token from the table. The header uses vsp-xs / hsp-sm, the main section uses vsp-md / hsp-sm, and the card grid uses hsp-sm for gaps. Reading the code (or the Tailwind classes in a real project) immediately tells you which semantic slot each spacing value fills.
When to Use
Good fit
- Large teams where multiple developers touch the same components — the constrained token set prevents spacing drift
- Design-system-driven projects where designers hand off spacing specs using named tokens rather than pixel values
- Production applications where visual consistency directly impacts user trust and brand perception
- Long-lived codebases that will be maintained and refactored over years — a tight token set makes global spacing changes straightforward (update one token, the whole app adjusts)
Not needed
- Prototypes and hackathons where speed matters more than consistency
- Small personal projects where one developer maintains full context
- Tailwind learning projects where using the full default scale is part of the learning process
Deep Dive
For detailed token strategies in specific categories:
- Color Token Patterns — Semantic color scales, brand colors, state colors, and surface layers (see also Three-Tier Color Strategy for the underlying architecture)
- Typography Token Patterns — Font size, line-height, font-weight, and letter-spacing token strategies
- Token Preview — Visual reference of all available tokens
- Component Tokens & Arbitrary Values — When to use system tokens vs arbitrary values