Typography & Spacing — Building a Tiny Design System
~12 min · typography, spacing, design-system
Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
Two things separate ad-hoc UIs from designed ones: a typographic scale and a spacing scale. Tailwind gives you defaults; the win is using them with intent.
The typographic scale
Tailwind ships text-xs through text-9xl. The default values follow a roughly 1.25x ratio (a 'major third' in design terms). Use four to six sizes in a single project, not all twelve:
text-xs — captions, helper text, badges
text-sm — secondary copy, table cells
text-base — body copy
text-lg — emphasized body, intro paragraphs
text-xl to text-2xl — section headers
text-3xl to text-4xl — page titles
Resist the urge to use every size. Discipline is what makes a UI feel designed.
Font families
Three slots usually cover an app: a sans-serif for body, a monospace for code/data, and (rarely) a display font for hero titles. Wire them in @theme as --font-sans, --font-mono, --font-display. Tailwind generates font-sans, font-mono, font-display utilities from those.
The spacing scale
Tailwind's spacing scale starts at 0.5 (2px) and goes up. The values follow a 4px grid (1 = 4px, 2 = 8px, 4 = 16px, 6 = 24px, 8 = 32px). Stick to the scale. The moment you start writing p-[13px], your UI is no longer on a grid.
Vertical rhythm
Pick a default vertical spacing between related blocks (space-y-4) and unrelated blocks (space-y-8). Use them consistently. Sections of a page should breathe; clusters within a section should sit close. The brain reads spacing as semantic.
The scale is the design system. A design system isn't a Figma library — it's the set of sizes, spaces, colors, and fonts you commit to. A working Tailwind project with five text sizes, four spacing values, and three semantic colors has a design system. A Figma library with 50 components and no scale discipline doesn't.
Code
@theme — typography + spacing tokens·css
@import "tailwindcss";
@theme {
/* Fonts — three slots */
--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--font-display: "Cal Sans", "Inter", sans-serif;
/* Custom spacing slots layered on top of Tailwind's 4px grid */
--spacing-section: 4rem; /* between major page sections */
--spacing-cluster: 1rem; /* between related items */
--spacing-gutter: 1.5rem; /* page gutters */
}
/* Use as: gap-cluster, py-section, px-gutter */
A page that uses the scale with intent·tsx
function HomePage() {
return (
<main className="min-h-screen bg-bg text-fg">
<div className="max-w-3xl mx-auto px-gutter py-section space-y-section">
{/* Hero — display font, big title */}
<section>
<h1 className="font-display text-4xl text-fg">
Welcome
</h1>
<p className="mt-2 text-lg text-muted">
A short intro paragraph in slightly emphasized body type.
</p>
</section>
{/* Content cluster — tight spacing within, big spacing between sections */}
<section className="space-y-cluster">
<h2 className="text-2xl font-semibold">Latest posts</h2>
<article className="space-y-2">
<h3 className="text-lg font-medium">First post</h3>
<p className="text-base text-muted">Body paragraph.</p>
<p className="text-xs text-subtle">Posted 2026-05-25</p>
</article>
</section>
</div>
</main>
);
}
Take the home page above (or your bootstrap project's App.tsx) and audit it: list every text size, every spacing value, every font family in use. If the list is longer than 6 text sizes, 5 spacing values, or 3 fonts, prune. Refactor to use the reduced set. Notice that the page reads more consistent — that's the design system showing through.
Hint
An honest audit is the hardest part. Grep your JSX for text-, p-, m-, space-y-, gap-, font- and tally. The fixes are easy once you can see the sprawl.
Progress
Progress is local-only — sign in to sync across devices.