The CSS z-index property
The CSS z-index property sets the stacking order of overlapping elements — a higher number sits in front of a lower one. It only works on elements with a position other than static (or on flex/grid items). If z-index seems to do nothing, a missing position is almost always why.
Overview
z-index decides which element wins when boxes overlap — think a dropdown that needs to sit above the content, or a modal above everything. A higher value is closer to the viewer, a lower value further back. Negative values are allowed and push an element behind its siblings.
The single most common gotcha: z-index has no effect on a statically-positioned element, which is the default. The element needs position set to relative, absolute, fixed or sticky first (flex and grid items are an exception and respond to z-index directly). If you have set a sky-high z-index and nothing moved, check the position.
The deeper concept is stacking contexts. Certain properties — a positioned element with a z-index, any element with an opacity below 1, a transform, and others — create a new, self-contained stacking world. A child's z-index only competes within its context, never against elements outside it. That is why a z-index of 9999 sometimes still loses: it is trapped in a context that, as a whole, sits behind its rival.
Syntax
selector {
position: relative; /* or absolute, fixed, sticky */
z-index: 10;
}
Values
The z-index property accepts the values below. Every property also accepts the CSS-wide keywords inherit, initial, revert and unset.
| Value | Description |
|---|---|
auto |
The default. The element stacks in normal order without forming a new layer. |
0 |
A baseline stacking level, on the same plane as auto siblings. |
positive |
Higher numbers sit in front, e.g. z-index: 10. Use sparingly and consistently. |
negative |
Pushes the element behind its siblings, e.g. z-index: -1. |
Example
<style>
.stack { position: relative; height: 120px; font: 600 14px system-ui, sans-serif; }
.stack div { position: absolute; width: 90px; height: 70px; border-radius: 10px; display: flex; align-items: center; justify-content: center; color: #fff; }
.back { left: 30px; top: 10px; background: #94a3b8; z-index: 1; }
.front { left: 70px; top: 30px; background: #1c7ce9; z-index: 2; }
</style>
<div class="stack">
<div class="back">z: 1</div>
<div class="front">z: 2</div>
</div>
Best practices
- Remember
z-indexneeds a non-static position (or a flex/grid parent). No position, no effect. - Use a small, agreed scale (say 10, 100, 1000 for dropdowns, sticky bars, modals) instead of escalating to 9999 — that war never ends well.
- When a high z-index still loses, look for a parent forming a stacking context with opacity, transform or a z-index of its own.
- Keep z-index values documented or tokenised in larger projects so layering stays predictable across components.
Frequently asked questions
Why is z-index not working?
position: static, on which z-index has no effect. Give it position: relative (or absolute/fixed/sticky) and the z-index will apply.