* { line-height: calc(1em + 0.5rem); }

Draft: started Tagged ,

Just in case that title was too huge to really comprehend,
or you’re using a feed reader that improperly mangles the title:

* {
	line-height: calc(1em + 0.5rem);
}

I’ve been using this technique for the last few years and it seems pretty good.

Well, actually in my new 2026 website I’ve gone for 0.375em instead of 0.5em, but we’ll get to that.
I think it’s slightly easier to explain and convince with the 0.5 number.

Why

You want a gap between lines for readability.
Otherwise you end up with a paragraph like this one
(unless you’re reading in a feed reader that strips styles).
CSS’s design encourages you to set the gap to a multiple of the font size.
This is fine at small sizes, but works badly for headings and other large text:
because in practice, you want it to scale sublinearly, or not at all.

So historically, people have tended to set line-height each time they adjust font-size.
It works, but it’s far too easy to do inconsistently.

When you get to very large text (such as headings),
you want to have a reasonable gap
between the lines.
This technique gets you that.
The gaps don’t need to scale on font-size.

Whereas if you just wrote
line-height: 1.5,
which looked fine at normal sizes,
it becomes too spacious
and you can’t fit much on screen
and it looks ugly for its
rivers of white.

And if you just wrote
line-height: 24px or 1.5rem or such,
it gets all squished up
and you can’t read it because you’re distracted
because it looks so ugly.

How

Unfortunately, you can’t just define it on :root:
the calc(1em + 0.5rem) will only be calculated once, and the computed value inherited.

So instead you have to define it on every element, so it gets calculated on every element:

* {
	line-height: calc(1em + 0.5rem);
}

* is sadly not quite enough, because of pseudoelements.
Depending on what you do, you might want to add ::before, ::after, ::marker, ::first-line, ::first-letter, and potentially more.

Comparing the results to old-style declarations

Supposing you have a base font-size of 16px:

Font size calc(1em + 0.5rem)
computed line-height
calc(1em + 0.375rem)
computed line-height
Pixels Factor Pixels Factor
16px24px1.5 22px1.375
18px26px1.44424px1.333
20px28px1.4 26px1.3
22px30px1.36428px1.273
24px32px1.33330px1.25
26px34px1.30832px1.231
28px36px1.28634px1.214
30px38px1.26736px1.2
32px40px1.25 38px1.188
34px42px1.23540px1.176
36px44px1.22242px1.167
38px46px1.21144px1.158
40px48px1.2 46px1.15
42px50px1.19048px1.143
44px52px1.18250px1.136
46px54px1.17452px1.130
48px56px1.16754px1.125

In traditional typesetting terms, calc(1em + 0.5rem) is 8px leading regardless of font size.

I believe 1em, making the leading not scale, is a good choice, but you can adjust that coefficient if you wish.

As for the 0.5rem part, that one’s definitely more subjective.
Normally I go for 0.5rem, this time I decided to go for 0.375rem, which (simplifying) is 6px, 2px less.

Downsides

The main problem is that it has to be set on everything. That makes it harder to override.
In my sample paragraph above, I couldn’t just write <p style=line-height:1>,
I had to apply that to all of its descendents (br and em) too.

Body text: Fonts:
Theme:
Explanation of all this
(yes, this works without JavaScript; persists to cookies)