When building websites or more specifically, assets for websites, we tend to focus on building within the x
and y
axis of the web page. We position things from top to bottom and from left to right. This results in the misconception that developers can only place items within a 2D plane when building websites; however, a webpage also consists of a third dimension, the z
axis*. CSS gives developers the tools to access and manipulate all three dimensions, therefore, we can explicitly control the stacking order of HTML elements. We accomplish this by adding the z-index
property to our CSS rules. This property accepts a numerical value which can be a positive* or negative number*.
Consider the following:
Looking at the demo below, we can see that we have two boxes which are stacked on top of one another. Following the natural flow of HTML elements, we can see that .box2
is stacked on top of .box1
because of the translate property that we've defined.
What happens when we want .box1
to stack on top of .box2
? How would we go about solving that problem? Yes of course we could change the order of the HTML elements to solve the problem but are there any other ways?
In the styles.css
file, let's try defining a z-index
property on .box1
-- give it a try!!
.box1 { background-color: black; } .box2 { background-color: blue; translate: 15px -30px; }
We can see that by adding the z-index
property to .box1
we cause .box1
to stack on top of .box2
. Now you may wonder what happens if we add the z-index
property to .box2
but give it a higher value than the z-index
property in .box1
. Give it a try with the demo above!
Generally speaking, elements with a higher z-index value will appear on top. However, this is not always the case and we'll explore why shortly. It's also important to note that if no value for z-index
is set, the browser will use the document source order to dictate z-index
instead.
The z-index
property is one of the CSS rules that are most often misunderstood because when we talk about the z-index
we sometimes forget to talk about the important concepts that make the z-index
property possible. This results in a lot of frustration when trying to properly layer elements in web applications because sometimes the z-index
property does not behave as expected. In this article, we'll explore things like layers & groups, the stacking context, and how it all applies to the z-index
property.
Layers and Groups
Earlier we mentioned that the z-index
property introduces the idea of having layers within our web page. We identified that it gives us access to the third dimension -- z-axis -- thus allowing us to control how close or far an object is away from us.
If you've used graphics software such as Adobe Illustrator or Affinity Design, then the concept of layering assets to create a final image may be familiar to you. For those types of software, layers are an important tool that allow users to add components to an image and work on them independently without permanently changing the original image.
Users who are using layers in these types of software most often add one layer to adjust the colour & brightness of the image (layer 1) and then add another layer to add some special effects (layer 2). This gives users the ability to separate concerns so that the special effects that are added in layer 2 do not affect the change in colour or brightness found in layer 1. Talking about layers in this context automatically implies that a stacking order exists. We can see this demonstrated in the image below -- here we have the bottom-most layer, the middle, and then the top layer of the image.
The stacking order of the image can always be rearranged thus there could be a scenario where layer 2 is below layer 1 which is below layer 3. If that were the case, then it would look something like the image below.
These programs also give you the ability to group layers to prevent cluttering and give you a sense of organization in your layers. This means that if we create multiple groups then we now create a stacking order for our groups.
Let's say we had the following scenario:
Bottom
Group 2
Group 1
top
Looking at the above example we can re-order Group 1 and Group 2 so that Group 2 is now on top of Group 1. Doing this implies that all of the layers within Group 2 are now on top of the layers within Group 1. Now let's see what happens when we change the layer order within the groups. Drag the layers within each group and see how it affects the topmost layer output.
The important thing to notice here is that changing the layer order within the groups does not change the layer order of the groups. No matter how many times we change the order of D, E, and F, Group 2 will always be the topmost layer if it's on top of Group 1. In other words, any layers within group 2 will always show up on top of all the layers in group 1. Therefore, if we wanted layer B to be the topmost layer, then we would have to move Group 1 to the top of the horizontal stack and layer B to the top of the verticle stack. Give it a try in the example above!
When it comes to fully understanding the z-index
and the mysteries behind it, these concepts of layers and groups are the secrets that help us solve the conundrum.
Understanding the stacking context
So you've been working really hard to create this layered system but it seems like nothing is working!! Even the cheeky hack of setting the z-index
to a really high value such as 999999 doesn't seem to work. If you're unsure of what I'm referring to, here's a coding playground to check out.
.App { font-family: sans-serif; text-align: center; } .boxes { height: 50px; aspect-ratio: 1/1; border-radius: 5px; position: relative; } .container { position:relative; isolation:isolate; } .box1 { background-color: black; } .box2 { background-color: blue; translate: 15px -30px; z-index: 2; }
Here we have two boxes, .box1
and .box2
, which are overlapping. Right now .box2
overlapping .box1
but in order to change that we have to set the z-index
property value on .box1
to be higher than .box2
right?!?!? Let's try it -- did it work?
It's really weird because earlier we mentioned that elements that have a higher z-index
value would appear on top but in the above example, it doesn't seem to be the case. Seriously, what's going on here?!?!?
To understand this better, we first have to talk about the stacking context.
A stacking context is a group of elements that have a common parent and together this group moves up and down the z-axis. To put it another way, a stacking context is a space for layering to occur within a three-dimensional space. They are very similar to the groups/folders we were talking about earlier. This means that elements can exist within a stacking context and establish a stacking order -- ie an order to the layers. Within an individual stacking context, we can now begin to re-arrange the order of the elements and we do this using the z-index
property. Looking at the folder example from earlier, just as we could have multiple groups that are siblings of each other, we can have multiple stacking contexts that are siblings of each other.
An important thing to note is that the stacking context cannot be escaped by its children once it has been established on the parent. Just as the re-ordering of layers within an individual group did not change the order of the groups themselves, the re-ordering of layers within an individual stacking context does not change the order of the stacking context itself.
This is really crucial and it explains why adding a z-index
value of "99999999" sometimes doesn't work -- this is especially the case in our example above.
Knowing this, can you fix this example... Give it a try!!
.App { font-family: sans-serif; text-align: center; } .boxes { height: 50px; aspect-ratio: 1/1; border-radius: 5px; position: relative; } .container { position:relative; isolation:isolate; } .box1 { background-color: black; } .box2 { background-color: blue; translate: 15px -30px; z-index: 2; }
Adding a z-index
value of "99999999" doesn't work in this case because .box1
is trapped within a stacking context that is below .box2
. Let's see what happens when we add a z-index
property on the parent of .box1
*.
Hooray it works!!!
Creating new stacking contexts
I hope we've solved a lot of frustrations with the z-index
property by understanding how stacking context works. But a few questions still remain and one in particular is: How are new stacking contexts even created?
In short, a new stacking context is created whenever a position
property is set to a value other than static
and a z-index
property is defined. I say this with a grain of salt because it is more nuanced than what we've described.
What's the deal with position?
The position
property is one of those oddball CSS properties that is used to well position elements on a web page. It has 5 known values: fixed
, relative
, absolute
, sticky
, and static
.
A new stacking context is defined on an element that has a position
property set to either absolute or relative and has a z-index
property defined as well. We've seen this in the examples above, however, this is not the only way! Here are some others:
- Setting
opacity
to a value less than1
- Setting
position
tofixed
orsticky
(No z-index needed for these values!) - Adding a
z-index
to a child inside adisplay: flex
ordisplay: grid
container - Using
transform
,filter
,clip-path
, orperspective
- Using
will-change
with a value likeopacity
ortransform
(useful for animations) - Setting the value of
container-type
tosize
orinline-size
(useful for container queries) - Explicitly creating a context with
isolation: isolate
There are a few other ways to create new stacking contexts and you can find the full list on MDN.
Something to highlight
As we've seen above, there are many ways to create a new stacking context but a common misconception still exists.
POP QUIZ!!
We have to set the position property to either relative or absolute in order to use z-index
Let's check out the example below:
.App { font-family: sans-serif; text-align: center; display: flex; flex-direction: column; align-items: flex-start; } .boxes { height: 50px; aspect-ratio: 1/1; border-radius: 5px; position: relative; color: white; } .container { isolation: isolate; } .box1 { background-color: black; position: static; z-index: 3; } .box2 { background-color: blue; translate: 15px -30px; z-index: 2; }
In the example above we can see that we're using the z-index
property on an element that does not define a position
property. We can do this because this element's parent is a flex container. When we create a new flex-container, we are also creating a new stacking context thus flex-children have the ability to use the z-index
property even though their position property is set to static.
To sum up, a stacking context can be created in one of two ways:
- By creating a new region using the position property
- By creating a new composite layer
Wrapping up
The frustration with the z-index
property is one that I have personally battled with for a while so I empathize with anyone who has some battle scars!! The z-index
property is a really powerful tool that allows us to position elements within a three-dimensional space on the web. It does this as it allows us to explicitly control the stacking order of HTML elements, thereby enabling us to position elements close or further away from the viewer.
To fully understand the z-index
property, we first had to look at defining different layers and how that correlated with layers that were defined within groups. We found that changing the layer order within the groups did not change the layer order of the groups. This meant that if we created multiple groups then we now created a stacking order for those groups.
We explored the stacking context and how it relates to layers, groups, and the z-index
property. We found that we could have multiple stacking contexts and that the re-ordering of layers within an individual stacking context did not change the order of the stacking context itself. We finally solved why adding a z-index
value of "99999999" sometimes doesn't work!! YES!
To finish off, it's important to recognize that z-index
only works within a stacking context because its job is to re-order layers within a group or better yet, within a stacking context. Therefore, if no stacking context exists, then the z-index
property will not work.
All right, I'm going to wrap it up there! Hope you found this useful, and I'll catch you in the next one... Peace!
Practice problems
PSSSST! Hey you! Yaa you! Enjoyed the article?? Here's a fun little exercise for you to try out! 👀
Exercise
These are a series of questions or mini-games that are associated with the article you just finished reading! They are meant to help solidify the concepts talked about in this article. Have fun!
on this page
layers and groupsunderstanding the stacking contextcreating new stacking contextssomething to highlightwrapping uppractice problems