Z-index is a CSS property that controls the stacking order of positioned elements on a web page, determining which elements appear in front of or behind others when they overlap. While the X and Y axes govern horizontal and vertical positioning, z-index operates along the imaginary Z axis, which points toward and away from the viewer's screen.
Z-index accepts integer values, both positive and negative, as well as the keyword auto. An element with a higher z-index value will appear on top of an element with a lower value. For example, a modal dialog assigned z-index: 100 will render above a navigation bar with z-index: 10, assuming both elements share the same stacking context. A value of auto means the element does not establish a new stacking context and inherits the order of its parent.
One important prerequisite is often overlooked: z-index only works on elements that have a position value other than static, which is the default. Setting position: relative, absolute, fixed, or sticky on an element is required before z-index takes effect. Without this, the property is simply ignored by the browser.
Understanding Stacking Contexts
The most common source of confusion with z-index is the concept of a stacking context. A stacking context is an independent layer in which child elements are stacked relative to one another. Certain CSS properties trigger the creation of a new stacking context, including opacity values less than 1, transform, filter, will-change, and isolation: isolate, among others.
When a new stacking context is created, its children are confined within it. No matter how high a child's z-index value is set, it cannot visually escape its parent stacking context to appear above elements that belong to a sibling or ancestor context. This is why a developer might set z-index: 9999 on an element and still find it hidden behind another element with a seemingly lower value - the two elements exist in separate stacking contexts, and it is the z-index of their respective context roots that determines the final rendering order.
Z-index also interacts with the viewport in meaningful ways when working with fixed or sticky positioning, where elements are anchored relative to the visible area of the browser rather than the document flow. In these cases, managing stacking order becomes especially important to avoid overlapping headers, sticky sidebars, or floating UI components.
Practical Considerations
Keeping z-index values organized across a large codebase can become difficult without a clear system. Many teams define z-index values as named variables or design tokens, such as --z-modal: 300 or --z-tooltip: 400, to maintain a predictable and readable hierarchy. Avoiding arbitrarily large values like 9999 helps prevent conflicts and makes the stacking order easier to reason about as a project grows.