Contact me

Mastering CSS: Vertical Rhythm

Mastering CSS: Vertical Rhythm Featured Image

Vertical rhythm is an important concept in web design and development. It makes the page and typography feel consistent and visually pleasant. Maintaining the rhythm accross the site, especially on larger collaborative projects, can be quite a challenge. With SASS, LESS and now the CSS variables, configuring and maintaining the vertical rhythm has never been easier. In this article, we are going to cover the very basics of vertical rhythm and how to implement it using CSS variables and CSS calc function.

Vertical rhythm basics

Vertical rhythm is usually used on following CSS properties:

  • Typography - line-height
  • Spacing - padding (top and bottom), margin (top and bottom)
  • Offsets - top, bottom
  • Size - height, min-height, max-height

Notice how we don't use vertical rhythm for font-size. Modular scale is usually used for font sizes in typography. Vertical rhythm is only used for spacing and vertical size.

First off, we need to determine the rhythm unit. This will act as a base for multiplication for calculating vertical ryhthm. Since vertical rhythm is tied with repetition, and the most repetitive spacing on any website is a line-height of body element, the base line-height of a page. We'll use this base line-height as rhythm-unit.

html {
    /* Set parent font size */
    font-size: 18px;
}

body {
    /* Set base line-height */
    line-height: 1.778rem;
}

Note that we are using px value for easier calculation, it's recommended to use relative values wherever you can. From this configuration, we can calculate rhythm unit value: Rhythm unit = 18px * 1.778rem = 32px. Alternatively, we can check the line-height of the body element in element inspector.

Vertical rhythm example with 32px rhythm unit from this article (very meta!)

On the image, we can see the baseline grid of 32px (1 rhythm unit) and how text fits nicely into it. Notice how the heading's line-height is exactly 2 times the base rhythm unit. That means that heading's line height has rhythm unit multiplier of 2 (with bottom margin of 1 rhythm unit).

Rhythm unit multipliers

Of course, we can't have only one spacing value. This is where rhythm unit multipliers come in.

html {
    /* Set parent font size */
    font-size: 18px;
}

body {
    /* rhythmUnit = 32px */
    line-height: 1.778rem;
}

.spacing__vertical--1 {
    /* 1x rhythmUnit = 32px */
    padding-bottom: 1.778rem;
}

.spacing__vertical--2 {
    /* 2x rhythmUnit = 64px */
    padding-bottom: 3.556rem;
}

.spacing__vertical--3 {
    /* 3x rhythmUnit = 96px */
    padding-bottom: 5.334rem;
}

We have our basic classes for spacing which are calculated by multiplying the rhythm unit by 2, 3, 4, etc. But these classes are not flexible enough and they won't cover all of the use-cases for vertical rhythm. That's why we will create CSS variables (also can be done in SASS and LESS) to store and dynamically calculate the vertical rhythm values.

:root {
    /* Base values */
    --typography__fontSize: 18px;
    --spacing__rhythmUnit: 1.778rem; /* 1.778rem * 18px = 32px */

    /* Calculations */
    --spacing__vertical--1: var(--spacing__rhythmUnit);
    --spacing__vertical--2: calc(2 * var(--spacing__rhythmUnit));
    --spacing__vertical--3: calc(3 * var(--spacing__rhythmUnit));
}

html {
    font-size: var(--typography__fontSize);
}

body {
    line-height: var(--spacing__vertical--1);
}

.spacing--default {
    padding-bottom: var(--spacing__vertical--1);
}

.button--default {
    height: var(--spacing__vertical--2);
}

.heading--primary {
    line-height: var(--spacing__vertical--3);
}

We have improved upon the previous example and created reusable variables that we can use for all mentioned use-cases (typography, spacing, offset, height, etc.). By using these variables, we can ensure the consistent vertical rhythm of all elements on the website. But we need to keep in mind when we add some CSS rules (like borders) that push the content down and don't have to adhere to rules of vertical rhythm.

Compensating for borders

When we add vertical (top or bottom) borders to an element, they add to the overall height of the element and push the content down. This also affects our vertical rhythm (if border width is not in vertical rhythm value) and we have to compensate for adding vertical borders by either reducing the rhythm value of vertical padding, margin or line-height(if element has no padding or margin).

:root {
    /* Base values */
    --typography__fontSize: 18px;
    --spacing__rhythmUnit: 1.778rem; /* 1.778rem * 18px = 32px */

    --border__width--default: 0.111rem; /* 2px */

    /* Calculations */
    --spacing__vertical--1: var(--spacing__rhythmUnit);
    --spacing__vertical--2: calc(2 * var(--spacing__rhythmUnit));
    --spacing__vertical--3: calc(3 * var(--spacing__rhythmUnit));
}

html {
    font-size: var(--typography__fontSize);
}

body {
    line-height: var(--spacing__vertical--1);
}

.heading--primary {
    border-bottom: var(--border__width--default) solid #aaa; /* 2px */
    padding-bottom: calc(
        var(--spacing__vertical--1) - var(--border__width--default)
    ); /* 32px - 2px */
    line-height: var(--spacing__vertical--3); /* 96px */
}

Taking a look at the .heading--primary class, we can see that we have set a bottom border, padding and line-height. Since we have padding, we are going to compensate for added bottom border by reducing the width of the border from bottom padding.

If we assume that the heading takes one line of text, have the following calculation of the element's height: 2px + (32px - 2px) + 96px = 128px which is equal to 4 times the vertical unit.