For a redesign, a design agency stepped in and presented a design system where they tweaked the line height for almost all elements.
To make it somewhat concrete:
| element | font size | line height | ratio |
|---|---|---|---|
| large display | 52px | 60px | 1.1538 |
| … | … | … | … |
| h1 | 36px | 44px | 1.2222 |
| h2 | 32px | 40px | 1.2500 |
| … | … | … | … |
| summary | 20px | 28px | 1.4000 |
| body | 18px | 28px | 1.5556 |
| caption | 12px | 19px | 1.5000 |
| … | … | … | … |
The line height ratios for elements that are meant as headings are roughly around 1.25, and the rest of the textual elements revolve around 1.5.
To keep things simple, and to align with the already existing system, we agreed to stick to these two ratios instead of setting a unique ratio for each element.
However, during the discussion, it became clear why the designer opted for these line heights. The designer wanted to always have even numbers for line heights. I can deeply empathize with such a sentiment.
They disliked the fact that, for example, a font size of 18px and a relative line height ratio of 1.5 results in 27px. They would sleep better if it were 28px.
(It’s an entirely different discussion whether these values were ultimately implemented with fixed pixels or using rem units.)
The solution
I haven’t used much the round() function from CSS, however in the past, I’ve come across many interesting use cases and ideas for it.
So the solution was to take advantage of it and to always bump the line height up to the next even number if the calculation resulted in an odd value.
This way, the general line height was based on 1.5, and the exceptions were handled with round():
body {
font-size: 18px;
line-height: round(up, calc(1.5 * 1em), 2px);
}
h1, h2, h3, h4, h5, h6 {
line-height: round(up, calc(1.25 * 1em), 2px);
}
Not a big deal, but pretty interesting when using it for the first time.