Boxes and positioning
Stacking and overflow
Diagnose clipped content and overlapping layers by understanding overflow behavior, z-index, and independent stacking contexts.
Two things confuse people more than anything else in CSS: content that spills out of its box, and elements that refuse to sit on top of each other. Both have simple explanations once you know where to look.
Overflow
A box is often smaller than what it contains. A long line of code, a wide table, a URL with no spaces. When that happens the content overflows, and the overflow property decides what the browser does about it.
For a code sample I want a horizontal scrollbar, not a clipped line:
.code-sample {
overflow-x: auto;
}
auto shows a scrollbar only when it’s needed. My advice is to prefer scrolling or wrapping over clipping whenever the content matters.
Be careful with overflow: hidden. It clips, and it also turns the element into a scroll container even if you never see a scrollbar. That has side effects: a position: sticky child now sticks inside that box instead of the page, and focus outlines that poke out of the box get cut off. Keyboard users lose the visual cue that tells them where they are.
z-index and stacking contexts
z-index decides which element paints on top when boxes overlap. It only works on positioned elements (and on flex and grid items). A higher number wins.
But the number only counts inside its own stacking context. A stacking context is a group of elements the browser paints together, as one unit. Once an ancestor forms one, no z-index inside it can escape it. z-index: 9999 on a menu means nothing if the menu’s parent sits in a context that is below its neighbor.
Many properties create a new stacking context. The common ones are a positioned element with a z-index other than auto, opacity below 1, any transform, fixed or sticky positioning, and container-type: inline-size.
So when a dropdown appears behind a card next to it, don’t raise the number. Inspect both ancestors. Nine times out of ten the two elements live in different contexts, and the fight is between the parents, not the children.
For everyday interfaces I keep a small, named scale of layers, like --layer-dropdown: 10 and --layer-modal: 20. If you find yourself typing 999999, something upstream is wrong.
Native <dialog> elements and popovers go into the browser’s top layer, which sits above every stacking context. For a real modal, use that. It beats any z-index war.
Try this on your own page: put a transform on a card’s parent and watch its menu drop behind the next card. Then toggle opacity, transform, and position on the ancestors in DevTools, one at a time, until the layer order makes sense.
Quick check
Result
You got of right.
Lesson completed