UI/UX Atlas
Typography Intermediate

Line Height (Leading) & Vertical Rhythm

Mastering line height and vertical rhythm transforms walls of text into breathing, readable prose — and makes entire page layouts feel deliberate rather than accidental.

7 min read

The full lesson

Every typographic decision lives on two axes. The horizontal axis covers letter spacing and line length. The vertical axis covers how lines stack on top of each other. Line height — called leading in print (named after the strips of lead typesetters once wedged between lines) — is the most powerful lever on that vertical axis.

Get it wrong and even a beautiful typeface feels cramped or eerily floaty. Get it right and readers glide through paragraphs without noticing the mechanics at all.

Vertical rhythm extends this idea beyond a single text block to the whole page. When spacing, type sizes, and layout heights all share a common grid unit, the eye traces invisible horizontal rails through the composition. The result feels engineered rather than assembled.

What Line Height Actually Controls

The CSS line-height property sets the height of each line box — the invisible rectangle that wraps one line of text. The extra space between the line box height and the actual glyphs is split equally above and below the characters. This is called half-leading. The visible gap between two lines of text is always the sum of the half-leading below one line and the half-leading above the next.

A unitless value (e.g. line-height: 1.5) multiplies the ratio against the element’s current font size. It inherits as a ratio, not a fixed pixel value. This is almost always what you want.

/* Unitless ratio — preferred */
p {
  font-size: 1rem;
  line-height: 1.5; /* = 24px at 16px base */
}

/* Pixel value — avoid for body text; doesn't scale with font size changes */
p {
  line-height: 24px;
}

/* Em value — computes correctly but a common trap in inheritance */
p {
  line-height: 1.5em; /* inherits computed px, not the ratio */
}

Choosing the Right Value

There is no single “correct” line height. The right value depends on three things: font size, line length (measure), and typeface characteristics (x-height, tracking, and weight).

Body text

WCAG 2.2 Success Criterion 1.4.12 (Text Spacing) requires that content does not lose function when line height is set to at least 1.5 times the font size. Treat 1.5 as a reasonable floor, not a magic number. In practice, body text typically lands between 1.4 and 1.8 depending on the typeface:

ContextTypical rangeNotes
Long-form article body1.55 – 1.75Wider measure tolerates higher leading
UI body copy (cards, tooltips)1.4 – 1.55Shorter lines, tighter reading context
Code blocks1.5 – 1.7Monospace fonts often have lower x-height
Mobile body text1.5 – 1.65Shorter line lengths read well with mid-range leading

Headings

Headings are set larger and read more quickly. The eye takes them in almost as a single unit rather than tracking word by word. Tight leading actually looks better at display sizes:

  • h1 (display / hero): line-height: 1.05 – 1.15
  • h2 (section title): line-height: 1.15 – 1.25
  • h3 (subsection): line-height: 1.25 – 1.35

A common mistake is leaving the browser default (line-height: normal, which resolves to roughly 1.2) on all heading levels. For small headings that wrap to two or three lines, 1.2 is too tight. For large single-line display text, it can be appropriate.

The measure connection

Leading and line length are coupled. A narrow column (45–60 characters) with leading of 1.8 feels like a double-spaced document. A very wide measure (90+ characters) with leading of 1.4 is fatiguing because the eye struggles to find the start of the next line. As measure increases, leading should increase proportionally — roughly +0.05 for every 10-character increase beyond 60ch.

Vertical Rhythm: From Lines to Page

Vertical rhythm means aligning vertical spacing across your entire layout to a baseline grid or a consistent spacing unit. When done well, elements feel like they belong to the same system even when they are far apart.

The classical approach — snapping every element’s baseline to a 4px, 8px, or custom grid — works well in print. On the web it is hard to enforce perfectly. Images, varied font metrics, and dynamic content all break strict baseline alignment. The modern, pragmatic approach focuses on a consistent spacing scale instead of pixel-perfect snapping.

Defining a spacing unit

Pick a base unit that harmonizes with your base line height:

:root {
  --font-size-base: 1rem;      /* 16px */
  --line-height-base: 1.5;     /* 24px computed */
  --space-unit: 0.25rem;       /* 4px — quarter of the line height */
}

With a 24px line height, multiples of 4px (4, 8, 12, 16, 20, 24, 32, 40, 48…) keep vertical spacing visually related to text rhythm. This is why most design systems — Material Design, Base Web, Radix — use a 4px base space unit.

Paragraph and section spacing

Spacing between paragraphs should signal a new thought without breaking the sense of continuous flow. A reliable rule of thumb: margin-block-end on paragraphs should equal roughly one full line height (i.e. 1em in relative terms, since the em of the element equals its font size).

p {
  margin-block-end: 1em;   /* exactly one line worth of space */
}

h2 {
  margin-block-start: 2em;  /* two lines above a major section */
  margin-block-end: 0.5em;  /* half a line between heading and following text */
}

The key insight: heading margins should connect the heading to the content that follows it, not to the content above it. margin-block-start should be larger. margin-block-end should be smaller. The heading belongs to the section it introduces.

CSS Techniques and Modern Implementation

Fluid line height with clamp()

Just as fluid type sizes use clamp(), fluid line height lets you taper leading as text scales up:

h1 {
  font-size: clamp(2rem, 5vw, 4rem);
  /* Tighter leading at large sizes, slightly looser at small */
  line-height: clamp(1.05, 1.1 + 0.2vw, 1.2);
}

This prevents an awkward situation: a heading that looks perfect at 64px on desktop but feels uncomfortably loose at 32px on mobile when the same unitless ratio is applied to both.

Design tokens for spacing

Under the W3C DTCG token format, line height belongs in a dedicated token group. A three-tier structure keeps it maintainable:

{
  "lineHeight": {
    "tight": { "$value": 1.1, "$type": "number" },
    "snug": { "$value": 1.25, "$type": "number" },
    "normal": { "$value": 1.5, "$type": "number" },
    "relaxed": { "$value": 1.625, "$type": "number" },
    "loose": { "$value": 1.75, "$type": "number" }
  }
}

Semantic tokens then reference those primitives:

{
  "text-body": { "$value": "{lineHeight.normal}", "$type": "number" },
  "text-heading-display": { "$value": "{lineHeight.tight}", "$type": "number" }
}

This approach — primitive tokens feeding semantic tokens — means you can change the base scale globally without hunting for hardcoded values across stylesheets.

Variable fonts

Variable fonts with a wdth (width) or custom axes can subtly affect the visual impression of spacing. At a narrow width, compressed glyphs may need slightly tighter leading. At wide widths, they may benefit from slightly more. If your design system supports variable fonts, consider linking line-height adjustments to font-variation-settings axis values via CSS custom properties.

WCAG Requirements and Accessibility

WCAG 2.2 SC 1.4.12 (Text Spacing) is the primary accessibility reference point. It requires that no content or functionality is lost when all of the following are applied at the same time:

  • Line height set to at least 1.5 times the font size
  • Spacing after paragraphs set to at least 2 times the font size
  • Letter spacing set to at least 0.12 times the font size
  • Word spacing set to at least 0.16 times the font size

This is a minimum non-breakage criterion — not a design recommendation. Meeting it means your layout does not collapse when users apply custom stylesheets. It does not mean 1.5 is the optimal value. For many typefaces and contexts, 1.6 or 1.7 is the better design choice.

Users with dyslexia, low vision, or cognitive disabilities commonly override browser defaults with custom stylesheets that increase line height. Build layouts that accommodate this gracefully: avoid overflow: hidden on text containers and use min-height rather than height.

Common Mistakes and How to Fix Them

Do

Use unitless line-height values (e.g. 1.5) on all text elements so the ratio is inherited correctly at every font size. Set heading line heights explicitly and scale them tighter as size increases. Build a spacing scale from a unit tied to your base line height (multiples of 4px or 8px). Use margin-block-start larger than margin-block-end on headings to connect them to the content they introduce.

Don't

Don’t use em or px values for line-height on containers with nested text at different sizes — children inherit the computed value, not the ratio. Don’t rely on the browser default (normal, ~1.2) for body text — it’s too tight for comfortable reading. Don’t apply the same line-height to body text and large display headings. Don’t ignore WCAG 1.4.12 by using overflow: hidden on text containers with fixed heights.

Outdated habits still circulating

Fixed px line heights remain common in older codebases. They break as soon as a user changes their browser font size or a heading wraps to a second line. Replace them with unitless ratios.

Copy-pasting line-height: 1.5 everywhere is better than nothing, but it ignores the size-leading relationship. Large headings with 1.5 leading look like a double-spaced draft.

Pixel-perfect baseline grids in web UI — borrowed from print design — require constant hacks the moment images, icons, or user-generated content appears. Use a consistent spacing scale instead. It delivers 80% of the visual benefit at 5% of the maintenance cost.

Practical Checklist

Before shipping any text-heavy screen, verify:

  1. Body text line height is 1.4 or greater (aim for 1.5–1.65 for article-length content).
  2. All heading levels have explicit, progressively tighter line-height values.
  3. Paragraph margin-block-end is approximately 1em (one line worth of space).
  4. Section headings have more space above them than below.
  5. No text container uses overflow: hidden with a fixed height — use min-height.
  6. Spacing values are multiples of your base spacing unit (4px or 8px).
  7. Layout survives the WCAG 1.4.12 Text Spacing test without content loss.