z-index: of CSS can be very confusing…
You set it to a new value and nothing happens. No matter what value you choose, it seems to have no effect. You try larger and larger
z-index: values in a vain attempt to make your element appear on top.
“Why is it not working?” I hear you scream…
But do not despair!
z-index is not that difficult to understand once you understand its core concepts.
This blog post is a short dive into how it works.
z-index: is a property of HTML elements, and is used to control the stacking order of those elements.
It determines the position of an element on the ‘z-axis’, which can be thought of as the order in which elements appear in front of or behind each other.
You can also think of it as the position of the element coming out of the screen towards the viewer.
HTML elements can overlap
div HTML elements on a page, A and B.
<div style="background-color: seashell; ...">A</div> <div style="background-color: crimson; ...">B</div>
Now we can force one to overlap over the other by setting a negative top margin on B:
<div style="background-color: seashell; ..."> A </div> <div style="background-color: crimson; margin-top: -1rem; ..."> B </div>
But what determines which div should be on top in this case?
The order in which they appear in the HTML source code determines how they overlap.
Specifically, elements defined further along or later in the HTML source code will sit above ones that where defined earlier.
In this case B above A.
But more detail later on…
Setting the z-index to some value
What is we wanted div A to be on top of B in our example above?
If you want to be explicit about the order in which elements ‘stack’ on the page, you need to set
z-index: to a relevant value
z-index: can be set to a positive or negative integer (or
But what number should you set it to?
And why does it sometimes not seem to work?
The z-index only applies to elements in certain situations
Lets look at our previous example of divs A and B.
If we set the z-index on A to some number higher than on B we might expect A to now sit on top:
<div style="z-index: 10; ..."> A </div> <div style="z-index: 0; margin-top: -1rem; ..."> B </div>
But that didn’t change anything!
Let’s start to explore why…
In order for the z-index property to have an effect on an element, the element must either:
- have it’s
position:attribute set to either
sticky, but not
- or use the
Why? More on that later.
The catch is that
static is the default
position: mode. So in our example above A is
Thus if you only specify a
z-index: without setting the
display mode, it will have no effect.
That’s the first reason why your
z-index: might not be working!
Let’s try our example again but set
position: relative on A:
<div style="z-index: 10; position: relative; ..."> A </div> <div style="z-index: 0; margin-top: -1rem; ..."> B </div>
Success! A is now on top of B.
And the z-index is relative to the ‘stacking context’
But there is another thing…
Many people don’t realize that the
z-index: value is relative to a specific context (grouping of HTML elements).
So to understand how the elements will stack requires an awareness of the so called ‘stacking context’ of the element.
The Stacking Context
A stacking context is a group of elements that move together as a group along the z-axis.
z-index: value of any elements in the stacking context group, is only related to the other elements in that same group.
The parent element of the group then determines the stacking order of the group as a whole.
Let’s repeat that.
Setting an element’s
z-index: higher than any of its siblings elements (elements in the same ‘stacking context’) will
stack it on top of those siblings.
But no matter the
z-index: value, the element will not stack relative to any elements outside of its group.
The parent element’s
z-index: will determine where the group as a whole sits, relative to other elements around it
(ie relative to the parent element’s ‘stacking context’).
Creating a ‘stacking context’
Consider back to our previous example with divs A and B. First B was pushed over A with a negative margin:
We then set the
relative for A, and that caused it to be on top of B.
Ok now let’s try set the
position: of B to
<div style="z-index: 10;..."> A </div> <div style="z-index: 0; position: relative; margin-top: -1rem; ..."> B </div>
Hm… B is on top of A again. The z-index of A has not caused it to stack on top.
Look carefully however and you will notice that this time the B element actually “hides” the text of the A element.
It is no longer overlapping the A element, but rather it is covering it.
Its subtle, compare it to the first example where B was pushed over A with a negative margin:
So what does this mean?
It means that the
position: relative attribute on B has caused it to create a new ‘stacking context’ for itself. This makes the renderer
consider B as “stackable”. The elements are no longer just overlapping. B is now “stacked” on top of A.
A on the other hand is not stackable because it is not in a ‘stacking context’. Thus the z-index of A is not relevant to the stacking order of B!
Now let’s make A join B in the same ‘stacking context’.
If we set the
position: of A to
relative also, so both are now positioned relatively to their parent, then they end up
being siblings in the same ‘stacking context’ and the z-index of A is now relevant to the stacking order of B.
<div style="z-index: 10; position: relative; ..."> A </div> <div style="z-index: 0; position: relative; margin-top: -1rem; ..."> B </div>
And you guessed it, B is now on top of A.
Other ways to create a ‘stacking context’
Certain CSS properties force an element to have a new ‘stacking context’.
position: as mentioned earlier, but also
transform: & others.
You can also explicitly create a grouping using the
isolation: isolate property, which is useful if
you don’t want or need to use any of the other properties!
For example, instead of using
position: relative on both elements, we can use
isolation: isolate on both instead:
<div style="z-index: 10; isolation: isolate; ..."> A </div> <div style="z-index: 0; isolation: isolate; ..."> B </div>
A “stacking context” is required for z-index to work
So thinking back to earlier in this article, we said that the
z-index: value of an element is only applied
if say the element has
position: relative or other attributes set.
Now it makes sense why. By setting the
position: attribute to
relative we are creating a new ‘stacking context’ for the element.
Initially, when all we did was set
z-index: values, the elements were not in a ‘stacking context’ and so the
z-index: values were ignored.
The default ‘stacking order’
By default, elements in a stacking context that have no z-indexes set, are stacked in the order they appear in the HTML document.
This is known as the
auto z-index mode.
Since it is the default you normally don’t specify it, but if you did, (for example to override a value set by another CSS declaration)
you would do
The z-index at work: an example
Let’s look at a more complex example:
<div style="z-index: 1; isolation: isolate; ..."> <div style="z-index: 10; position: relative; ..."> A </div> <div style="z-index: 1000; ..."> B </div> </div> <div style="z-index: 3; isolation: isolate; ..."> C </div>
What do you think the stacking order will be?
Answer: C on top, A in the middle, B at the bottom.
Why: Because C is in a stacking context with the parent element of A and B. They are siblings and the one with the highest
z-index: sits on top (ie C is over the group of A and B).
So the 2 child elements A and B stack according to their parent when considered to div C.
Now inside that first div however, even though B has a much larger
z-index: value that A, A actually creates
a new stacking context (as it is
position: relative) but B does not (in fact is it
position: static by default).
So B is not in a stacking context and the
z-index: of B is not relevant!
For an article specifically about z-index go to Z-index and stacking contexts on web.dev and for one on stacking contexts go to Stacking Contexts on Josh Comeau’s blog. Both of these articles are excellent!
I highly recommended Josh Comeau if you want to learn about CSS. His articles are understandable, visual, well researched and dive deep into the topic.
Also checkout web.dev as another excellent source.
Found that useful? Subscribe for more
Keep up to date with all my content by subscribing to my very low traffic and zero spam substack.