Table of Contents
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:
- make the typography component render a block element by default
- 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