CSS Positioning Isn't About Coordinates — It's About Who Keeps the Seat
Positioning bugs — overlapping content, missing scroll targets, elements that vanish behind containers — are among the most common CSS frustrations. Knowing which positioning mode preserves document flow and which one destroys it is the difference between a layout that breaks on content change and one that adapts.
Document flow is the default top-to-bottom, left-to-right arrangement that gives pages their natural stretch. Block elements take whole rows; inline elements flow like text; inline-block elements sit in a row but accept width and height. Positioning layers on top of this flow, letting elements deviate from their default spot.
Relative positioning offsets an element from its original position but keeps the seat reserved — the space remains, and surrounding content doesn't reflow. Absolute positioning pulls an element out of flow entirely and anchors it to the nearest positioned ancestor, which is why a parent set to `position: relative` is the classic trick for containing absolutely positioned children. Fixed positioning pins an element to the viewport, ignoring scroll, while sticky positioning toggles between relative and fixed behavior as the element hits a scroll boundary.
When positioned elements overlap, z-index controls stacking order, but only within the same stacking context. A child's z-index can't outrank a sibling of its parent if the parent's stacking context sits behind.
Most positioning confusion comes from conflating visual placement with document-flow behavior. The mental model of 'leaving the queue' versus 'standing up from your seat' maps directly to whether surrounding content reflows.
The common pattern of `position: relative` on a parent with no offsets exists solely to create a coordinate system for child elements — it's a container contract, not a visual adjustment.
Sticky positioning's constraint by its parent container is an underappreciated gotcha: a sticky header will scroll away with its parent section even if the viewport threshold hasn't been crossed.