Chapter 5
Typography and Spacing — A Developer's Survival Kit
The two highest-leverage things you can do for a UI without a designer are: pick a sensible type scale and stick to it, and pick a spacing unit and stick to it. Most "this doesn't look professional" reactions are visible right here. Get these two right and you're already past the "looks like a developer made it" line.
This chapter covers what a type scale is, how to pick one, the few rules of typography that actually matter, and the spacing system that quietly powers nearly every well-designed product in 2026. Examples are in Tailwind, which already encodes most of these decisions for you — but it's worth knowing what you're getting.
The myth of "I just need to pick a font"
Engineers approaching UI without design background often start with "what font should I use?" and stop there. Font choice is roughly the last typographic decision that matters. The first three, in order:
- Type scale — the set of sizes you'll use, and the ratio between them.
- Line height — how tall each line is relative to its size.
- Line length — how wide a column of body text is.
A boring system font with these three right looks better than a custom font with them wrong.
The type scale
A type scale is a fixed set of font sizes that your interface uses, generated by multiplying a base size by a ratio.
For example, with a base of 16px (the browser default for body text) and a ratio of 1.25:
| Step | Size | Where to use |
|---|---|---|
| Base × 1.25⁰ | 16px | Body text |
| Base × 1.25¹ | 20px | Subheadings |
| Base × 1.25² | 25px | Section headings |
| Base × 1.25³ | 31px | Page titles |
| Base × 1.25⁴ | 39px | Hero / display |
| Base × 1.25⁻¹ | 13px | Small / metadata |
| Base × 1.25⁻² | 10px | Micro labels |
Common ratios:
- 1.125 (Major Second) — very subtle steps, good for dense UI like dashboards.
- 1.2 (Minor Third) — the most common middle-ground choice.
- 1.25 (Major Third) — slightly more dramatic, good for content-heavy products.
- 1.333 (Perfect Fourth) — strong contrast, common in editorial and marketing sites.
- 1.618 (Golden Ratio) — dramatic, mostly for landing pages with display headlines.
Tailwind's default font sizes are essentially a custom scale based on roughly 1.2:
text-xs 12px
text-sm 14px
text-base 16px
text-lg 18px
text-xl 20px
text-2xl 24px
text-3xl 30px
text-4xl 36px
text-5xl 48px
For most products, using the Tailwind defaults is the right answer. Don't customize unless you have a reason. The defaults are the result of a lot of designer-hours, and they're internally consistent.
The discipline is the same whether you're using Tailwind defaults or your own scale: pick a small set of sizes, and use only those. A UI with sizes drawn from a 7-step scale looks deliberate. A UI with arbitrary sizes — 13px here, 14.5px there, 19px for a heading because "20 felt too big" — looks accidental.
Line height — the invisible decision
Line height (or leading) is the vertical space between lines of text. It's set as a multiplier of font size: 1.5 means the line is 1.5× the font size tall.
The two rules that matter:
- Body text: 1.5–1.6. Comfortable reading, room for descenders, room for the eye to track from line end to next line start. Tailwind's
leading-relaxed(1.625) and the defaultleading-normal(1.5) both fall in this range. - Headings: 1.1–1.3. Large text already has plenty of vertical space relative to its weight; loose leading on a heading makes it feel disconnected from itself.
// Good defaults
<h1 className="text-3xl font-semibold leading-tight">Page title</h1>
<p className="text-base leading-relaxed text-gray-700">Body text…</p>A subtle but consistent failure: setting body text to a single global line-height: 1.2 because "the design looked tight." Cramped body text is exhausting to read. People won't articulate it, but they'll bounce.
Line length — the rule that surprises people
Line length is how many characters fit on a line of body text. Typography research (and centuries of book-printing tradition) converges on a comfortable range:
- Optimal: 50–75 characters per line.
- Acceptable: 45–90.
- Too short: below 40, the eye jumps too often, breaking flow.
- Too long: above 100, the eye loses the next-line target on the long return sweep.
Tailwind has a utility for this: max-w-prose caps content at roughly 65 characters. Use it on every body-text container.
<article className="max-w-prose mx-auto">
<h1 className="text-3xl font-semibold mb-6">Article title</h1>
<p className="leading-relaxed text-gray-700">
Body text that comfortably wraps at around 65 characters per line…
</p>
</article>If you're not sure why a screen "feels off" and the issue isn't size or weight, it's often line length. Wide-screen UIs that let body text run the full container width feel exhausting in a way that's hard to articulate but easy to fix.
Font choice (the part that mattered least)
After the scale, height, and length are settled, you can think about the font.
For 2026, the boring answer is also the right one for most products:
-
Default to a system font stack unless you have a brand reason not to.
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;Tailwind's
font-sansis essentially this. Zero extra HTTP requests, perfect rendering, native to each OS, free. -
If you want a custom font, pick one variable font. A variable font is a single file that contains a range of weights and styles, much smaller than loading multiple weight files. Inter, IBM Plex Sans, Geist, and Plus Jakarta Sans are all good neutral choices.
-
Two fonts max in a product. One sans for UI, one serif if you want editorial flavour for long-form reading. Three is a lot. Five is a problem.
-
Self-host fonts where you can. Google Fonts is fine for quick prototypes; for production, self-hosting is faster, more private, and avoids an external dependency.
What to avoid:
- Decorative fonts in body UI (script fonts, novelty fonts — except as branded display)
- Three or more fonts mixed in close proximity
- Loading 6 weights when you'll use 2
The 4-pixel grid
The other half of the survival kit: a consistent spacing unit applied across the whole interface.
The convention in modern design systems is a 4px (or sometimes 8px) base unit. Every space — padding, margin, gap, height — is a multiple of that unit. Tailwind's spacing scale is built on it:
0.5 = 2px
1 = 4px
2 = 8px
3 = 12px
4 = 16px
6 = 24px
8 = 32px
12 = 48px
16 = 64px
20 = 80px
This means p-4, gap-4, mt-6, mb-12 all snap to the grid. Spacing values that don't snap to the grid look "off" even when users can't say why. A 17px margin next to a 16px gap creates micro-misalignments the eye picks up unconsciously.
The rule: use only Tailwind spacing utilities, never arbitrary values like mt-[17px], unless you have a specific alignment reason.
Vertical rhythm
A useful pattern is vertical rhythm — consistent spacing between sections that creates a sense of regularity.
<div className="space-y-8">
<section>{/* Section 1 */}</section>
<section>{/* Section 2 */}</section>
<section>{/* Section 3 */}</section>
</div>space-y-8 puts 32px between every direct child. The eye picks up the rhythm; the page feels deliberate. Compare to ad-hoc mb-6, mb-8, mb-4 on each section — same rough spacing, much less coherent.
Spacing within elements
A useful mental model — three "scales" of spacing for different relationships:
- Tight (4–8px) — between closely related elements, e.g., a label and its input.
- Medium (12–24px) — between elements within the same group, e.g., between fields in a form.
- Wide (32–64px) — between distinct sections.
Mix these consistently and the structure of the screen becomes legible at a glance. Crowded UIs almost always have too little large spacing; fragmented UIs have too much small spacing.
Putting it together — a typed and spaced component
A small but representative example: a settings card.
<section className="max-w-prose">
{/* Section heading: tight leading, generous bottom margin */}
<h2 className="text-2xl font-semibold leading-tight mb-2">
Notification preferences
</h2>
<p className="text-sm text-gray-600 mb-6 leading-relaxed">
Choose how and when you want to be notified.
</p>
{/* Form fields: medium spacing between, tight within */}
<div className="space-y-4">
<label className="block">
<span className="block text-sm font-medium text-gray-700 mb-1">
Email frequency
</span>
<select className="border rounded-md px-3 py-2 w-full">
<option>Real-time</option>
<option>Daily digest</option>
<option>Weekly summary</option>
</select>
</label>
{/* …more fields… */}
</div>
{/* Wide separation before action row */}
<div className="mt-8 flex items-center gap-3">
<button className="bg-blue-600 text-white px-4 py-2 rounded-md font-medium">
Save changes
</button>
<button className="text-gray-600 px-4 py-2 font-medium">
Cancel
</button>
</div>
</section>Notice the spacing relationships:
- 4px (
mb-1) between label and input — tight, signalling they belong together - 16px (
space-y-4) between fields — medium, signalling same-group separation - 32px (
mt-8) before action row — wide, signalling a different concern
Type:
text-2xl font-semiboldfor heading — clearly the most prominenttext-sm text-gray-600for description — secondarytext-sm font-medium text-gray-700for labels — slightly heavier than description- Default
text-baseimplied for input text
That's the system. It's not glamorous, but applying it consistently across an entire app is the difference between "feels professional" and "feels homemade."
Real-world examples to study
Three product UIs worth opening and squinting at:
- Vercel Dashboard. Clean type scale, generous whitespace, controlled use of weight.
- GitHub. Decades-refined typography, perfect line lengths in code, consistent spacing.
- Linear. Compact but never cramped — masterful tight-vs-medium spacing.
In each, look for: how many sizes are in use, what the line lengths feel like, where they use bold vs regular, and how spacing reinforces grouping.
Going deeper. Refactoring UI by Adam Wathan and Steve Schoger is the practical companion to this chapter. It's the book that translated design intuition into rules engineers can apply, and most of its 200 pages are either typography or spacing. If anything in this chapter clicked, that book pays for itself the first time you ship a UI after reading it.
A typography & spacing checklist
For every screen you ship:
- Type scale: are you using only sizes from a defined scale? No
text-[17px]. - Body text line height: 1.5–1.6. Headings: 1.1–1.3.
- Body text width: capped around 65–75 characters.
max-w-proseif in doubt. - Maximum two fonts in the whole product. Often one is enough.
- Spacing snaps to the 4px grid. No arbitrary values.
- Consistent vertical rhythm between sections. Use
space-y-*utilities. - Three spacing scales — tight (4–8), medium (12–24), wide (32–64) — used intentionally.
What you should walk away with
- Type scale, line height, and line length are the typography decisions that matter most. Font choice is far behind these.
- Tailwind's defaults are good defaults. Customize only with reason; the boring choice is usually the right one.
- The 4px grid is not optional. Spacing values that don't snap to it create micro-misalignments people can't name but can feel.
- Three spacing scales — tight, medium, wide — used consistently, encode grouping into the visual structure.
In chapter 6 we add the third leg of "design system" basics: a colour system that's both internally consistent and accessible by WCAG standards, with real Tailwind code.
Next up — Chapter 6: Color Systems and Accessibility — WCAG, with Real Code. Why your gray-on-gray secondary text might be illegal in some jurisdictions, and how to build a colour system that ships clean Lighthouse audits.