z-index represents the depth of the DOM element in the browser. The higher the value closer the element will be to the user. Means elements with lower z-index will be pushed down in the stacking contexts. Is that it? There is more to z-index than just stacking elements. It can solve many layout problems if used correctly.

To use CSS z-index we need to specify a z-order number for the specified element. This will change the depth or z-order of a positioned element and its descendants. If not specified, the default value for the z-index is set to auto. Which will follow the natural order of stacking as per the order in which they are defined in HTML. This means The elements that are at the bottom of the HTML will get a higher z-index than the ones at the top of the code.

Syntax for z-index:

Selector{ z-index: <value>; }

<value>: It accepts any integer value within 32bit range that is : 0 - 2,147,483,647.

z-index property can have the following values:

  • auto – default value.
  • number – Integer value.
  • initial – initial value.
  • inherit – inherits value from immediate parent.

JavaScript Usage

document.getElementById('selector').style.zIndex = <value>;

Why use z-index:

Creating overlapping layers on the web across various browsers can be challenging sometimes. Especially if you're not the maker of the source. The below example shows how layers overlap when z-index is defined.

You see z-index makes it easy to overlap elements just by defining a z-index number for the element. when z-index is applied to any element, creates something known as the stacking context. To understand the stacking context, Let's first understand how natural stacking works and its problems.

The Natural Stacking Order:

As I mentioned earlier, elements with no z-index defined will get auto z-index and get a 0 value. But positioned elements with z-index will always get a higher z-index than non positioned elements. Non-positioned elements will have z-index stacked from bottom to the top - this is the natural stacking order of the DOM elements.

How the default source order works when no z-index is defined:

The children of a stacking context are stacked from bottom to top in order within the parent. There are some rules that can create a stacking context, this will move elements on top of the auto-valued elements and change the depth of the element. Each of these rules has precedence over other rules meaning rule #1 has less and rule #6 has the higher precedence so elements with rule #6 will be on top of other elements.

  1. Background and borders of the element that establish stacking context
  2. Elements with negative stacking contexts ordered as defined in HTML
  3. Non-positioned, non-floated, block-level elements, ordered as defined in HTML
  4. Non-positioned, floated elements, ordered as defined in HTML
  5. Inline elements ordered as defined in HTML
  6. Positioned elements, ordered as defined in HTML

Check the demo with natural stacking order. This is what you'd normally do when adding elements one by one. Drag the slider to decrease the amount of margin to overlap the elements.

You only have to check values for one class in this demo that is:

.layer{ margin:0 auto;}

In this demo the elements are defined as follows:

  1. Top Bun
  2. Veggies
  3. Cheese
  4. Beef
  5. Lattice
  6. Bottom Bun

A slider will change this layer position to -40px top and bottom so you'll see how elements naturally stacks and overlap with other elements.

The first burger is not something you expect with the demo. But this is natural. Layers are always stacked as they are defined in the source.

To fix the demo naturally,

We have to reverse the order of layers in the source. Imagine making a burger, we'll have to put the bottom-bun first, then everything else goes on top of that layer. Similarly, if we reverse the order in the source, it creates burger layers in the correct depth but they are visually reversed. In this demo, we have used negative margins to fix the stack, but negative margins don't always play well in all conditions.

We can hack depth sometimes with the help of the rules discussed above, some properties will create a stacking order for the element and move the element above the auto-valued elements.

We'll use the same burger example with fewer layers so this is more clear. In the below demo,

We are using the default source order with the properties that create stacking context. From the above rules, #1 and #2 are obvious. We need the background to see layers overlaying and layers with position and negative z-index will always be moved below auto layers. In this example, I have demonstrated how stacking order is created when we apply block, float, inline properties.

HTML Structure:

<div class="layer buntop" style="padding:0 10px;"><img src="bun.png" /></div>
<div class="layer veggies"><img src="veggies.png" /></div>
<div class="layer lettuce" ><img src="lettuce.png" /></div>
<div class="layer bunbottom" style="padding:0 10px;"><img src="bun_bottom.png" /></div>

 CSS Style:

.buntop{ position: relative;}
.veggies{ display: inline;}
.lettuce{ float: left; }

In this example, I have used the 4 rules discussed above to demonstrate the stacking - float, display, and position properties create stacking context. The position being with the highest order.

There is absolutely no change in source order. The code looks like how it appears visually. I'll call this method a hack. A more defined way to use stacking context is always using z-index.

Stacking context when z-index is defined:

We now know how the default source order works without a z-index. But when the z-index is defined, the rendering order of the element and its child is affected by that z-index value. This happens because the z-index enables special properties which cause them to form a stacking context.

The order of the stacking context is affected by some other CSS properties defined along with the z-index value. The element that has either of the following properties are defined, the element will get order accordingly:

  1. Root element of the document (<html>).
  2. position value absolute or relative.
  3. position value fixed or sticky.
  4. child of a flex container.
  5. child of a grid container.
  6. opacity value less than 1.
  7. mix-blend-mode value other than normal.
  8. properties with value: transform, filter, perspective, clip-path , mask / mask-image / mask-border
  9. isolation value isolate.
  10.  -webkit-overflow-scrolling value touch.
  11.  will-change value specifying any property that would create a stacking context on non-initial value.
  12.  contain the value of layout, or paint, or a composite value that includes either of them (i.e. contain: strict, contain: content).

Creating stacking contexts with other properties:

If we apply any of the properties defined below, It will create a stacking context, ordered by how they are defined in the source.

  • Opacity: Value less than 1 like 0.9999 will create a stacking context pushing other elements down.
  • Transform: Any kind of transform will create stacking context. This means just assigning default values will create a staking context for that element.
  • Clippath: When we add any shape of clippath to an element, It will push another element down.
  • Mix-blend-mode: Any mix-blend-mode other than default values will create stacking context.
  • Filter: By applying any valid filter, a new stacking context is created.
  • Perspective: By enabling any perspective value will create stacking context.
  • Mask: Any kind of mask will get that element on top of other elements.

You see the problem here, when we use any of these methods to overlay objects on top of one another, we have pretty limited ability. We can go only a few steps above in depth. Other elements will still follow the source order. So we can have limited layers of overlay without using z-index.

  1. Elements with natural source order. 1st Layer or It doesn't create any stacking context.
  2. Elements that are floated, inline, positioned without z-index. This method can have 4 layers at max.
  3. Elements that have either of the properties defined, as discussed above. It can have 1 layer.

So this method is not useful when we have layers of elements to be stacked. It's good for getting over a few elements. Having different properties to stack elements or overlay them is too confusing.

Debugging stacking context issues

If you are reading this for the first time, I assume you have thought z-index means order in z-axis from the viewer. The greater the index closer to the viewer. But it's a bit more complex than that. Sometimes, we run into a condition where you feel like the z-index is not working or It has no effect no matter how big the number you enter for the element. The element just can't overlap with another element.

What to do in this situation?

Step 1: You can debug the stacking issues with firefox or chrome dev tools. All the browsers have this element inspection tool. You need to inspect that element first.

debug z-index problem step-1

Step 2: Once you inspect the element, You need to search for the element in the code. You'll see a highlight in your browser window as you hover over the code. You need to select the element to debug.

debug z-index problem step-2

Step 3: Next step is to inspect the CSS properties and their values. If you know about the stacking context and z-index you can debug the element by turning off the z-index, increasing or decreasing the value depending on what you are trying to do.

debug z-index problem step-3

Step 4: You have to do this for the element itself moving towards the parent and up in the hierarchy doing the same process until the solution is found.

So if you know what to look for, it's just a matter of seconds to debug z-index related problems.

Why does z-index not work?

Some reasons why the z-index might not be working are:

  • Element's parent has the lower z-index. In this case, no matter how great the z-index of the child might be, it would never get over the element whose z-index is greater than the z-index of the element's parent.
  • The element doesn't have a position defined. This could be the case when the element itself or its parent does not have position property defined. If parents don't have position property, elements with higher z-index can overlay all the elements within their nearest relative element, root element otherwise.
  • There could be a position property problem. Elements with position elements defined will be stacked with precedence. So this means, the position value of static or sticky can't overlay the element with position relative or absolute until z-index is defined with the proper values. This means position fixed and static should have a higher z-index to overlap elements with the same z-index with position absolute or relative.

Bottom Line

Although z-index is not such a complex property, sometimes it's hard to debug overlapping elements in CSS. Having knowledge of stacking context and how they create stacking, definitely helps understand the problem better. Beginner developers could use these fundamentals to solve the beginner's problems. Pro developers know how to use stacking context and how to use z-index precisely in a scalable application.

Leave a Reply

Your email address will not be published. Required fields are marked *