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.
Having different line heights for headings is pretty common, and the current design was no different.
In the end, we agreed to keep the current system of two (relative) line heights in place, instead of having 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.
Because with a font size of 18px and a relative ratio of 1.5, the line height is 27px. They would sleep better if it were 28px.
(It’s an entirely different discussion that things weren’t implemented with fixed pixels but rather using the base font size.)
I haven’t used much the round() function from CSS, however in the past, I’ve come across many interesting use cases and ideas.
So the solution was to take advantage of it and always bump the line height up to the next even number if the calculation resulted in an odd value.
This way, the line height was based on 1.5, and exceptions were handled with round():
:root {
--lh-ratio: 1.5;
}
body {
line-height: round(up, calc(var(--lh-ratio) * 1em), 2px);
}
Not a big deal, but pretty interesting when using it for the first time.