Somewhere in nearly every developer's early career comes the day they discover CSS specificity, flexbox alignment, or z-index stacking context by way of a bug that made no visible sense until it suddenly, completely did. A handful of CSS behaviors carry this exact reputation — technically documented, entirely logical once understood, and consistently surprising the first time they're encountered without warning.
z-index doesn't work the way its name suggests
The natural assumption about z-index is that it's a simple global ranking — higher number always renders on top, full stop, no exceptions. In reality, z-index only has meaning within what's called a "stacking context," and elements can create new stacking contexts through a range of triggers beyond just having a z-index value set — certain CSS transforms, certain opacity values less than 1, and several other properties can all independently establish a new stacking context. Once a new stacking context exists, z-index values inside it are only compared against other z-index values within that same context, not against every z-index value on the entire page. This produces the maddeningly common bug where an element with an enormous z-index like 9999 still renders behind another element with a much smaller z-index, because the two elements belong to entirely different stacking contexts that were never being compared against each other in the first place.
Margin collapsing between vertically adjacent elements
When two block-level elements stack vertically and both have a margin between them, CSS doesn't add the two margins together the way most people initially expect — it collapses them into a single margin equal to whichever of the two was larger. A ten-pixel bottom margin on one element followed by a fifteen-pixel top margin on the next element produces a fifteen-pixel gap between them, not twenty-five. This behavior exists for genuinely reasonable historical typesetting reasons (preventing accumulated whitespace between many stacked paragraphs from growing unpredictably large), but it consistently surprises anyone encountering it for the first time, especially since the collapsing behavior has its own further set of conditions determining exactly when it does and doesn't apply — margins on elements with certain other properties set, or margins between a parent and its first or last child, don't always collapse the same way.
Percentage-based padding is calculated from width, even for top and bottom
Setting padding-top or padding-bottom as a percentage produces a value calculated relative to the containing element's width, not its height — which feels deeply counterintuitive, since top and bottom padding visually affects vertical space, and it's natural to assume a vertical property's percentage would be calculated against a vertical dimension. This specific behavior is actually the basis of a well-known CSS technique for maintaining a fixed aspect ratio box before the modern aspect-ratio property existed (setting padding-top as a percentage was one of the few reliable ways to force a responsive element to maintain a specific width-to-height ratio), but understanding why that old technique worked requires knowing this specific, unintuitive width-based calculation rule for vertical padding percentages.
Absolutely positioned elements escape normal document flow entirely
Setting position: absolute removes an element from the normal document flow entirely, meaning surrounding elements behave exactly as if that absolutely positioned element didn't exist for layout purposes — no space is reserved for it, and it doesn't push adjacent content aside the way a normally-flowing element would. This regularly surprises people who expect absolute positioning to work more like "move this element to a specific spot while everything else adjusts around it," when the actual behavior is closer to "remove this element from the layout calculation entirely, then place it precisely wherever specified, with everything else laid out as if it were never there at all."
Why none of this is actually a CSS design flaw
Every one of these behaviors is fully documented, consistently implemented across browsers, and exists for coherent underlying reasons rooted in CSS's original design goals — mostly, requirements around flexible document layout that predate the vast majority of how CSS actually gets used today for complex application interfaces. The surprise isn't a sign that CSS is poorly designed; it's a sign that CSS was designed for a somewhat different original context than most of its current heavy usage, and a handful of its foundational rules don't map cleanly onto the intuitions a developer coming from a more typical programming background naturally brings to it.
Where to go deeper
Specificity — a closely related CSS behavior with its own reputation for quiet surprises — gets its own dedicated explanation in an earlier post on this blog, if you haven't run into that particular one yet. For visually experimenting with layout properties like flexbox and grid rather than reasoning through them purely in the abstract, our Flexbox generator and Grid generator both let you adjust values with live sliders and watch the actual rendered result update immediately.