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 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
.
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:
- Z-index is applied in all contexts, so if you don’t define a context, it will use the DOM’s global context;
- As a standard, the HTML elements have a
position
property, defined asposition: static
; - 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, thez-index
property works without the need to change the standard positioning of the elements in thegrid
andflex
layouts; - If the
z-index
value is not assigned, the element will use the standard order of the DOM; - 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! :))