Is CSS line-height affected by the parent?

Today, I searched for «Is CSS line-height affected by the parent?» and found no direct answers. After figuring out the answer, I decided to document it here for others facing the same issue.

If you are here just looking for the answer and not interested in the context, you can skip ahead directly to the TLDR.

The problem

Imagine you are maintaining the design system pattern library. You have a well-established typography component, which is responsible for applying one of the font-size/line-height combinations defined by designers. It's the most used component of a design system, that existed for a long time and seems to be pretty stable.

One day a designer from your team comes to you and says: «the typography component uses the correct values for font-size/line-height and is correctly rendered in the component playground, BUT the rendered line height is different (incorrect) when the component is used in one of the applications».

The struggle

You can't think of any plausible explanation for this behavior. Intrigued, you fire up your browser debugger and open the problematic application to check what's happening there.

You check line-height assigned by the typography component, and it's the same between the component playground and that application. The "Computed" tab of the browser debugger confirms that this exact value is really applied to the element in both cases. You make screenshots of what's rendered, compare them to each other and there's indeed a difference between the playground and the application. The application renders text with a larger line height than the component playground does.

«Weird!» - you think, trying to come up with an explanation for this behavior. «Maybe it's because of some other CSS property?» - you decide to start adding properties from the application to the component playground, hoping to observe what property will result in increase of the rendered line height. Your first suspects are text-rendering and -webkit-text-size-adjust but they clearly do not affect the line-height. Next, you copy all of the CSS properties applied, making sure that the "Computed" tab of the browser debugger for the element looks exactly the same between the component playground and the application.

At this point, the only plausible explanation left is that the parent is somehow affecting the child's rendered line height. You start with changing parent's line-height - and voilà - you can see that it affects the rendered line height of a child in the component playground. And while in the application parent doesn't have a line-height explicitly set, it still inherits one that was set for one of the ascendants (as line-height is an inherited property).

What does the spec say?

But how is it possible that parent affects the child's line-height? You start searching for any information online about this weird scenario. There aren't any results that directly talk about line-height in a "parent-child" context. Then you find the relevant CSS spec, and you're lucky to quickly notice the following paragraph there:

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).

Why does no one talk about this?

So this isn't a browser bug, it is mentioned in the spec and it is just how CSS works. But why does nobody talk about it? Why, in your 10-year career, you have never heard about a parent setting a minimum line-height for a child? Is it because "slightly bigger line height" does not pose a problem for most people, and sharp designer eye is needed to notice the inconsistency? Maybe, but you feel like there's gotta be another reason.

Looking for answer, you read the whole "Line-height caluclations" section of the spec, and only then you realize that the key detail you were missing is the display property value. The spec quote above specifies that block parent sets a minimum line-height for inline children.

In other words, no one talks about it because developers typically set the line-height on block elements.

Looking at your subjective experience, you recall that line-height is usually set for the whole page with overrides for the individual headings/paragraphs, but it's almost never overridden for an inline element. Therefore block parent controlling line-height is the default expectation, and having a line-height set on an inline element is a weird edge case that isn't worth talking about.

The typography component you were debugging defaults to using inline element to support using two different styles next to each other in an inline context (e.g. using a monospaced font for a specific term inside the text paragraph). However, supporting inline elements allows line-height to be overridden by the parent element, thus breaking the encapsulation of the typography component.

The resolution

Well, ideally typography component should simply stop trying to set the line-height on an inline element, applying line-height to a block parent instead. However, the block parent is outside of the typography component boundaries, and therefore cannot be affected by the typography component.

Thus, the only option is to prevent the typography component from relying on line-height set on the inline element. One way to do that is to make the typography component context-aware:

  1. make the typography component render a block element by default
  2. allow the typography component to render an inline element only if it's a descendant of a block element rendered by the other instance of the typography component

TLDR

Parent can affect the rendered line height of a child. However, that only happens if the elements in question are a block parent and an inline child. In which case parent sets the minimum line height for a child.

The resolution is:

  • either to start setting line-height on the parent instead of the child
  • or to change child's display to a value that makes it a block element