Positioning and containing blocks: why your fixed header moved
Which box each position value is placed against, why transform, filter and contain on an ancestor trap position: fixed, what sticky needs to work, and the inset shorthand.
Comes up in: Web fundamentals, UI coding
Learn#
Priya’s fixed header scrolls away on one page only. Nothing changed in the header’s CSS. A wrapper three levels up got a transform for a slide-in animation.
Every positioned box is placed against a containing block. That is the box that top, left and percentages refer to. The table shows which box that is for each position value.
| position | Containing block |
|---|---|
| static, relative, sticky | The nearest block container ancestor (usually the parent) |
| absolute | The nearest ancestor that is positioned (not static), or the initial containing block |
| fixed | The viewport, unless an ancestor has transform, filter, perspective or containment |
That last exception is Priya’s bug. A transform on any ancestor makes it the containing block for fixed children. So “fixed” now means “fixed to that ancestor”.
State
- header → containing block: viewport
Page
empty
The header has position: fixed and top: 0. Normally its containing block is the viewport, so it stays put while scrolling.
Read the steps as text
- The header has position: fixed and top: 0. Normally its containing block is the viewport, so it stays put while scrolling.
- A teammate adds a slide-in animation to the page wrapper: transform: translateX(0).
- Any transform on an ancestor makes that ancestor the containing block for fixed descendants.
- Now the header scrolls away with the page. Removing the transform, or moving the header outside the wrapper, fixes it.
Predict#
Predict first
A sidebar has position: sticky; top: 0, but it never sticks. Its parent has overflow: hidden. Why?
Sticky sticks relative to its nearest scrolling ancestor. overflow: hidden makes the parent that ancestor, and the parent does not scroll, so nothing sticks. People blame the sticky element, but the problem is an ancestor.
Interview angle#
Say it like this — about 30 seconds
“Every positioned element is laid out against a containing block. For absolute, it is the nearest positioned ancestor. For fixed, it is normally the viewport. But a transform, filter or containment on an ancestor makes that ancestor the containing block. That is a classic broken-header bug. Sticky needs a threshold such as top: 0. It sticks within its nearest scrolling ancestor, and only while its parent is on screen. So an overflow hidden or a short parent stops it.”
Follow-up What does position: sticky need to work?
Follow-up Why is my fixed header broken inside a transformed parent?
Follow-up What does inset: 0 mean?
Practice#
Guided
Encode the containing-block rules, including the traps that capture fixed elements.
Done when
- Absolute finds the nearest positioned ancestor or trap, else “initial”.
- Fixed finds the nearest trap, else “viewport”.
- Static, relative and sticky return the parent.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What is the containing block of an absolute element?
Its nearest positioned ancestor (or the initial containing block).
Name three ancestor properties that trap position: fixed.
transform, filter, perspective (also contain and will-change).
What does sticky need?
An offset such as top: 0 and a scrolling ancestor it can stick within.
What does inset: 0 set?
top, right, bottom and left to 0.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Percentages in top, left, width and height refer to the containing block. For absolute elements, it is the inside edge of the positioned ancestor’s border Source: MDN: Layout and the containing block.
Check yourself: What does top: 50% of an absolute element refer to?
50% of its containing block's height.
How it works underneathSenior rounds
will-change: transform creates a containing block even before any transform runs, because the browser prepares for one. contain: paint and layout do too Source: CSS Transforms 1: containing block for all descendants.
Check yourself: Does will-change: transform trap fixed descendants?
Yes, it behaves as if a transform were set.
Edge cases and trapsWhere people slip
Check yourself: Why does a sticky element stop sticking near the end of its section?
It cannot leave its parent; it stops at the parent's edge.
In productionStaff-level answers
inset sets all four offsets at once, as in inset: 0 for overlays.<dialog> and popovers, which live in the top layer (WEB.07.02).Check yourself: Where should a fixed header or modal live in the DOM to avoid traps?
Near the root, outside animated wrappers, or in the top layer.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What does
position: stickyneed to work?