If you have struggled with z-index
you are not alone.
Look at the code snippet below:
The circle div
has a z-index
of 99999
but it is still showing under the header which has a z-index
of 2
. why is this happening? Should circle
be on top as it has the highest z-index
?
z-index
doesn't simply place element on top of lower z-index
or no z-index
items. There is a logic to it. If you understand this logic, then next time when you add a z-index property, you will know where to put it and why.
Lets dig into it:
z-index
is based on the stacking context of the HTML elements. Stacking context simply means how the elements are stacked in the HTML tree whether the elements are in same stacking context or not e.g in the code snippet above the stacking context of header
and main
is different than circle
. We can create new stacking context as soon as we add z-index properties to the elements.
There are also many other ways of creating stacking context. If you want to dig deeper you can read about it here
if we add z-index to header element then it will create a new stacking context comparing main
and header
. The circle z-index will have no effect as it has its own stacking context inside the main
element's context. But if we create a z-index on the circle div
then the context will be between it siblings ( if any ).
In order to solve the problem, all we need to do is remove the z-index from the main which will remove the new stacking context. Now it will simply be header
and then circle
in terms of stacking context.
Related Links:
Stacking Context
Stacking without z-index
I hope you enjoyed this article. Follow for more css tips and tricks.
Top comments (10)
Only if:
auto
ANDposition
value ofabsolute
orrelative
ORdisplay: flex
ORdisplay: grid
The
relative
inheader
doesn't seem to have a reason to exist. Once that is removed the problem goes away.In fact you can clean it up even further.
index: 2
inheader
can be removed.index: 1
inmain
can be removed, eliminating that stacking context. We have to keepposition: relative
in order for thecircle
absolute positioning to work.index: 99999
incircle
can be removed. Thediv
will automatically appear on top ofmain
because of HTML source order-later/nested elements will naturally stack on top of earlier ones within the same stacking context (Stacking without the z-index property).Now the only remaining stacking context is the one from the
html
root.This demonstrates that using
z-index
indiscriminately can cause more problems than it solves - especially as it is responsible for a new stacking context being created ondisplay: flex
,display: grid
,position: relative
/absolute
elements.For Firefox there is a CSS stacking context inspector add-on/extension. (Chrome devtools extension; github)
PS: Block Formatting Contexts are something entirely different.
Thanks for the feedback. I agree, however that was the usecase i was tackling. In essence that z-index will NOT bring the element to the top. Also added more links to the article.
Just wanted to mention that
transform
will also affect stacking context - which isn't mentioned in the article.The article does link to the stacking context which lists the various conditions under which a new stacking context is created.
I was focusing on the ones that involved
z-index
in one way or another - i.e.z-index
by itself isn't sufficient for creating new stacking context but it can cause one to be created when other specific declarations are already in place.Right, which makes sense. I just thought for the sake of this article/post it's worth mentioning as some may not realise that
transform
s can affect the context as well.Taking from ops example, let's say they want a fixed header and the main area to transition during route changes. Some may not expect adding a
transform
tomain
will change the stack: codepen.io/getreworked/pen/JjMrMZQ...Creating Stacking Contexts hints at why stacking contexts exist in the first place; some property values need to be contained and isolated to keep things manageable.
Interestingly the Incomplete List of Mistakes in the Design of CSS states that tables and
overflow: scroll
should have created their own stacking contexts as well.Whatever works for you—but I don't understand how that even works as a mental model.
Whenever things get a little more complicated it becomes necessary to locate the relevant stacking contexts to identify the positioned element (flex, grid item) where the
z-index
needs to be added/modified in order to obscure/reveal the desired portion of the layout.I find the first hurdle is understanding that
z-index
has no effect unless the element:All of which create their own local stacking context (in combination with
z-index
). Howeverz-index
doesn't work on stacking context roots created by other property values, e.g.opacity: 0.9
.The next hurdle is understanding that
z-index
only coordinates the ordering of stacking contexts within the same parent stacking context.Back to front ordering within a stacking context:
For example the stacking context hierarchy for Stacking context example 3
The reason the third to sixth
div.lev3
appears under the seconddiv.lev2
:div.lev3
are contained bydiv#container2
div#container2
is contained by the firstdiv.lev2
div.lev3
are contained by the firstdiv.lev2
div.lev2
's stacking context stacks in front of the firstdiv.lev2
which means it stacks on top of anything that it contains, including thosediv.lev3
sSo the
z-index: 10
on thediv.lev3
is entirely futile.However throwing this at the end of
style
:has the desired effect as it pulls the second
div.lev2
behind the first.Alternately
works as well as it stacks the first
div.lev2
in front the second one.One thing that's confusing about this example is that
div.lev1
s don't have separate stacking contexts while thediv.lev2
s do (due toopacity: 0.9
). Bothdiv.lev1
anddiv#container1
exist in the#document
stacking context butdiv#container1
'sabsolute & z-index: 1
creates a stacking context that stacks in front of non-positioned elements (div.lev1
).When each
div.lev1
creates it's own stacking contextThe problem happens all over again.
fixes it (but only because
position: relative
is already present).Thanks, it makes sense, if you think, that "circle" is part of "main", which is below "header". You could also change the "main z-index", at least it depends on what you want to do.
Of course it works all the time, you just need to know how to use it and it will always work.
It doesn't work when we dont know how to use it!
Nice article