Master the three CSS properties that determine body text readability: line-height, letter-spacing, and measure. Learn the rules, the numbers, and when to break them.
You can choose the perfect typeface, load it with optimal performance, and scale it beautifully — and still produce unreadable text by getting line height, letter spacing, or line length wrong. These three properties are where most implementation mistakes live. A line height of 1.2 on body text is suffocating; 2.0 is disorienting. Tracking expanded 0.2em on 14px labels looks elegant; the same tracking on 48px headings looks desperate. This lesson gives you the numbers and the reasoning behind them.
line-height in CSS accepts unitless numbers (1.5), lengths (24px), percentages (150%), or the keyword normal. Always use unitless numbers. Here is why: when you write line-height: 1.5, every element that inherits this value multiplies its own font-size by 1.5 to get its line height. A child element with font-size: 2rem gets line-height: 3rem. If instead you write line-height: 24px, every inheriting element uses 24px regardless of its font-size — a 32px heading gets 24px line height, causing lines to collide. The word normal resolves to approximately 1.2 and is too tight for body text on most typefaces. Recommended values: 1.4–1.6 for body text in long-form reading. 1.2–1.35 for heading text. 1.0–1.1 for large display text (tighter optical relationship between lines). Below 1.0 lines overlap. Never use below 1.0 except for decorative single-line text.
1/* Correct: unitless values — scale proportionally */
2body { line-height: 1.6; } /* ~25.6px at 16px font */
3h3 { line-height: 1.3; } /* ~26px at 20px font */
4h1 { line-height: 1.1; } /* ~38.5px at 35px font */
5.display{ line-height: 1.0; } /* Same as font-size */
6
7/* Wrong: px value — does not scale with font-size */
8body { line-height: 24px; } /* h1 with 48px font is crushed */Letter spacing (tracking) in CSS is the letter-spacing property, expressed in em units for proportional scaling or px for fixed offsets. The cardinal rule: never track body text tighter than the typeface default (0em). Negative tracking is only appropriate for large display headings (48px+) where the natural optical spacing looks loose. At large sizes, -0.02em to -0.04em improves cohesion. At body size (14–18px) negative tracking collapses letters together, destroying legibility. Uppercase text almost always needs positive tracking — all-caps words with default spacing look cramped. 0.05em–0.12em is typical for uppercase labels, buttons, and navigation. The rule of thumb: the larger the text, the tighter you can track; the smaller the text, the looser you should track.
1/* Body text: never track */
2body {
3 letter-spacing: 0; /* Do not touch — designed for this size */
4}
5
6/* Uppercase labels: positive tracking improves readability */
7.label, .badge, .nav-item {
8 text-transform: uppercase;
9 font-size: 0.75rem;
10 letter-spacing: 0.08em; /* 1.2px at 15px — comfortable */
11 font-weight: 600;
12}
13
14/* Large display headings: slight negative tracking */
15.display-heading {
16 font-size: clamp(3rem, 6vw, 6rem);
17 letter-spacing: -0.025em; /* Tightens optical spacing at large sizes */
18 line-height: 1.05;
19}
20
21/* Subheadings: neutral to very slightly loose */
22h2 {
23 font-size: 1.777rem;
24 letter-spacing: -0.01em; /* Tiny negative for crisp display */
25}The measure is the width of a line of text. The classic typographic recommendation is 45–75 characters per line (66 characters is often cited as ideal for single-column body text). Too narrow (under 40ch) and the eye performs too many line returns, creating a choppy, breathless reading experience. Too wide (over 80ch) and the eye cannot track back to the start of the next line reliably, causing re-reading of the same line. The CSS ch unit is defined as the width of the '0' (zero) character in the current font at the current size. For proportional fonts, ch is an approximation — the average character width is around 0.5ch, so a max-width of 65ch gives roughly 65 average-width characters per line. This is a reasonable approximation; the exact character count varies by typeface. For monospace fonts, ch is exact.
1/* Optimal measure for body text */
2article, .prose {
3 max-width: 65ch; /* ~65 chars per line — ideal for reading */
4 margin-inline: auto; /* Centre in available space */
5}
6
7/* Narrow measure for pull quotes or callouts */
8blockquote {
9 max-width: 45ch;
10 margin-inline: auto;
11 font-size: 1.25em;
12 line-height: 1.55; /* Slightly looser for shorter measure */
13}
14
15/* Wide measure for two-column layouts */
16.two-col-text {
17 /* Each column gets ~40ch — right at the minimum */
18 columns: 2;
19 column-gap: 3rem;
20 max-width: 85ch; /* Total width across both columns */
21}word-spacing adds or removes space between words. Default (normal) is appropriate for almost all body text. But it becomes relevant in two edge cases: justified text where inter-word gaps can grow extremely wide at narrow column widths (use word-spacing: normal and rely on hyphens: auto instead), and uppercase or wide-tracked headings where the combination of tracking and normal word spacing creates inconsistent visual rhythm. A small positive word-spacing (0.05em–0.1em) with uppercase tracked text maintains the visual separation between words without relying entirely on tracking.
1/* Justified text: let hyphens handle gaps, not word-spacing */
2.justified {
3 text-align: justify;
4 hyphens: auto;
5 -webkit-hyphens: auto;
6 hyphenate-limit-chars: 6 3 3; /* Min word, before-break, after-break */
7 /* Do NOT use word-spacing here — it compounds with justify gaps */
8}
9
10/* Uppercase tracked heading: slight word-spacing for rhythm */
11.section-label {
12 text-transform: uppercase;
13 letter-spacing: 0.1em;
14 word-spacing: 0.05em; /* Slightly wider word gaps match tracking */
15 font-size: 0.75rem;
16 font-weight: 700;
17 color: #6b7280;
18}
19
20/* Body text: leave word-spacing at default */
21body { word-spacing: normal; }The five most common readability failures and their CSS fixes. (1) Collapsed leading (line-height: 1.2 on body text): fix with line-height: 1.6. (2) Excess tracking on small text (letter-spacing: 0.1em on 14px body): fix by removing tracking from body text entirely. (3) Overflowing measure (no max-width on article content at desktop): fix with max-width: 65ch. (4) Too-tight display heading (line-height: 1.4 on a 60px h1): fix with line-height: 1.05. (5) Uppercase text without tracking (text-transform: uppercase on nav items with default spacing): fix with letter-spacing: 0.08em.
1/* Readability audit: check these properties on any site */
2
3/* 1. Line height audit */
4getComputedStyle(document.body).lineHeight
5/* Should be ~1.5-1.6× font-size for body text */
6
7/* 2. Measure audit */
8/* Open DevTools, hover article element, check width */
9/* Should be under 75ch for readable body text */
10
11/* 3. Tracking audit */
12getComputedStyle(document.body).letterSpacing
13/* Should be 0 or 'normal' for body text */
14
15/* Common fixes applied together */
16article {
17 font-size: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
18 line-height: 1.65; /* Fix: collapsed leading */
19 max-width: 65ch; /* Fix: excessive measure */
20 letter-spacing: 0; /* Fix: remove errant tracking */
21 margin-inline: auto;
22}
23article h1 {
24 line-height: 1.1; /* Fix: too-loose display heading */
25 letter-spacing: -0.02em; /* Appropriate negative track for large size */
26}Before and after comparison of a poorly set article with all five common readability failures corrected.
Next lesson covers typography accessibility — WCAG 1.4.12 Text Spacing, minimum contrast ratios, dyslexia-friendly settings, and how to build type systems that accommodate all users.
Your progress is saved in this browser, on this device only.