Type something to search...
What is vertical rhythm in web typography?

What is vertical rhythm in web typography?

Vertical rhythm is the consistent, predictable spacing that runs from the top of a page to the bottom: the gaps between lines, paragraphs, headings, images and sections all relate to one shared unit instead of being chosen at random. In web typography that unit is usually the line height of your body text, for example 1.5rem, and every margin and padding is a multiple or simple fraction of it. When rhythm is in place, the page feels calm and organised even if the reader never notices why. When it's missing, spacing looks slightly off in dozens of small places, and the page feels busy. You build it in CSS with a base line height, a small set of spacing values derived from it, and a little discipline.

Most readers can't name vertical rhythm, but they feel its absence immediately: headings that float nowhere near the text they introduce, images with awkward gaps, and lists that seem to belong to a different document. Consistent rhythm makes long pages easier to scan and makes a design system easier to maintain, because spacing stops being a per-component decision. In this article you'll learn where the idea comes from, how to choose a base unit, how to space headings, paragraphs, lists and media, how the lh and rlh units help, and how to check your rhythm in the browser.

Where Vertical Rhythm Comes From

In print, type was set on a page with a fixed leading, so every line of body text sat a precise distance below the one before it. Book designers extended this into a grid: headings, captions and images were sized and positioned so that body text on the left and right pages lined up, and text on the front of a sheet lined up with text on the back. Robert Bringhurst summarised the idea in The Elements of Typographic Style by comparing space in a page to time in music, and advising designers to add and remove space in measured intervals.

The web doesn't work in the same way. Content reflows, users zoom, fonts load late, and images arrive at sizes you don't control. A pixel-perfect baseline grid is possible but fragile, which is why there's a separate discussion of baseline grids on the web. Vertical rhythm is the more practical, forgiving version: you don't insist that every baseline lands on a grid line, but you make sure every vertical space comes from the same family of values.

Choose a Base Unit

Everything starts with the body text. Pick a body font size and a line height that reads comfortably, then treat the resulting line height as your rhythm unit.

A common starting point:

  • Body font size: 1rem, which is 16px with default browser settings.
  • Body line height: 1.5, giving a 24px line box.
  • Rhythm unit: 1.5rem, the same 24px.

You can capture this in custom properties so every component reads from the same source:

:root {
  --font-size-base: 1rem;
  --line-height-base: 1.5;
  --rhythm: calc(var(--font-size-base) * var(--line-height-base)); /* 1.5rem */

  /* Spacing scale built from the rhythm unit */
  --space-quarter: calc(var(--rhythm) * 0.25); /* 0.375rem */
  --space-half: calc(var(--rhythm) * 0.5);     /* 0.75rem */
  --space-1: var(--rhythm);                     /* 1.5rem */
  --space-2: calc(var(--rhythm) * 2);           /* 3rem */
  --space-3: calc(var(--rhythm) * 3);           /* 4.5rem */
}

body {
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

Using rem matters here. If a reader increases their default font size in browser settings, the whole rhythm scales with it, and the proportions between text and space stay intact.

Why Not Pixels?

Pixel values look precise, but they break the relationship between text and space as soon as the reader changes their default font size. A 24px margin around 20px text no longer equals one line. Keeping both the font size and the spacing in rem, or deriving spacing from the line height with lh and rlh units, preserves the rhythm for everyone.

Space Paragraphs With the Rhythm Unit

The simplest rule that gives you a lot of rhythm for free: put one rhythm unit of space between paragraphs, and only on one side.

p,
ul,
ol,
blockquote,
pre,
figure {
  margin-block: 0 var(--space-1);
}

Applying margin only to the bottom (margin-block-end) avoids surprises from margin collapsing and makes spacing easy to reason about: each block owns the space after it. Some teams prefer the "flow" or "stack" pattern instead, where the space goes above every element except the first:

.flow > * + * {
  margin-block-start: var(--flow-space, var(--space-1));
}

This lobotomised owl selector (* + *) selects every child that follows a sibling, so the first element in a container never gets a stray top margin. You can override the gap for a specific element by setting --flow-space on it.

Either approach works. What matters is that you pick one and use it everywhere. For more on choosing the size of the gap itself, see how to choose the right paragraph spacing.

Space Headings So They Belong to Their Content

Headings break the rhythm if you treat them like paragraphs. A heading should sit closer to the text it introduces than to the text before it, so the reader groups it correctly. That means more space above and less below.

h1, h2, h3, h4 {
  line-height: 1.2;
  margin-block: 0;
}

.flow > h2 {
  --flow-space: var(--space-2);
}

.flow > h3 {
  --flow-space: calc(var(--space-1) * 1.5);
}

.flow > h2 + *,
.flow > h3 + * {
  --flow-space: var(--space-half);
}

Here an h2 gets two rhythm units above it, and whatever follows it gets only half a unit. The heading visually attaches to its section.

Make Heading Line Heights Fit the Rhythm

Large headings with the body line height of 1.5 look loose, so headings usually get a tighter line height. That can knock them out of rhythm if you care about exact multiples. One way to keep both is to size the heading's line height in rhythm units rather than as a ratio:

h2 {
  font-size: 2rem;
  line-height: calc(var(--rhythm) * 1.5); /* 2.25rem, tighter than 1.5 x 2rem */
}

h1 {
  font-size: 2.75rem;
  line-height: calc(var(--rhythm) * 2);   /* 3rem */
}

Now each line of an h2 takes exactly one and a half rhythm units, and each line of an h1 takes two. If a heading wraps onto two lines, it still uses a whole or half multiple of the unit. This is the closest most sites need to come to a strict grid.

Use the lh and rlh Units

CSS now has units that are made for rhythm:

  • lh: The computed line height of the current element.
  • rlh: The computed line height of the root element, usually your body line height if you set it on html.

Both are supported in current versions of Chrome, Edge, Firefox and Safari. With them, you don't need a custom property to express "one line of space":

html {
  font-size: 100%;
  line-height: 1.5;
}

p,
ul,
ol {
  margin-block: 0 1rlh;
}

h2 {
  margin-block: 2rlh 0.5rlh;
}

.callout {
  padding: 1rlh;
}

Because rlh always refers to the root line height, a margin of 1rlh on a small caption is the same size as on a paragraph. That's what you want for rhythm. lh, by contrast, is local: 1lh inside an h1 is the h1's line height, which is useful for things like icon sizes that should match a line of the text they sit beside.

If you need to support older browsers, give a rem fallback first:

p {
  margin-block-end: 1.5rem;
  margin-block-end: 1rlh;
}

Browsers that don't understand rlh ignore the second declaration.

Keep Lists, Images and Other Blocks in Rhythm

Text is the easy part. The awkward elements are the ones whose height you don't control.

Lists

Treat a list as one block with the normal gap after it. Items inside it usually need less space than paragraphs, or none, so the list reads as a unit:

li + li {
  margin-block-start: var(--space-quarter);
}

Images and Embeds

An image that is 317px tall pushes everything below it off any grid. You can't make arbitrary images a multiple of 24px without cropping, but you can make their containers consistent:

figure {
  margin-block: var(--space-1) var(--space-2);
}

figure img {
  display: block;
  width: 100%;
  height: auto;
}

figcaption {
  margin-block-start: var(--space-half);
  font-size: 0.875rem;
  line-height: var(--space-1);
}

Using display: block removes the small gap below inline images caused by the text descender space. If you control the content, choosing fixed aspect ratios with aspect-ratio and heights that are a multiple of the rhythm unit gives you a tighter result, but most sites accept that media breaks the strict grid and simply keep the space around it consistent.

Borders and Padding

A 1px border adds 1px of height. On a strict baseline grid that matters, but for general vertical rhythm it rarely does. If you want to be exact, use outline or an inset box-shadow for decoration, since neither affects layout.

Check Your Rhythm in the Browser

You can overlay a guide that shows your rhythm unit with a repeating gradient. Add a class to the body while you're working:

.debug-rhythm {
  background-image: linear-gradient(
    to bottom,
    rgb(0 120 255 / 0.15) 1px,
    transparent 1px
  );
  background-size: 100% 1.5rem;
}

Each faint line marks a multiple of 1.5rem from the top of the page. Paragraph text should sit neatly between lines, and gaps should look like whole or half units. You'll quickly spot elements using a stray margin: 20px from an old stylesheet.

A short script can also list every distinct margin in use, which is a good way to audit a page:

const values = new Map();

for (const el of document.querySelectorAll("main *")) {
  const style = getComputedStyle(el);
  for (const prop of ["marginTop", "marginBottom"]) {
    const value = style[prop];
    if (value !== "0px") {
      values.set(value, (values.get(value) ?? 0) + 1);
    }
  }
}

console.table(
  [...values].sort((a, b) => b[1] - a[1]).map(([value, count]) => ({ value, count }))
);

Run it in the browser console. A page with good rhythm shows a handful of values, all multiples of your unit:

┌─────────┬─────────┬───────┐
│ (index) │ value   │ count │
├─────────┼─────────┼───────┤
│ 0       │ '24px'  │ 41    │
│ 1       │ '12px'  │ 17    │
│ 2       │ '48px'  │ 9     │
│ 3       │ '6px'   │ 6     │
└─────────┴─────────┴───────┘

If the table shows 13px, 18px, 22px and 30px, you've found where the rhythm breaks.

Common Mistakes

  • Spacing in mixed units: Mixing px, em and rem margins means some gaps scale with the text and others don't.
  • Margins on both sides: Top and bottom margins on every element collapse in ways that are hard to predict, especially inside flex and grid containers, where margins don't collapse at all.
  • Using em for margins on headings: margin-top: 2em on a 2rem heading is 4rem, not two rhythm units. Use rem or rlh for spacing that should be consistent across elements.
  • Too many spacing values: A scale with fifteen steps is no scale. Four to six values cover most layouts.
  • Chasing perfection: Users zoom, fonts fall back and content changes. Aim for consistency, not a pixel-perfect grid.

FAQ: Vertical Rhythm

Not quite. A baseline grid is a strict form of vertical rhythm where every line of text sits on a fixed set of horizontal lines. Vertical rhythm is the broader idea that all vertical spacing comes from one consistent unit, even if baselines don't line up exactly.

Use the line height of your body text. With a 16px body size and a line height of 1.5, the unit is 24px, or 1.5rem. Every margin and padding then becomes a multiple or simple fraction of that value.

Both work. The rlh unit expresses the root line height directly and is supported in current major browsers. If you need older browser support, declare a rem value first and the rlh value second as a progressive enhancement.

No. Large headings look better with a tighter line height, around 1.1 to 1.3. To keep them in rhythm, you can set their line height to a multiple of the rhythm unit, such as 1.5 or 2 units, rather than a plain ratio.

You usually can't without cropping, because image heights vary. Keep the space above and below each figure consistent, use display block on images to remove the inline gap, and accept that media will break a strict grid.

Yes. Small screens show less content at once, so consistent spacing helps readers see where sections begin and end. You may use a slightly smaller rhythm unit on narrow screens, but keep the same proportions.


Conclusion

Vertical rhythm is the habit of deriving every vertical space on a page from one unit, normally the line height of your body text. Set that unit once, build a small spacing scale from it, and use it for paragraph gaps, heading margins, list spacing and the space around figures. Modern CSS makes this easier than it used to be: rem keeps spacing tied to the reader's font size, and the lh and rlh units let you express "one line" directly.

Don't aim for a perfect grid. Aim for a page where every gap clearly belongs to the same system, headings sit close to the content they introduce, and a quick audit shows only a handful of spacing values. That consistency is what makes a long page feel easy to read.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper