
When z-index: 9999 still appears behind another element, the browser is usually not broken. You are probably comparing elements in different stacking contexts. z-index is not a global ranking system for the whole page. It is meaningful inside the current stacking context.
What a stacking context is
A stacking context is an independent layering world. Children are stacked inside that context, and then the entire context is compared as one unit in its parent context.
The key rule:
- Elements in the same stacking context can compare
z-indexdirectly. - Elements in different contexts are first limited by their parent contexts.
If a child has z-index: 9999 inside a parent that loses to a sibling context, the child still loses.
Common stacking context triggers
Common conditions that create stacking contexts include:
- The root
htmlelement. position: absoluteorrelativewith non-autoz-index.position: fixedorsticky.opacity < 1.transform.filter.clip-path.perspective.isolation: isolate.- Some flex or grid items with non-
autoz-index.
This is why a small animation or transparency change can unexpectedly change layer behavior.
Why z-index looks ignored
Consider:
<div class="card">
<div class="tooltip">tooltip</div>
</div>
<div class="modal">modal</div>
.card {
position: relative;
z-index: 1;
}
.tooltip {
position: absolute;
z-index: 9999;
}
.modal {
position: relative;
z-index: 2;
}
The tooltip may still appear behind the modal. The parent comparison is:
card (1) < modal (2)
The tooltip's 9999 only wins inside .card. It does not jump out and beat .modal.
transform and opacity surprises
These properties are common sources of accidental contexts:
.panel {
transform: translateY(8px);
}
.mask {
opacity: 0.99;
}
They are visual effects, but they also affect stacking behavior. Animation, drag-and-drop, modal, and tooltip bugs often combine stacking context issues with rendering performance. See Reflow and Repaint: Why CSS Animations Get Janky for the performance side.
When isolation: isolate helps
You can intentionally create a separate stacking context:
.surface {
isolation: isolate;
}
This is useful when a component should manage its own internal layers without leaking into the surrounding page.
It is not a universal fix. It creates a clearer boundary, but you still need a page-level layer strategy for modals, drawers, headers, and popovers.
Debugging order
- Confirm whether the element is positioned or otherwise eligible for
z-index. - Inspect parent elements for stacking context triggers.
- Compare sibling parent contexts before comparing child numbers.
- Decide whether the fix is a parent z-index, DOM movement, portal, fixed positioning, or top-layer API.
If you keep increasing a number from 20 to 9999 and nothing changes, the issue is probably the comparison boundary, not the number.
When not to keep raising z-index
Avoid random z-index escalation when:
- A global modal is behind a local tooltip.
- Sticky headers, drawers, and popovers all define their own local scales.
- A component library and app code maintain unrelated layer constants.
- Parent containers have accidental
transformoropacity.
Use a small global layer scale for app-wide overlays, and use portals when a local DOM position creates the wrong boundary.
Related FreeMac guides
- For inheritance and reset behavior, read CSS initial, inherit, and unset: How to Choose.
- For animation performance, read Reflow and Repaint: Why CSS Animations Get Janky.
- For React overlay placement, the Chinese version currently has a React Portal guide.
Continue reading
CSS 3D: perspective and transform-style Explained
Understand CSS 3D transforms through perspective, transform-style: preserve-3d, translateZ, rotateY, flattening, and parent-child 3D scene setup.
7 min readReflow and Repaint: Why CSS Animations Get Janky
Understand browser rendering steps, reflow, repaint, compositing, and why transform and opacity are usually better animation targets than width, height, top, or left.
7 min readCSS initial, inherit, and unset: How to Choose
Understand CSS initial, inherit, and unset by first checking whether a property normally inherits, with practical examples for color, margin, reset styles, and components.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.