Z-index and stacking contexts

The CSS Podcast - 019: z-index and stacking contexts

Say you've got a couple of elements that are absolutely positioned, and are supposed to be positioned on top of each other. You might write a bit of a HTML like this:

<div class="stacked-items">
    <div class="item-1">Item 1</div>
    <div class="item-2">Item 2</div>
</div>

But which one sits on top of the other, by default? To know which item would do that, you need to understand z-index and stacking contexts.

Z-index

The z-index property explicitly sets a layer order for HTML based on the 3D space of the browser—the Z axis. This is the axis which shows which layers are closer to and further from you. The vertical axis on the web is the Y axis and the horizontal axis is the X axis.

Each axis surrounding the element

The z-index property accepts a numerical value which can be a positive or negative number. Elements will appear above another element if they have a higher z-index value. If no z-index is set on your elements then the default behaviour is that document source order dictates the Z axis. This means that elements further down the document sit on top of elements that appear before them.

In normal flow, if you set a specific value for z-index and it isn't working, you need to set the element's position value to anything other than static. This is a common place where people struggle with z-index.

This isn't the case if you are in a flexbox or grid context, though, because you can modify the z-index of flex or grid items without adding position: relative.

Negative z-index

To set an element behind another element, add a negative value for z-index.

.my-element {
    background: rgb(232 240 254 / 0.4);
}

.my-element .child {
    position: relative;
    z-index: -1;
}

As long as .my-element has the initial value for z-index of auto, the .child element will sit behind it.

Add the following CSS to .my-element, and the .child element will not sit behind it.

.my-element {
  position: relative;
  z-index: 0;
  background: rgb(232 240 254 / 0.4);
}

Because .my-element now has a position value that's not static and a z-index value that's not auto, it has created a new stacking context. This means that even if you set .child to have a z-index of -999, it would still not sit behind .my-parent.

Stacking context

A stacking context is a group of elements that have a common parent and move up and down the z axis together.

In this example, the first parent element has a z-index of 1, so creates a new stacking context. Its child element has a z-index of 999. Next to this parent, there is another parent element with one child. The parent has a z-index of 2 and the child element also has a z-index of 2. Because both parents create a stacking context, the z-index of all children is based on that of their parent.

The z-index of elements inside of a stacking context are always relative to the parent's current order in its own stacking context.

Creating a stacking context

You don't need to apply z-index and position to create a new stacking context. You can create a new stacking context by adding a value for properties which create a new composite layer such as opacity, will-change and transform. You can see a full list of properties here.

To explain what a composite layer is, imagine a web page is a canvas. A browser takes your HTML and CSS and uses these to work out how big to make the canvas. It then paints the page on this canvas. If an element was to change—say, it changes position—the browser then has to go back and re-work out what to paint.

To help with performance, the browser creates new composite layers which are layered on top of the canvas. These are a bit like post-it notes: moving one around and changing it doesn't have a huge impact on the overall canvas. A new composite layer is created for elements with opacity, transform and will-change because these are very likely to change, so the browser makes sure that change is performant as possible by using the GPU to apply style adjustments.

Resources

Check your understanding

Test your knowledge of z-index

<section>
  <article>1</article>
  <article>2</article>
  <article>3</article>
  <article>4</article>
</section>

Which article is on top by default?

1
It's in the very back.
2
Try again!
3
Try again!
4
Last in the document sits on top yep!

If z-index isn't working, what property should you inspect on your element?

display
Not the likely property for why z-index isn't working.
relative
This is a CSS value, not a property.
position
Make sure this is set to something other than static.
animation
Not the likely property for why z-index isn't working.

Do flexbox and grid need position: relative?

Yes
These display types don't need it.
No
Using z-index inside a flexbox or grid layout will work without position: relative.