CSS Gotchas: Specificity, Box Model, Flexbox, and Position
Why your !important rule isn't winning, the difference between box-sizing values that breaks layouts, flexbox alignment surprises, and the position: sticky requirements nobody mentions.

CSS looks simple from a distance and gets gnarly fast. The cascade, specificity, the box model, flexbox alignment, positioning — each has subtle rules that produce "why isn't this working?!" moments even for senior front-end devs.
1. Specificity arithmetic — count the right things
Specificity is a 4-tuple: (inline, IDs, classes/attrs/pseudo-classes, elements/pseudo-elements).
| Selector | Specificity |
|---|---|
* | (0,0,0,0) |
div | (0,0,0,1) |
div p | (0,0,0,2) |
.button | (0,0,1,0) |
.button.primary | (0,0,2,0) |
input[type=text] | (0,0,1,1) |
#header | (0,1,0,0) |
#header .nav a | (0,1,1,1) |
style="color: red" (inline) | (1,0,0,0) |
Any of the above + !important | beats non-important, ties broken by other rules |
Higher tuple wins. Tuples compared left-to-right (an ID always beats any number of classes).
Common gotcha: !important doesn't actually win against another !important with higher specificity. So #header .nav { color: red !important } beats a { color: blue !important }.
2. The box-sizing default ruins layouts
By default, width only covers the content. Padding and border are added on top.
``css
.box {
width: 100px;
padding: 10px;
border: 1px solid;
}
/* Actual rendered width: 122px */
``
Most modern projects use:
``css
*, *::before, *::after {
box-sizing: border-box;
}
``
Now width includes padding and border. Sizing math actually works as expected.
3. Collapsing margins
Vertical margins of adjacent block elements collapse — the larger wins, instead of summing:
``html
<p style="margin-bottom: 20px">A</p>
<p style="margin-top: 30px">B</p>
``
The gap between A and B is 30px, not 50px.
Margin collapsing breaks if you add: padding, border, flex/grid context, overflow: hidden on parent, or any display other than block.
If you want predictable spacing, use gap (with flex/grid) or a single direction for margins.
4. position: absolute parent — what is "absolute" relative to?
absolute is relative to the nearest positioned ancestor (any ancestor with position: relative | absolute | fixed | sticky). If none exists, it's relative to the viewport.
This trips people up when nested layouts behave unexpectedly:
``html
<div>
<div class="parent"> <!-- no position set -->
<div class="child" style="position: absolute; top: 0;">
<!-- positions relative to viewport, not .parent -->
</div>
</div>
</div>
``
The fix: give the parent position: relative. This is the most common positioning bug.
5. position: sticky needs all the right ingredients
Sticky only works if:
- A scroll direction value (
top,bottom,left, orright) is set - The nearest scrolling ancestor has enough height to actually scroll past it
- No ancestor has
overflow: hiddenoroverflow: autoclipping it - The element is inside a block with sufficient height
The most common bug: overflow: hidden on a parent silently kills sticky.
6. Flexbox: justify-content vs align-items (depends on direction)
The classic confusion:
- `justify-content` acts along the main axis (horizontal by default)
- `align-items` acts along the cross axis (vertical by default)
But if you change flex-direction: column, the axes swap:
justify-contentis now verticalalign-itemsis now horizontal
This is the source of countless "why doesn't this center" bugs.
7. Flex children: flex-basis: auto is not the default
```css .child { flex: 1; /* shorthand for: flex-grow: 1; flex-shrink: 1; flex-basis: 0; */ }
.child { flex: 1 1 auto; /* not the same! */ } ```
The default flex-basis is auto in some shorthands and 0 in others. With flex-basis: 0, items split available space equally. With flex-basis: auto, items take their content width first, then grow.
If your flex items aren't equal-width when you expect them to be, this is usually why.
8. z-index only works on positioned elements
```css .bad { z-index: 999; /* does nothing! */ }
.good { position: relative; z-index: 999; } ```
z-index requires a positioning context. Without one, the value is ignored. Flex/grid items are the exception — they can stack with z-index without explicit position.
9. Stacking contexts are sneakier than z-index
A new stacking context is created by:
position: relative/absolutewithz-indexother than autoposition: fixed/stickyopacityless than 1transform,filter,will-changeon certain propertiesisolation: isolate
Once an element creates a stacking context, its children are stacked within it — they can never escape to a higher level, no matter how big their z-index.
This is why a modal with z-index: 99999 sometimes appears behind a parent that has transform: translateX() applied. The parent created a stacking context.
10. display: none vs visibility: hidden vs opacity: 0
| Renders space? | Clickable? | In tab order? | |
|---|---|---|---|
display: none | no | no | no |
visibility: hidden | yes | no | no |
opacity: 0 | yes | yes | yes |
That last row is a common accessibility bug — an opacity-0 button is still clickable and still in tab order. Use visibility: hidden if you want to hide without removing from layout.
11. Inline elements ignore width/height
``css
span {
width: 100px; /* ignored */
height: 50px; /* ignored */
}
``
You can set width/height on inline elements but they have no effect. Switch to display: inline-block (preserves inline flow but accepts width/height) or block.
12. vh on mobile changes when the URL bar appears/disappears
``css
.hero {
height: 100vh; /* mobile: jumps when address bar hides */
}
``
100vh includes the address bar area on mobile, so when the bar hides, your hero suddenly grows. Use 100dvh (dynamic viewport height) in modern browsers, with 100vh as fallback.
Need help with cross-browser layouts?
CSS bugs are the trickiest to debug because everything looks right "in theory." If you've got a layout that breaks on Safari/iOS but works everywhere else, or a print stylesheet that ignores half your rules, we can fix it.
Need Help With Your Website?
I fix these problems every day. Send me a message and I'll take a look.
Get Help Now