My Learning Curve

Feed / Lea Verou's blog

Inherit ancestor font-size, for fun and profit

Thursday, June 24, 2021

If you’ve been writing CSS for any length of time, you’re probably familiar with the em unit, and possibly the other type-relative units. We are going to refer to em for the rest of this post, but anything described works for all type-relative units.

As you well know, em resolves to the current font size on all properties except font-size, where it resolves to the parent font size. It can be quite useful for making scalable components that adapt to their context size.

However, I have often come across cases where you actually need to “circumvent” one level of this. Either you need to set font-size to the grandparent font size instead of the parent one, or you need to set other properties to the parent font size, not the current one.

If you’re already familiar with the problem and just want the solution, skip ahead. The next few paragraphs are for those thinking “but when would you ever need this?”

Sometimes, there are workarounds, and it’s just a matter of keeping DRY. For example, take a look at this speech bubble:

Note this in the CSS:

/* This needs to change every time the font-size changes: */
top: calc(100% + 1em / 2.5);
font-size: 250%;

Note that every time we change the font size we also need to adjust top. And ok, when they’re both defined in the same rule we can just delegate this to a variable:

--m: 2.5;
top: calc(100% + 1em / var(--m));
font-size: calc(var(--m) * 100%);

However, in the general case the font size may be defined elsewhere. For example, a third party author may want to override the emoji size, they shouldn’t also need to override anything else, our CSS should just adapt.

In other cases, it is simply not possible to multiply and divide by a factor and restore the ancestor font size. Most notably, when the current (or parent) font-size is set to 0 and we need to recover what it was one level up.

I’ve come across many instances of this in the 16 years I’ve been writing CSS. Admittedly, there were way more use cases pre-Flexbox and friends, but it’s still useful, as we will see. In fact, it was the latest one that prompted this post.

I needed to wrap elements by a generic container for a library I’m working on. Let me stop you there, no, I could not just set classes on the options, I needed an actual container in the DOM.

As you can see in this pen, neither

nor custom elements work here: when included in the markup they are just discarded by the parser, and when inserted via script they are in the DOM, but the options they contain are not visible. The only elements that work inside a