layout · web-platform
Containing block
also called positioning context (community), reference box (community), offset parent (community)
The box an element's percentages and offsets are measured against, which is not always its parent and changes with how the element is positioned.
Every percentage and every offset in CSS is measured against some box, and that box is
the containing block. It is easy to assume the answer is always the parent, and for
ordinary elements in normal flow it usually is: the parent’s content box. The moment an
element is positioned the rule changes. An absolutely positioned element measures
against the padding box of its nearest positioned ancestor, skipping every ancestor in
between that happens to be static. A fixed element measures against the viewport. So
width: 50% and top: 0 on the same element can be talking about two entirely
different rectangles, which is why the useful question is never “what is 100%” but
“100% of what”.
The part that catches people out is that positioning is not the only thing that creates
one. A transform on an ancestor makes that ancestor the containing block for its
absolutely positioned descendants, and it does the same for fixed ones, which is the
only common way a fixed element stops tracking the viewport. filter,
backdrop-filter, will-change naming any of those properties, contain: paint or
layout, and container-type all do it too. None of them look like positioning, which
is why a fixed header that mysteriously scrolls away is almost always sitting inside
something with a transform on it, often applied for an animation somewhere else
entirely. The same properties usually create a stacking context as
well, and the two are separate things that happen to share triggers: one decides what
your offsets mean, the other decides what paints in front of what.
Anchor positioning is the modern answer to the frustration this causes, and the contrast is worth holding onto. Anchor positioning names an element you chose and places another element against it, explicitly, across the DOM and without either one needing to be an ancestor of the other. A containing block is the box your offsets resolve against whether or not you meant to create one, decided by the cascade and by whichever ancestor happens to carry a transform today. One is a declaration, the other is a consequence.
The same box turns up wherever a layout takes a percentage. A
motion path resolves its own percentage offsets against the containing
block, so moving a transform up the tree can change where an animation travels without
touching the animation. A container query container is a containing
block, since container-type brings containment with it. When something lands in an
unexpected place, the fastest diagnosis is not to read the element’s own rules but to
walk up its ancestors asking which one is positioned, transformed, filtered or
contained, because whichever one answers first is the box you are actually working in.
Which word?
| If you want | say |
|---|---|
| asking what 100% is actually 100% of | containing block |
| a component that must adapt to its slot, not the screen | container query |
| why a huge z-index still sits behind something | stacking context |
Related
See also: Anchor positioning