How do z-index and stacking contexts work?
CSSThe short answer
z-index controls the vertical stacking order of elements — higher values appear in front of lower values. But it only works on positioned elements (position: relative, absolute, fixed, or sticky). The tricky part is stacking contexts — a new stacking context creates a self-contained layer where z-index values only compete within that context, not globally.
z-index basics
.box-a { position: relative; z-index: 1;}.box-b { position: relative; z-index: 2;}/* box-b appears in front of box-a */z-index only works on elements with position set to something other than static (the default). Without position, z-index does nothing.
What creates a stacking context
This is where most confusion comes from. A new stacking context is created by:
position: relative/absolute/fixedwithz-indexset (notauto)opacityless than 1transform,filter,perspective,clip-pathposition: fixedorposition: stickyisolation: isolate- Flex/grid children with
z-indexset
Why stacking contexts matter
Elements inside a stacking context can only compete with each other — they cannot "escape" their parent's context.
.parent-a { position: relative; z-index: 1;}.child-a { position: relative; z-index: 9999;}.parent-b { position: relative; z-index: 2;}.child-b { position: relative; z-index: 1;}Even though .child-a has z-index: 9999, it still appears behind .child-b because .parent-a (z-index: 1) is behind .parent-b (z-index: 2). The child cannot escape its parent's stacking context.
This is the most common reason "z-index is not working" — the element's parent has a lower stacking context than the element it is trying to appear in front of.
The default stacking order
Without any z-index, elements stack in this order (back to front):
- Background and borders of the root element
- Non-positioned block elements in document order
- Floated elements
- Inline elements
- Positioned elements in document order
Debugging z-index issues
When z-index is not working:
- Check if the element has
positionset (notstatic) - Check if a parent creates a stacking context (look for
opacity,transform,z-indexon ancestors) - Use browser DevTools — some browsers show stacking contexts in the Elements panel
isolation: isolate
If you want to create a stacking context without side effects:
.component { isolation: isolate;}This creates a new stacking context without changing position, opacity, or transform. It is useful in component libraries where you want to prevent z-index leaking between components.
Common Pitfalls
A very common CSS pitfall is setting z-index: 9999 and wondering why it still does not work. The problem is almost always a parent element creating a stacking context with a lower z-index. No matter how high you set the child's z-index, it cannot break out of its parent's context. Always check the parent hierarchy when debugging z-index issues.
Interview Tip
The key concept interviewers want to hear is stacking contexts — not just z-index values. Show the parent-child example where a child with high z-index is still behind another element because of its parent's context. If you can mention isolation: isolate, that shows advanced CSS knowledge.
Why interviewers ask this
z-index problems are some of the most frustrating CSS issues. Interviewers ask about it to see if you understand stacking contexts, not just the z-index property itself. A candidate who can explain why "z-index 9999 does not work" demonstrates deep CSS understanding.