I encountered a bit of a HTML/CSS oddity the other day, and was a little stumped as to what was happening so I thought I’d share what I learnt in case that helps other people stumbling across the same thing.
The problem
Take a basic <details>
and <summary>
combo:
<details>
<summary>System Requirements</summary>
<p>
Requires a computer running an operating system. The computer must have some
memory and ideally some kind of long-term storage. An input device as well
as some form of output device is recommended.
</p>
</details>
Now, consider the following inocuous CSS:
html {
box-sizing: border-box;
}
* {
box-sizing: inherit;
}
While there is nothing too ground-breaking here, what’s interesting to note is that anything within the <details>
element will have a content-box
box sizing, and not a border-box
one.
Feel free to judge by yourself in this demo.
This is not specific to box-sizing
though. There is nothing special about this property that would cause this behaviour. In face, all enforced inheritance break down at the <details>
layer, as Šime Vidas pointed out on Twitter.
The reason
Amelia Bellamy-Royds was so kind as to explain why that is:
Because of the weird display model of
<details>
, it is implemented as a shadow DOM (with the summary slotted in first, and then the rest of the light DOM contents). Inherited properties will inherit through the composed tree including shadow elements, which you can’t style.CSS inheritance should follow [<details> → shadow root → <slot> → <summary>]. But
box-sizing
isn't normally inherited, and the* { box-sizing: inherit }
rule in the document won’t match either the shadow root node or the slot element.
Amelia then recommended enabling the “Show user agent shadow DOM” Chromium DevTools setting, which enhance the DOM representation with browser shadow DOM. Inspecting our demo, we can see something like this now:
<details>
#shadow-root (user-agent)
<slot name="user-agent-custom-assign-slot" id="details-summary">
<!-- ↪ <summary> reveal -->
</slot>
<slot name="user-agent-default-slot" id="details-content">
<!-- ↪ <p> reveal -->
</slot>
<summary>System Requirements</summary>
<p>
Requires a computer running an operating system. The computer must have some
memory and ideally some kind of long-term storage. An input device as well
as some form of output device is recommended.
</p>
</details>
As Amelia explains, the <summary>
is inserted in the first shadow root slot, while the rest of the content (called “light DOM”, or the <p>
tag in our case) is inserted in the second slot.
The thing is, none of these slots or the shadow root are matched by the universal selector *
, which only matches elements from the light DOM. Therefore, <summary>
properly inherits box-sizing
from its parent, but its inner shadow root does not, and neither do the inner slots, hence why the <summary>
and the <p>
elements don’t.
The workaround
I played with some ideas to apply the box-sizing
rule to shadow roots as well, but I didn’t find anything too conclusive.
What’s particularly interesting is that things work as you’d expect on Firefox though. So either Firefox does not implement <details>
with Shadow DOM (which it doesn’t have to, as the implementation is not specified), or it does but it makes inheritance work as expected. There is an open whatwg/html issue about this.
I guess a simple fix is to apply box-sizing: border-box
to details > *
as well, or to apply box-sizing: border-box
to everything and bypass inheritance entirely.