Good typography is mostly invisible โ when text is well-spaced, readers absorb content without noticing the typesetting. But when line-height is too tight, measure too wide, or letter-spacing wrong, readers fatigue quickly. In this lesson you will master the CSS properties that control how text breathes: line-height, letter-spacing, word-spacing, text-indent, and the critical concept of measure (line length) โ the properties that separate professional typography from amateur work.
Text Spacing Properties
| Property | Controls | Recommended Values |
|---|---|---|
line-height |
Vertical space between lines of text | 1.5โ1.75 for body; 1.1โ1.3 for headings |
letter-spacing |
Horizontal space between individual characters | -0.02em on headings; 0.05โ0.1em on uppercase labels |
word-spacing |
Space between words | Normal is usually ideal; use sparingly |
text-indent |
First-line indentation of a paragraph | 1โ2em for classic book indentation |
max-width (measure) |
Maximum line length โ characters per line | 60โ75 characters โ approx 45โ65ch |
line-height Values Compared
| Value | Type | Behaviour | Recommendation |
|---|---|---|---|
1.6 (unitless) |
Multiplier | Scales with element font-size | โ Best โ child elements inherit the ratio not a computed value |
1.6em |
em | Computed once; children inherit the px value | โ ๏ธ Causes inheritance issues in nested elements |
26px |
Fixed px | Fixed regardless of font-size changes | โ Breaks if font-size changes |
normal |
Browser default | ~1.2 in most browsers | โ Too tight for body text |
Typography Readability Guidelines
| Element | line-height | letter-spacing | Measure (ch) |
|---|---|---|---|
| Body paragraph | 1.6โ1.75 | 0 (default) | 55โ75ch |
| Large heading (h1) | 1.05โ1.2 | -0.02em to -0.04em | 20โ30ch |
| Small heading (h3โh4) | 1.25โ1.4 | -0.01em | 35โ50ch |
| UI label / caption | 1.4โ1.5 | 0.04โ0.1em (uppercase) | N/A |
| Code / monospace | 1.5โ1.7 | 0 (default) | 80โ100ch |
line-height (e.g. 1.6 not 1.6em or 26px). A unitless value is a multiplier โ children inherit the ratio and apply it to their own font-size. A px or em value is computed once and the fixed pixel result is inherited, causing cramped or excessively spaced child text.ch unit โ the width of the “0” character in the current font โ is ideal for controlling line length (measure). max-width: 65ch gives approximately 65 characters per line, which typographers recommend as the optimal reading measure for body text. It automatically adjusts when the font changes.letter-spacing on body text at small sizes makes text harder to read. Only use negative letter-spacing on large headings (24px+) where default tracking feels too loose. Adding positive letter-spacing to all-caps labels (letter-spacing: 0.08em) significantly improves readability for short uppercase strings.Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
<title>Text Spacing and Readability</title>
<style>
html { font-size: 100%; }
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Inter', system-ui, sans-serif; background: #f8fafc; padding: 40px 24px; color: #334155; }
/* โโ Article with optimal readability โโ */
.article {
max-width: 65ch; /* ~65 chars per line โ ideal measure */
margin: 0 auto 48px;
background: white;
border-radius: 16px;
padding: 40px 48px;
border: 1px solid #e2e8f0;
}
.kicker {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em; /* wide tracking on uppercase */
color: #4f46e5;
margin-bottom: 12px;
}
.article h1 {
font-family: 'Playfair Display', Georgia, serif;
font-size: clamp(1.75rem, 4vw, 2.5rem);
font-weight: 700;
line-height: 1.15; /* tight for large headings */
letter-spacing: -0.025em; /* slightly tighter tracking */
color: #0f172a;
margin-bottom: 20px;
}
.byline {
font-size: 0.85rem;
color: #94a3b8;
margin-bottom: 28px;
padding-bottom: 28px;
border-bottom: 1px solid #f1f5f9;
letter-spacing: 0.01em;
}
.article p {
font-size: 1rem;
line-height: 1.75; /* relaxed for body text */
color: #475569;
margin-bottom: 20px;
/* First paragraph โ no indent */
}
/* Traditional book-style: indent after first paragraph */
.article p + p {
text-indent: 1.5em;
margin-bottom: 16px;
}
.article h2 {
font-size: 1.2rem;
font-weight: 700;
line-height: 1.3;
letter-spacing: -0.01em;
color: #0f172a;
margin: 32px 0 12px;
}
/* โโ Spacing comparison โโ */
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
max-width: 900px;
margin: 0 auto;
}
.comp-box {
background: white;
border-radius: 12px;
padding: 24px;
border: 1px solid #e2e8f0;
}
.comp-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #64748b;
margin-bottom: 12px;
}
.tight { line-height: 1.1; font-size: 0.9rem; color: #334155; }
.loose { line-height: 1.75; font-size: 0.9rem; color: #334155; }
</style>
</head>
<body>
<article class="article">
<p class="kicker">CSS Typography</p>
<h1>The Art of Readable Text on the Web</h1>
<p class="byline">By Jane Smith · June 2025 · 5 min read</p>
<p>Well-crafted typography is the foundation of every readable website. The spacing between lines, the width of the text column, and the subtle tightening of heading letter-spacing all work together invisibly to guide the reader's eye and reduce cognitive load.</p>
<p>Research consistently shows that lines between 55 and 75 characters produce the fastest reading speeds and the highest comprehension. Wider columns force the eye to travel too far; narrower columns require too many line returns.</p>
<h2>Line Height Is Not Just Aesthetic</h2>
<p>A line-height below 1.4 on body text causes readers to re-read lines or lose their place. A line-height above 1.9 makes the reader feel the text is disconnected โ each line floats independently. The 1.6โ1.75 sweet spot creates rhythm without drift.</p>
</article>
<div class="comparison">
<div class="comp-box">
<p class="comp-label">line-height: 1.1 (too tight)</p>
<p class="tight">This paragraph has a line-height of 1.1 โ far too tight for comfortable body reading. The lines crowd together and the eye struggles to track from the end of one line to the start of the next.</p>
</div>
<div class="comp-box">
<p class="comp-label">line-height: 1.75 (ideal)</p>
<p class="loose">This paragraph has a line-height of 1.75 โ comfortable and breathing. The eye can easily find the next line, and the text feels open without the lines feeling disconnected from each other.</p>
</div>
</div>
</body>
</html>
How It Works
Step 1 โ max-width: 65ch Enforces Optimal Measure
The ch unit equals the advance width of the “0” character. max-width: 65ch limits lines to approximately 65 characters โ within the typographically optimal 55โ75 range. Because it is relative to the font, the measure automatically adjusts when font-size or font-family changes.
Step 2 โ Unitless line-height Inherits Correctly
The body’s line-height: 1.75 is a ratio. A nested element with font-size: 0.75rem inherits the ratio 1.75 and applies it to its own 12px size, giving line-height of 21px. If we had set line-height: 28px, the nested element would also inherit 28px โ too loose for 12px text.
Step 3 โ Negative letter-spacing Tightens Large Headings
At large sizes (2rem+), the default browser letter-spacing between glyphs feels loose. letter-spacing: -0.025em pulls characters slightly closer, giving headings a more professional, publication-quality feel. The em unit means the tightening scales proportionally with font-size.
Step 4 โ Wide letter-spacing Improves Uppercase Labels
The kicker uses letter-spacing: 0.1em โ 10% of the font-size added between each character. All-caps text at small sizes becomes harder to read without tracking because capitals were designed with inter-character spacing in mind for mixed case. Adding tracking compensates.
Step 5 โ text-indent Creates Book Paragraph Style
Using the CSS sibling selector p + p (a paragraph directly after another paragraph), the text-indent: 1.5em applies only to subsequent paragraphs โ the traditional print convention where only the first paragraph of a section has no indent, while following paragraphs are indented.
Real-World Example: Editorial Article Stylesheet
/* editorial.css โ production article typography */
:root {
--font-body: 'Georgia', serif;
--font-ui: 'Inter', system-ui, sans-serif;
--color-text: #1c1917;
--color-muted: #78716c;
--color-accent: #dc2626;
}
.prose {
font-family: var(--font-body);
font-size: clamp(1rem, 1.5vw, 1.125rem);
line-height: 1.8;
color: var(--color-text);
max-width: 68ch;
margin-inline: auto;
}
/* Headings inside prose */
.prose h1 {
font-family: var(--font-ui);
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.03em;
margin-bottom: 0.75em;
color: var(--color-text);
}
.prose h2 {
font-family: var(--font-ui);
font-size: clamp(1.25rem, 2.5vw, 1.75rem);
font-weight: 700;
line-height: 1.25;
letter-spacing: -0.02em;
margin: 2em 0 0.75em;
color: var(--color-text);
}
.prose h3 {
font-family: var(--font-ui);
font-size: 1.125rem;
font-weight: 600;
line-height: 1.4;
letter-spacing: -0.01em;
margin: 1.5em 0 0.5em;
}
/* Body paragraphs */
.prose p { margin-bottom: 1.5em; }
.prose p + p { text-indent: 1.25em; }
/* Kicker / category label */
.prose .kicker {
font-family: var(--font-ui);
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--color-accent);
display: block;
margin-bottom: 0.75em;
}
/* Blockquote / pull quote */
.prose blockquote {
font-size: 1.2em;
font-style: italic;
line-height: 1.5;
border-left: 4px solid var(--color-accent);
padding: 0.75em 1.25em;
margin: 2em 0;
color: var(--color-muted);
}
/* Inline code */
.prose code {
font-family: 'JetBrains Mono', 'Courier New', monospace;
font-size: 0.875em;
background: #f5f5f4;
padding: 0.15em 0.4em;
border-radius: 4px;
color: #c2410c;
}
Common Mistakes
Mistake 1 โ Line height too tight for body text
โ Wrong โ default browser line-height of ~1.2 is too tight for long-form reading:
p { font-size: 1rem; } /* line-height: normal ~= 1.2 โ uncomfortable to read */
โ Correct โ set an explicit comfortable line-height on body text:
body { font-size: 1rem; line-height: 1.7; }
Mistake 2 โ No max-width on text content
โ Wrong โ long lines on wide screens are exhausting to read:
.article-body { padding: 40px; } /* text can stretch to full viewport width */
โ Correct โ constrain line length with ch units:
.article-body { max-width: 65ch; margin-inline: auto; padding: 40px 24px; }
Mistake 3 โ Adding letter-spacing to body text
โ Wrong โ positive letter-spacing on body text slows reading speed:
p { letter-spacing: 0.05em; } /* text looks "airy" โ readers decode each letter */
โ Correct โ leave body text at default tracking; only adjust for headings and labels:
h1 { letter-spacing: -0.02em; } /* tighter for large headings */
.label { letter-spacing: 0.08em; } /* wider for small uppercase labels */
Quick Reference
| Property | Recommended | Notes |
|---|---|---|
line-height (body) |
1.6โ1.75 (unitless) | Never use px or em values โ they break inheritance |
line-height (heading) |
1.1โ1.3 | Tighter for larger text |
letter-spacing (heading) |
-0.01em to -0.04em | Tighter tracking at large sizes |
letter-spacing (uppercase) |
0.05em to 0.1em | Wider tracking improves all-caps readability |
max-width (measure) |
55ch to 75ch | ch unit auto-adjusts to font changes |
text-indent |
1โ2em | For print-style paragraph indentation |