Methodology
The reasoning behind design-token-lint — why raw Tailwind utilities are banned and what semantic tokens replace them.
design-token-lint enforces a specific approach to building design systems with Tailwind CSS. This page explains the reasoning.
The Problem
Tailwind ships with hundreds of utility classes and a default color palette. This is great for prototypes but creates problems at scale:
Inconsistency creeps in silently. A developer writes
p-4here,p-6there, andp-3somewhere else. None are wrong. None are flagged. Design drifts.Refactoring is expensive. When the design system changes — say, spacing base unit moves from
4pxto6px— every hardcodedp-4needs to be audited and possibly updated.Intent is lost.
p-4says "padding of 4 units" but doesn't say why. Is it card padding? Button inset? Section gutter? The class gives no clue.
The Solution
Replace raw numeric utilities with semantic tokens:
- <div class="p-4 gap-6 bg-gray-100 text-gray-900">
+ <div class="p-hgap-md gap-vgap-sm bg-surface text-fg">The semantic version:
Communicates intent (
surface,fg,md,sm)Can be re-tuned centrally without touching every component
Stays consistent because there's a finite vocabulary
Token Categories
Spacing
Use hgap-* (horizontal) and vgap-* (vertical) suffixes:
p-hgap-sm # small horizontal padding
m-vgap-lg # large vertical margin
gap-hgap-md # medium gapScale names (2xs, xs, sm, md, lg, xl, 2xl, ...) are defined in your Tailwind config and map to actual pixel values.
Colors
Use semantic names that describe role, not hue:
bg-surface # any surface (cards, panels)
bg-surface-alt # alternate surface tint
text-fg # primary foreground
text-muted # de-emphasized text
border-muted # subtle borders
bg-accent # primary action colorOr use project-scoped tokens:
bg-zd-black # project-specific palette
text-p7 # primary palette slot 7The exact names are up to your design system — the linter just blocks default Tailwind colors (gray, blue, red, etc.).
Sizing
The same drift problem applies to dimensions, not just spacing: w-64, h-96, size-40 are just as arbitrary as p-5. By default the linter also bans the numeric width/height/size scale (w-{n}, h-{n}, size-{n}, min-w-{n}, max-w-{n}, min-h-{n}, max-h-{n}, basis-{n}), pointing toward the same kind of semantic scale or an explicit arbitrary value:
w-[240px] # explicit, one-off dimension
h-avatar-lg # semantic sizing token, if your design system defines oneUnlike spacing/color, this project doesn't ship an opinionated sizing-token vocabulary of its own — arbitrary values (w-[240px]) are the pragmatic default escape hatch until your design system defines named sizing tokens.
What Still Works
The linter isn't trying to ban Tailwind. Most utilities still work:
Layout:
flex,grid,block,hidden,w-full,h-screenTypography:
font-bold,text-lg,leading-tight,tracking-wideEffects:
shadow,rounded,opacity-50,transitionZero, for any numeric spacing/sizing utility:
p-0,mt-0,px-0,inset-0,w-0,gap-0, etc. — the linter allows a literal0value automatically, not just the classes listed inallowedNon-numeric spacing-shaped values like
p-1px,mt-1px: these never matched the numeric{n}pattern in the first place, so they pass without needing anallowedentry eitherArbitrary values:
w-[28px],bg-[#123],p-[10px]
Only raw numeric spacing/sizing and default-palette colors are flagged.
Escape Hatches
Sometimes you genuinely need a raw value. Use one of:
Arbitrary values:
p-[14px]— explicit, searchable, reviewer-visibleIgnore comment:
{/* design-token-lint-ignore */}on the preceding line — see ignore syntaxAllowlist: add the class to your config's
allowedarray
Prefer arbitrary values for one-offs. Reserve allowlist for legitimate exceptions that repeat.
Further Reading
This linter is a concrete enforcement of the broader zudo-css-wisdom methodology — a set of patterns for building consistent, maintainable design systems with Tailwind CSS. Read that page for the full theoretical background.