Z index

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the CSS-and-design category.

Last Updated: 2024-03-28

The key to understanding z-index is to understand stacking contexts - essentially this means that there are different contexts within which z-index is active (akin to groups in photoshop or figma) and z-indexes internal to these groups cannot interact outside of these groups.

Thus "We shouldn't think of z-index purely as a way to change an element's order. We should also think of it as a way to form a group around that element's children. z-index won't work unless a group is formed." - Josh Comeau

Check out this CodePen example

You'll see there are two stacking contexts:

Even if you set the div child of main's z-index to infinity, it will appear under the header, at least so long as the header has a higher z-index than that div's container, main.

Reducing stacking contexts too A fix for this, that is almost Xen in its nature, is to remove the z-index on main, thereby turning what was once two stacking contexts into a single stacking contexts where the z-indexes can interact. However this won't help you at all if you need to position the parent element (main) too and cannot afford to remove its z-index.

Ordering with just position relative and negative margin offsets

You can change element ordering by adding position: relative to an element. It will appear above non-positioned elements even if these are later in the HTML.

See CodePen

Basics of using zIndex

Add both position and z-index styles

.over {
  position: relative; /* or absolute or fixed or sticky */
  z-index: 1
}

In general, z-index only works with "positioned" elements (elements that set position to something other than the default static) But the Flexbox specification adds an exception: flex children can use z-index even if they're statically-positioned.

E.g. here - with no position in side and just display flex in the wrapper, zIndex works.

<style>
  .wrapper {
    display: flex;
  }
  .second.box {
    z-index: 1;
    background: hotpink;
    margin-top: 20px;
    margin-left: -20px;
    margin-right: -20px;
  }
</style>

<div class="wrapper">
  <div class="first box"></div>
  <div class="second box"></div>
  <div class="third box"></div>
</div>

Default behavior of positioned elements without zIndex

If two positioned elements overlap without a z-index specified, the element positioned last in the HTML code will be shown on top. source

So how are stacking contexts created?

Pro-Tips

Sometimes the problem is not z-index at all - it is clipping. Try modifying (or removing) overflow properties to see if that helps.

Try out the isolation property. It allows you to seal off components from z-Index interactions elsewhere - but without you needing even change away from position static. The key problem it solves is that normally when you set a zIndex on something to create a stack context, you are also roped into choosing a number for your zIndex and this has interaction effects with sibling and parent elements or your chunck of code. See here

You might use apply this property to the parent wrapper , then reverse actual z-index values for internally.


.wrapper {
  isolation: isolate;
}

Tools

There are chrome extensions to debug stacking contexts - e.g. this