How the CSS stacking context works

Marciano theguildPosted by Marciano Schildmeijer on 21-5-2019

Anyone who has ever worked with HTML and CSS may have heard of, or worked with the z-index property. What it does is pretty straightforward: It allows you to manage the stacking order of so called “positioned” HTML elements. Anything with a higher z-index number should always appear above elements with a lower z-index. However, sometimes you end up in situations where the z-index property doesn’t seem to have any effect at all and it isn’t that straightforward as it seems. In these situations it’s likely that you’re dealing with differences in the stacking context.

Default rules of stacking elements

When no positioned elements are applied, the basic rules of the element stacking order is as follows:

All respecting the order they are declared in the document.

Stacking with positioned elements

When elements are positioned, they appear above non-positioned elements. Many layouts consist out of positioned elements where controlling the stacking order of elements is required. A common use case is for instance a modal you want to show above the normal content of your page. To control the stacking order, z-index is used on positioned elements. Z-index won’t work on non-positioned elements. Applying the position properties with values like fixed and sticky, or absolute and relative in combination with a z-index value other than auto, will create a new local stacking context.

Taking (local) stacking contexts into account

Things get more tricky now. When stacking contexts are created on an element, that element will become a root stacking context element for its children elements. Documents can have many stacking contexts, so it is not rare to have stacking contexts inside other stacking contexts. The problem this creates is that children of the newly created stacking context won’t be able to render on top of an element in a different stacking context. Never ever. Even when the children will become positioned elements. Even with z-index: 999999;.

To overcome this problem, you can swap out these child elements in the current DOM hierarchy and bring them to the same level of the root stacking context (<html> element). Positioned sibling elements can be ordered with the z-index property. However in more complex DOM structures where many elements live inside each other, this may include some rework of your CSS or even JavaScript and may be more complex.


Implicit creation of stacking context

Another important thing to mention is that there are more implicit ways of how stacking contexts are created. Earlier in this post, I mentioned one way of how stacking contexts are created. There are more CSS properties that create their own stacking contexts implicitly. Unfortunately there is no clear way to see when a stacking context is created at a glance. However, there is a cool Chrome plugin that can be a bit of a help when identifying stacking context elements. It basically collects the properties that cause the stacking context for each element and outputs information about the element like this (shown in the right column, Z-index tab):


Properties that also create stacking contexts are (source from MDN):

Why these properties have side-effects like these, is unclear.

Solving it in when using React

Most React applications can consist of those more complex DOM hierarchies, which won’t allow you to swap out elements with their own stacking context so easily. However, React incorporated a neat addition called “Portals” in v16. Portals will allow you to mount any DOM element at any place in your DOM structure. React also mentions this in their documentation:

“A typical use case for portals is when a parent component has an overflow: hidden or z-index style, but you need the child to visually “break out” of its container. For example, dialogs, hovercards, and tooltips.“


I hope this helped you prevent future headaches by learning about stacking contexts and the way they interact with z-indexes. Here is a summary of this post:

Thanks for reading and good luck stacking!

Bij Kabisa staat privacy hoog in het vaandel. Wij vinden het belangrijk dat er zorgvuldig wordt omgegaan met de data die onze bezoekers achterlaten. Zo zult u op onze website geen tracking-cookies vinden van third-parties zoals Facebook, Hotjar of Hubspot. Er worden alleen cookies geplaatst van Google en Vimeo. Deze worden gebruikt voor analyses, om zo de gebruikerservaring van onze websitebezoekers te kunnen verbeteren. Tevens zorgen deze cookies ervoor dat er relevante advertenties worden getoond. Lees meer over het gebruik van cookies in ons privacy statement.