In short
The CSS z-index property controls which element appears on top when elements overlap. This lesson explains stacking order and stacking contexts in simple words, and shows how to fix "z-index not working". What is z-index? A web page is not only flat.
The CSS z-index property controls which element appears on top when elements overlap. This lesson explains stacking order and stacking contexts in simple words, and shows how to fix "z-index not working".
What is z-index?
A web page is not only flat. When elements overlap, the browser must decide which one is in front. Imagine sheets of paper on a desk: the x-axis goes left to right, the y-axis goes top to bottom, and the z-axis comes out of the screen towards you. z-index sets an element's place on that z-axis.
A higher z-index comes to the front. A lower or negative one goes to the back.
.dropdown {
position: absolute;
z-index: 10;
}
Common mistake: Putting
z-indexon a normal, non-positioned element.z-indexonly works on elements with apositionother thanstatic, or on flex and grid items. If nothing happens, check that the element hasposition: relative(or absolute, fixed, sticky).
How does the browser stack elements by default?
Without any z-index, the browser paints things in this order, from back to front:
- The background and borders of the parent.
- Normal, non-positioned elements, in the order they appear in the HTML.
- Positioned elements (
relative,absoluteand so on), in HTML order.
So by default, a positioned element appears in front of a non-positioned one, and between two positioned elements, the one that comes later in the HTML wins.
Let's see three overlapping cards:
<div class="stack">
<div class="box red">Red</div>
<div class="box blue">Blue</div>
<div class="box green">Green</div>
</div>
.stack {
position: relative;
height: 220px;
}
.box {
position: absolute;
width: 140px;
height: 140px;
color: white;
font-weight: bold;
padding: 10px;
border-radius: 10px;
}
.red { background: #dc2626; top: 0; left: 0; }
.blue { background: #2563eb; top: 40px; left: 60px; }
.green { background: #16a34a; top: 80px; left: 120px; }
.red {
z-index: 3;
}
Without the last rule, green is in front because it comes last in the HTML. With z-index: 3 on the red box, red jumps to the front, even though it comes first. Blue and green keep their normal order behind it.
What is a stacking context?
This is the part that confuses most people. A stacking context is a group of elements that are stacked together as one layer. Think of it like a folder of papers: you can reorder papers inside the folder, but the whole folder sits at one spot in the pile. A paper inside a bottom folder can never go above a different folder that is on top, no matter its number.
An element creates a new stacking context when, for example, it has:
position: relativeorabsoluteand az-indexother thanauto.position: fixedorsticky.opacityless than1.- A
transform,filterorisolation: isolate. - Is a flex or grid item with a
z-indexother thanauto.
Here is the classic "z-index not working" problem:
<header class="header">
<div class="menu">Dropdown menu</div>
</header>
<section class="hero">Hero banner</section>
.header {
position: relative;
z-index: 1;
background: #1f2937;
color: white;
padding: 16px;
}
.menu {
position: absolute;
top: 100%;
left: 16px;
z-index: 9999;
background: white;
color: #1f2937;
padding: 16px;
box-shadow: 0 4px 12px rgb(0 0 0 / 0.2);
}
.hero {
position: relative;
z-index: 2;
background: #fde68a;
height: 200px;
padding: 16px;
}
The dropdown menu has z-index: 9999, yet it is hidden behind the yellow hero. Why? The menu lives inside .header, which is a stacking context with z-index: 1. The hero has z-index: 2. The whole header "folder" is below the hero, so the menu's 9999 only counts inside the header. To fix it, give .header a higher z-index than .hero (for example z-index: 3), or remove the z-index from .hero.
How to fix z-index not working
When an element refuses to come to the front, check these things in order:
- Does the element have a
positionother thanstatic(or is it a flex or grid item)? - Is a parent creating a stacking context with a low
z-index? Raise the parent, not the child. - Does a parent have
opacity,transformorfilter? These create a stacking context too. - Is a parent cutting it off with
overflow: hidden? That is clipping, not stacking, andz-indexcannot fix it.
Tip: Use a small, planned scale instead of random numbers like 9999. For example:
1for raised cards,10for dropdowns,100for sticky headers,1000for modals. Custom properties make this easy:--z-modal: 1000;.
Note: You can create a clean stacking context on purpose with
isolation: isolate;. It keeps thez-indexvalues of a component's children from leaking out and mixing with the rest of the page. Read more on MDN's stacking context page.
Practice exercise
Create a photo with a caption box that overlaps its bottom edge, and a tooltip that appears above both when you hover a small "i" icon. Make sure the tooltip is always on top.
Hint: give the photo wrapper position: relative, the caption position: absolute with z-index: 1, and the tooltip z-index: 2. Keep them all inside the same stacking context.
Key takeaways
z-indexcontrols which overlapping element is in front; higher numbers come forward.- It only works on positioned elements and on flex or grid items.
- Without
z-index, positioned elements are in front, and later HTML wins. - A stacking context groups elements; a child can never escape its parent's layer.
opacity,transformandfilteralso create stacking contexts.