Web typography: baseline grids made easy (finally!)
A baseline grid is a tool we can use to help (hopefully) make things easier and nicer when typesetting and designing layouts. They help align text or other elements across columns and pages, and aid in creating a harmonious vertical rhythm. This effectively means the spacing in the vertical plane of the design feels nicer because it has been carefully considered and is built from intrinsic relative measurements core to the layout (as opposed to arbitrary values).
The grid consists of equidistant horizontal lines down an entire design. Typically they are used in complex print design projects where they have the biggest impact, but they can be used on any project to good effect.
There has been many quests over the last 15+ years on cracking using them effectively on the web, and I think we’re finally there! (See these posts from 2007, 2011, 2012, etc).
10 years ago I worked on a side project called Sassline which would let you set text to a baseline grid on the web with Sass and Rems.
Note: Sass is a CSS pre-processor that lets you do more complex calculations which will compile into code you can use to style your websites. Rems are a relative unit which lets you set a base unit size and work proportionally from there rather than with fixed values.
At the time I’d say it was a pretty interesting approach to front-end development for web typography with the key focus on working proportionally from the baseline as a measurement for the rest of your design (which to this day I still think is a good idea — to quote my very smart colleague Tim Brown “design from the type outward”). Over the years I let maintaining the codebase slip however and it became somewhat out-of-date. The project was doing a bit too much of some things and not enough of others. The main issue being it was pretty dang complex all in all—especially when generally the point of using a grid as a tool is to make your work easier not harder.
Recently though, a note in a post from Jen Simmons caught my eye about some of the features introduced in Safari 17.2. There’s now support for a new unit which is exactly what makes setting text to a baseline in browsers possible — Cap height!
The measurement of 1cap
is equal to the cap-height of the first available font.
Turns out this is supported across all the major browsers too!
I believe it was Razvan Onofrei’s post which originally outlined a formula you can use to shift all your text, no matter the size or font, to sit on a baseline grid. Now at the time this was a huge pain to work with as you would have to manually figure out the Cap height of each font you used and input that as a decimal sizing relative to the em
sizing of the font. Hence needing to turn to things like Sass to be able to handle the math in the background while you write your code. The addition of using rems wasn’t necessary but became a helpful way to manage the baseline sizing and work relatively from it.
Now before I lose you entirely, all that complexity has now gone away. In two lines of code we can do the exact same thing 😲
Razvan’s formula was as follows:
font-size * (line-height - cap-height) / 2
Now the new Cap unit actually makes this even more simple as that is already taking into account the current font size so we can cut that out (as long as we use a united measurement for the line-height value). If we follow on the method I used for Sassline to set the baseline using Rems we can use this pure CSS to push all our text down proportionally to the baseline and reset to the next line beneath. We use some nested calc here to be able to do the math to get (line-height - cap-height) / 2.
padding-top: calc(calc(2rem - 1cap) / 2);
margin-bottom: calc(2rem - calc(calc(2rem - 1cap) / 2));
Here we also need to set the line-height of the element to 2rem to make it work, but seeing as we’d be setting that already I’ll keep my claim we can do this in two lines of code.
Here’s a full demo of that in action. You can toggle the grid on and off and switch out the font palettes to see it working with local fonts and web fonts alike, all with the same code and no additional values needed to be entered.
See the Pen CSS only baseline text by Jake (@jakegiltsoff) on CodePen.
You’ll notice if you dig into the code in that CodePen example I started getting carried away making it more complex. Always a hard balance in trying to make the code as easy to write and maintain vs adding more difficulty in comprehension. Nonetheless I think there is an additional step we can take to make it easier to work with by using CSS custom properties to save us re-writing a bunch of stuff.
--baselines: 3;
--beneath: 1;
--baseline-shift: calc(calc(calc(var(--baselines) * 1rem) - 1cap) / 2);
--baseline-push: calc(calc(var(--beneath) * 1rem) - var(--baseline-shift));
line-height: calc(var(--baselines) * 1rem);
padding-top: var(--baseline-shift);
margin-bottom: var(--baseline-push);
By doing this we can think in terms of the baseline to build our design — all you need to do is set the base font-size to give us the baseline size we need.
Depending on your comfort writing front-end code this may seem wildly complicated or overly simple (and to be honest it’s kinda both 😅). The rabbit hole goes much deeper and I’m going to dive in to see how well I can wrangle this to take into account different approaches to responsive typography.
My first quest will be seeing how well this works with a fluid baseline (i.e. molten leading). I’m also really curious how to best use container queries within more complex web typography as I’m not sure I’ve seen many of the benefits we were hoping they would introduce in action yet. Needless to say I’m excited about web typography again for the first time in a number of years.