Baseline Grids in CSS

Chasing vertical rhythm - bringing a print design fundamental to the web.

David Martin
October 22nd, 2020

Grids, as tools to organize elements in space, are fundamental to the practice of design. Alongside typography and color selection, they're one of the first things I think about when approaching a new design project.

Grids provide numerous benefits. They simplify the designer's job by providing a framework to work within, and they make the user's life easier by making the design easier to comprehend and interact with. They bring a sense of coherence and regularity to a design, that encourages trust and enhances the perception of the brand behind the design.

On the web, we're all too familiar with these concepts as applied to horizontal space. From the dark days of HTML table layouts, to early Bootstrap float-based grid systems, to new layout modules like flexbox and CSS grid, designers of the web have for decades been grappling with how to best manage the space along the x-axis.

But today I'd like to talk about the vertical axis!

Vertical Rhythm and the Baseline

Vertical Rhythm is the harmony (or lack thereof) established by the relationship between elements along the y-axis. Similarly to how you might have a three-column layout where three paragraphs take up equal portions of the horizontal space, you might want three stacked images to each take up an equal portion of the available vertical space. CSS grid gives us some exciting possibilities here, but is not intended to solve the problem of vertical rhythm with respect to typography.

Vertical rhythm is established typographically between the position of different lines of text, and between different typographic elements.

Vertical rhythm is established typographically between the position of different lines of text, and between different typographic elements (a header and a paragraph, for example). For the former concern, CSS gives us the line-height property; for the latter - we have margin and padding. However, there's an issue with line-height: it doesn't track with the baseline of our text. The baseline is the imaginary line on which the body of the letters sits. This is what our eyes are trained to follow when reading, but line-height encompasses the entirety of the space around a line of text.

So we have a disconnect between line-height and textual baseline. Our second difficulty is that the actual computed height of a line of text can vary between fonts (you might assume that two different fonts with the same font-size and the same line-height would take up the same amount of space on a page, but you'd be wrong). Finally, even if individual font-metrics were consistent, the calculations different browsers make to render them are not.

But, do not despair, aspiring typographer! With an understanding of how these different properties interact, there is a solution to be obtained. Unfortunately, it involves a good deal of manual manipulation of text and experimentation. Fortunately, we can establish a system that carries our baseline-based vertical rhythm throughout our design.

Solution: Abstract

1. Establish a baseline.

I do this by:

  1. Taking my base font-size (applied to body copy),
  2. Multiplying it by the default line-height (1.7).
  3. Dividing this in half to give a bit more flexibility (e.g. for setting height for small icons).

2. Shift text vertically until it rests on the baseline.

  1. Determine how many "baselines" a given type size should fill.
  2. Determine necessary offset.
  3. Set padding-top to shift text down to the baseline.
  4. Set margin-bottom to maintain consistent spacing between typographic elements.

Notes:

  1. You could also use margin-top and padding-bottom, but you should use a mix of margin and padding to avoid some weirdness when using only one or the other.
  2. Having a visual tool is pretty much a necessity here. I like baseliner.

Repeat (2) for each degree of the typographic scale.

Particularly if you're using multiple fonts, this process will need to be repeated for each distinct font-size in your design. As an aside - you really should be defining a global typographic scale. I particularly like the modular scale approach.

Solution: Technical

Following the above process gets you a baseline grid, but likely leaves you with a lot of "dirty" CSS declarations, and leaves you wanting something more elegant.

An elegant solution, I propose, has these properties:

  1. Lets you set relevant font properties in a simple, but coherent way (font-size, line-height, margin, padding).
  2. Is responsive, and allows for a changing font-size based on screen size (text is usually smaller on mobile, since the screens are smaller and held closer to our eyes).
  3. Minimizes repetition -- you don't want to have to re-declare all of the relevant properties on every single block of text in your design.

My solution is to use a simple mixin to set the relevant properties in a single line of code (the example here uses the postcss-mixins syntax, but this could easily be applied to SCSS, LESS, etc).

Here's the mixin:

	@define-mixin baseline $typeDegree, $lineHeightMultiple: 2, $offsetPalm: 1rem, $offsetDesk: 1rem {
		font-size: fontSize($typeDegree, 1);
		line-height: compare($baseline1, $lineHeightMultiple, *, rem);
		margin: 0 0 compare(compare($baseline1, 2, *, rem), $offsetPalm, -, rem);
		padding-top: $offsetPalm;

		@media screen and getBreakpoint(tabStart) {
			font-size: fontSize($typeDegree, 2);
			line-height: compare($baseline2, $lineHeightMultiple, *, rem);
			margin: 0 0 compare(compare($baseline2, 2, *, rem), $offsetDesk, -, rem);
			padding-top: $offsetDesk;
		}
	}

And here's how I'd call it:

@mixin baseline 0, 2, 0.9rem, 1.1rem;

This is the mixin call for my base font-size -- paragraph elements, body text, default anchor tags, etc. Remember the argument order is (typographic scale degree, line height multiple, offset for small screens, offset for big screens) -- so in this case I want the first degree of the typographic scale (0), lines of text to take up 2 "baselines" (remember we divided our proto-baseline in half in step 1), and for there for be a 0.9rem offset on small screens and a 1.1rem offset on big screens.

That's it. Now if I'm careful to honor the baseline grid for vertical spacing of other design elements, my typography will stay true to it as well.

Notes:

  1. This approach works because I'm careful to set my typographic styles globally. By establishing global typographic styles, we can ensure that they cascade down to any typographic elements throughout the site.
  2. The "compare" function in the above snippet just does some multiplication in this case and returns a value in rems. I could use the native CSS calc() function instead, but it's better to let the pre-compiler handle math where possible.

This sounds hard, why should I do it?

Honestly, maybe you shouldn't. There's a good argument to be made that the concept of a typographic baseline is best left in the print world - that we should instead embrace line-height and it's ramifications, and instead seek vertical rhythm in the interplay between larger design elements. You can absolutely make beautiful, coherent, engaging designs without going through all of this.

But, there's just something so damn pleasing about seeing lines of text line up between different columns. It's like ASMR for your lorem ipsum. If you're going for a certain aesthetic, or working with a heavily typographically-driven design, then I suggest it's worth considering this sort of approach and chasing the holy-grail of baselined text.

Link-stash: Further Reading

  1. An impeccable walkthrough from Smashing Magazine.
  2. A deep dive into the technical side of web fonts and font metrics.
  3. A case study from the NY Times, and how they achieved a baseline grid for a particular article.
  4. A solid basic overview from 24ways.

How about a share, friend?