Who's not Afraid of z-index-cover.png
Daniel Carvalho

Daniel Carvalho

15 Dec 2022 6 min read

Who's not Afraid of z-index?

Before I found out, thanks to CSS for JS Devas class, that the z-index had a logic behind it, I used to always fall into the z-index hell, trying to put one element on top of the other. This drove me to desperate measures, like the z-index: 9999999999999, and that’s why in this post I’ll show you good and bad practices with z-index, so that you can finally go to z-index heaven :)

What is it

Z-index is a CSS property that we use to alter the standard order of the HTML layers. The standard behavior of the browser is to render the next element in front of the last one. This way, the div with the class box__1 will be in the last layer of the pile while the div with the class box__3 will be on the first layer of that pile.

Div stack following the natural DOM order

<div class="box box__1">1</div>

<div class="box box__2">2</div>

<div class="box box__3">3</div>

Imagine that the HTML is a pile of layers and that z-index is used to set a new order for the elements in this stack. This means that, the higher the z-index value, the higher their position on the stack, therefore the more in front it is going to appear.

<style>
    .box{ position: relative }

    .box__1 { z-index: 1 }

    .box__2 { z-index: 1000 }

    .box__3 { z-index: 2 }
</style>

<div class="box box__1" >1</div>
<div class="box box__2">2</div>
<div class="box box__3">3</div>

The code above will result in something like the image below, because the div, with the class box__2 has the higher z-index.

Div stack following the <code>z-index</code> order

Where and when to use it

In the z-index hell, you’ll find a situation where not all elements respect the position you defined, as the codepen below.

In a situation like this, we usually enter into a duel with the z-index values ranging from 100 to some random gargantuan number that you chose to try to fix the conflict in the element’s order. To really fix it, you have to understand that:

  1. Z-index is applied in all contexts, so if you don’t define a context, it will use the DOM’s global context;
  2. As a standard, the HTML elements have a position property, defined as position: static;
  3. To apply the z-index, it is mandatory to change the positioning of the elements to one of the following: position: relative | absolute | sticky | fixed. Besides that, the z-index property works without the need to change the standard positioning of the elements in the grid and flex layouts;
  4. If the z-index value is not assigned, the element will use the standard order of the DOM;
  5. The highest possible value accepted by the z-index property is 2147483648, in other words, the highest number that can be stored in 32bits. If a value is higher than that, it will be ignored.

Bad uses

In a large application, z-index can be an even larger problem, because you can have several elements with the z-index explicitly declared. In this case, you might affect some other element when trying to alter these z-index’d elements.

Usually in this situation my approach used to be trying to use random values until I found something that fit perfectly in the context. This was a real work around, that relied on luck and made get to situations like the one below.


Good uses

But you don’t have to go through that, there are paths to avoid these problems! Down here, I’ll show you different approaches to avoid the z-index hell.

Isolation: isolate

The isolation property is supported by all browsers, except IE. As a standard, its value is auto, allowing the z-index of the sons of an element to mix with other elements from the DOM.

When we use the value isolate, we start isolating the z-index of the sons of that element in its own context. With that, you can declare, for example, z-index: 1000 in a son and this value will be considered only on the pile of elements in the same context, avoiding the problem above. In the code below, I isolate the header and post elements in a context so that they don’t stay in front of the modal. See in the codepen:


CSS variables

Another approach broadly used is the creation of variables for the z-index layers. This approach is used by Bootstrap and is based on the creation of “breakpoints” that guide the elements' priority in the layers of the z-index. For example, a modal that needs to be on top of a dropdown, its variable will have a larger value that the one of the dropdown. This prevents the team uses random values.

/* css variables */
$zindex-flow: 100;
$zindex-modal-backdrop: 1040;
$zindex-modal: 1060;

/* css variables */
--zindex-flow: 100;
--zindex-modal-backdrop: 1040;
--zindex-modal: 1060;


Tailwind approach

Tailwind uses the approach of applying classes to the element that needs an explicit z-index. With it, the team will have a clear view of the position of the element in the pile with indexes that go from z-0 to z-50 as a standard, able to be overwritten using z-[100].

$zIndex: (
  "0": 0,
  "10": 10,
  "20": 20,
  "30": 30,
  "40": 40,
  "50": 50,
  "60": 60,
  "70": 70,
  "80": 80,
  "90": 90,
  "100": 100,
  "auto": "auto",
);

@each $index, $level in $zIndex {
  .z-#{$index} {
    `z-index`: $level;
  }
}


Portals

Portals is an approach available in several frameworks, like Angular, and libraries, like React, to deal with z-index problems. It is an approach more recommended to UI libraries that use dropdown, modal and other priority components in the z-index. So you probably won’t need to use this kind of solution in your projects, but it is still important to know that it exists. It creates a div below the standard div in which the content is rendered, assigns the rule position: relative to that div and a z-index with a high value. On React, this value is 999.


Conclusion

Is there any questions that you have? Do you know a different approach from the ones I talked about here? Feel free to comment below and let’s get talking! :))

If you want to read more about z-index, these were some of my references: