Controlling the Stack: CSS z-index

Master the third dimension of web design. Learn how `z-index` and stacking contexts allow you to create rich, layered interfaces.

Lesson ProgressStep 1 of 9
Red
Blue
0 EXP

Hello! Let's explore the 'z-index' property. It controls how elements stack on top of each other.

/* Welcome to the 3D world of CSS! */

What is `z-index`?

The `z-index` property controls the vertical stacking order of elements that overlap. Think of your webpage as a stack of papers. The x-axis is horizontal, the y-axis is vertical, and thez-axis comes out of the screen toward you.

A higher `z-index` value (like `z-index: 10`) means an element is "closer" to you, appearing on top of elements with a lower `z-index` (like `z-index: 1`).

.top-layer {
  z-index: 2;
}
.bottom-layer {
  z-index: 1;
}

System Check

An element with `z-index: 5` will appear on top of an element with...?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Stacking Master

Correctly control the stacking order of elements.

🏗️
Position Aware

Understand the link between `position` and `z-index`.

✍️
Context Creator

Prove mastery of creating new stacking contexts.

Mission: Win the Stacking War

You have three overlapping boxes. The `.red` box has `z-index: 1` and the `.blue` box has `z-index: 2`. Modify the `.green` box to make it appear on top of all the others.

A.D.A. Feedback:

> System integrity looks stable. Code is valid.

Challenge: Order the Layers

Drag the CSS rules into the correct order to represent the visual stacking, from **top-most** (closest to you) to **bottom-most** (farthest away).

.middle { z-index: 5; position: relative; }
.top { z-index: 10; position: relative; }
.bottom { z-index: 1; position: relative; }

Challenge: Complete the Modal

Fill in the missing CSS properties to make a modal pop-up appear on top of its dark overlay.

.modal-content {
  position: ;
  z-index: ;
}
.modal-overlay {
  position: fixed;
  z-index: ;
}

Consult A.D.A.

Community Holo-Net

Peer Project Review

Submit your "Modal Project" for feedback from other Net-Runners.

Mastering the Third Dimension: A Deep Dive into CSS z-index

When you build a webpage, you're usually thinking in two dimensions: width (the x-axis) and height (the y-axis). But the web isn't flat. Elements constantly overlap, and controlling *which* element appears on top is a fundamental part of modern web design. This is the third dimension: the z-axis.

The CSS property that controls this is **`z-index`**. On the surface, it seems simple: give an element a higher `z-index` number, and it appears on top. However, `z-index` has a critical dependency that trips up nearly every new developer: the **Stacking Context**.

Rule #1: `z-index` Only Works on Positioned Elements

This is the single most common reason why your `z-index` isn't working. By default, all elements have `position: static`.

The `z-index` property has **absolutely no effect** on an element with `position: static`.

To make `z-index` work, you must first give the element a `position` value of:

  • `position: relative`: The most common choice. It doesn't take the element out of the normal document flow but *does* allow you to use `z-index` and offset properties (`top`, `left`, etc.).
  • `position: absolute`: Takes the element out of the document flow and positions it relative to its *nearest positioned ancestor*.
  • `position: fixed`: Takes the element out of the document flow and positions it relative to the viewport (the browser window).
  • `position: sticky`: A hybrid that acts like `relative` until it hits a specified scroll threshold, at which point it acts like `fixed`.

❌ Bad Practice

.my-element {
  /* This will NOT work */
  z-index: 10;
}

Missing a `position` property. `z-index` is ignored.

✔️ Good Practice

.my-element {
  position: relative;
  z-index: 10;
}

This element will now participate in stacking.

Rule #2: The All-Powerful Stacking Context

Now for the tricky part. You can't just compare the `z-index` of any two elements on a page. You can only compare the `z-index` of elements that live in the **same stacking context**.

Think of it this way:

  • Without Stacking Contexts: Imagine all your elements are loose papers on a desk. `z-index` is just the number you write on each one. The paper with "10" is always on top of the paper with "5".
  • With Stacking Contexts: Imagine your desk has several folders. Each folder is a **stacking context**. You can stack papers *inside* a folder (e.g., a paper with `z-index: 10` is on top of a paper with `z-index: 5` *within that same folder*).

But once you stack the folders themselves, **it doesn't matter what's inside**. If Folder A (`z-index: 1`) is below Folder B (`z-index: 2`), a paper with `z-index: 9999` inside Folder A can **never** appear on top of a paper with `z-index: 0` inside Folder B.

The entire "Folder A" stack is rendered, and *then* the entire "Folder B" stack is rendered on top of it.

The "z-index: 9999" Trap

<div class="parent-a">
  <div class="child-a"></div>
</div>
<div class="parent-b"></div>

<style>
.parent-a {
  position: relative;
  z-index: 1;
}
.child-a {
  position: absolute;
  z-index: 9999; /* TRAPPED! */
}
.parent-b {
  position: relative;
  z-index: 2;
}
</style>

In this example, `parent-b` will always be on top of `child-a`. `child-a` is trapped inside `parent-a`, and `parent-a` (`z-index: 1`) is stacked below `parent-b` (`z-index: 2`).

How to Create a Stacking Context

This is the secret. A new stacking context (a new "folder") is created by **more than just `z-index`**. An element creates a new stacking context if it has *any* of the following properties:

  • The root element ( <html> ).
  • An element with `position: absolute` or `position: relative` and a `z-index` value other than `auto`.
  • An element with `position: fixed` or `position: sticky`.
  • An element that is a flexbox (`flex`) or grid (`grid`) container's child, and has a `z-index` value other than `auto`.
  • An element with an `opacity` value less than `1`.
  • An element with a `transform`, `filter`, `perspective`, or `clip-path` value other than `none`.

That's right! Even setting `opacity: 0.99` on a parent element will create a new stacking context and can "trap" its children, causing unexpected `z-index` behavior.

The Stacking Order (Inside a Context)

Within a single stacking context, elements are painted in this exact order:

  1. The background and borders of the element that forms the context.
  2. Child elements with a **negative `z-index`** (e.g., `z-index: -1`).
  3. Child elements that are non-positioned (`position: static`).
  4. Child elements that are positioned (`position: relative`, etc.) with `z-index: auto` or `z-index: 0`.
  5. Child elements with a **positive `z-index`** (e.g., `z-index: 1`, `z-index: 10`).

This is why a `z-index: -1` element can appear *behind* its own parent. It's rendered at step 2, while its parent's background was rendered at step 1.

Key Takeaway: When your `z-index` isn't working, stop increasing the number. Instead, use your browser's dev tools to inspect the element's parents. Look for any property (`position`, `opacity`, `transform`, etc.) that is creating a new stacking context and "trapping" your element.

CSS `z-index` & Stacking Glossary

`z-index`
A CSS property that specifies the stack order of a positioned element. An element with a greater stack order (a higher `z-index` number) will always be in front of an element with a lower stack order.
Stacking Order
The order in which elements are rendered on the z-axis (perpendicular to the screen). This determines which elements appear on top of others when they overlap.
Stacking Context
A conceptual "group" of elements that share a common parent for stacking purposes. Once an element forms a stacking context, the `z-index` values of its children are only compared *within* that context and cannot "break out" to appear on top of elements in a different, higher context.
Positioned Element
An element whose `position` property is set to any value *other* than `static` (the default). The `z-index` property only applies to positioned elements.
`position: static`
The default `position` value for all elements. Elements with `position: static` ignore the `z-index` property completely.
`position: relative`
Makes an element a positioned element, allowing it to use `z-index`. It remains in the normal document flow. When combined with a `z-index` other than `auto`, it creates a new stacking context.
`position: absolute` / `position: fixed`
These values remove the element from the normal document flow and make it a positioned element. They always respect `z-index` and will create a new stacking context if `z-index` is any value other than `auto`.
`z-index: auto`
The default value for `z-index`. The element's stack order is set to 0 within its current context. It does *not* create a new stacking context (unless it's a flex/grid item).
Negative `z-index`
A `z-index` with a negative value (e.g., `z-index: -1`). This will place the element *behind* its parent element (which is at the `0` / `auto` level).
`opacity`, `transform`, `filter`
Properties that, when set to any value other than their defaults (e.g., `opacity: 0.9`), will also create a new stacking context, even without a `z-index`. This is a common source of stacking bugs.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching CSS and building robust and accessible web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest CSS specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!