Z-Index 99999: A CSS Conundrum

Unlocking the mysteries behind the CSS Stacking Context

Oct 23rd, 2023

css

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.

layer 3
layer 2
layer 1

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.

layer 3
layer 1
layer 2

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

Layer D
Layer E
Layer F

Group 1

Layer A
Layer B
Layer C

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.

Show more

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:

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:

  1. By creating a new region using the position property
  2. 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

Last updated October 23rd, 2023