Introduction
In CSS, we’re given a device to explicitly management the stacking order of HTML components: z-index
. Parts with the next worth will seem on prime:
Code Playground
As a result of .first.field
has a bigger z-index than .second.field
, it stacks in entrance. If we take away that z-index declaration, it falls to the again. The code above is editable—give it a shot!
Issues aren’t all the time so easy, nonetheless. Generally, the bigger z-index worth does not win.
Take a look at what is going on on right here:
Code Playground
.tooltip
has a a lot bigger z-index than header
! So why on earth is the header on prime?
To unravel this thriller, we’ll must find out about stacking contexts, an obscure-yet-fundamental CSS mechanism. On this article, we’ll discover what they’re, how they work, and the way we will use them to our benefit.
In the event you’ve ever used image-editing software program like Photoshop or Figma, you are most likely accustomed to the idea of layers:
Our picture has 3 separate canvases, stacked like pancakes. The underside layer is a cat picture, with 2 layers on prime that add foolish particulars. By flattening these layers, we wind up with a last composition:
In these packages, we will additionally group layers:
Like information in a folder, a gaggle permits us to phase our layers. When it comes to stacking order, layers aren’t allowed to “intermingle” between teams: All of canine
‘s layers will seem on prime of all of cat
‘s layers.
After we export the composition, we do not see the cat in any respect, because it’s behind the canine:
In the case of CSS, issues work in an identical approach: components are grouped into stacking contexts. After we give a component a z-index, that worth is simply in contrast in opposition to different components in the identical context. z-index values should not world.
By default, a plain HTML doc may have a single stacking context that encompasses all nodes. However we will create extra contexts!
There are various methods to create stacking contexts, however this is the most typical:
By combining these two declarations, a secret mechanism is triggered: a stacking context is created, forming a gaggle round this ingredient and all of its kids.
Let’s take one other have a look at our downside from above:
We are able to map out the stacking contexts being created on this snippet:
Our .tooltip
ingredient has a z-index of 999999, however that worth is simply related inside the <primary>
stacking context. It controls whether or not the tooltip reveals up above or under the adjoining <p>
tag, nothing extra.
In the meantime, within the dad or mum context, <header>
and <primary>
are in contrast. As a result of <primary>
has a smaller z-index, it reveals up beneath <header>
. All of its kids come alongside for the trip.
How will we resolve our tooltip downside? Nicely, on this case, we do not really must create a stacking context on our <primary>
:
Code Playground
With no z-index, <primary>
will not create a stacking context. Our hierarchy, then, appears like this:
-
The foundation context
-
<header>
-
<div class="tooltip">
-
As a result of the header and our tooltip are actually in the identical context, their z-index values face off, and the tooltip emerges because the victor.
An essential distinction: we’re not speaking about dad or mum/little one relationships right here. It does not matter that the tooltip is extra deeply nested than the header. The browser solely cares about stacking contexts.
We have seen how we will create a stacking context by combining relative or absolute positioning with z-index
, however it’s not the one approach! Listed below are some others:
-
Setting
opacity
to a price lower than1
-
Setting
place
tofastened
orsticky
(No z-index wanted for these values!) -
Making use of a
mix-blend-mode
aside fromregular
-
Including a
z-index
to a baby inside ashow: flex
orshow: grid
container -
Utilizing
remodel
,filter
,clip-path
, orperspective
-
Utilizing
will-change
with a price likeopacity
orremodel
-
Explicitly making a context with
isolation: isolate
(Extra on this quickly!)
There are a number of different methods as nicely. Yow will discover the total checklist on MDN.
This could result in some shocking conditions. Take a look at what’s occurring right here:
Code Playground
primary
does not set a z-index anymore, however it makes use of will-change
, a property that may create a stacking context all by itself.
To ensure that z-index to work, we have to set place
to one thing like relative
or absolute
, proper?
Not fairly. Take a look at what’s occurring right here:
Code Playground
The second field is lifted above its siblings utilizing z-index
. There are not any place
declarations wherever within the snippet, although!
Normally, z-index
solely works with “positioned” components (components that set place
to one thing aside from the default “static”). However the Flexbox specification provides an exception: flex kids can use z-index
even when they’re statically-positioned.
An earlier model of this publish mentioned that each one components that create a stacking context can use z-index
, however that was incorrect. 😬
There is a Bizarre Factor right here, and I believe it is value pondering about for a minute or two.
In our Photoshop analogy, there’s a clear distinction between teams and layers. All the visible components are layers, and teams might be conjured as structural helpers to comprise them. They’re distinct concepts.
On the net, nonetheless, the excellence is a bit much less clear. Each ingredient that makes use of z-index should additionally create a stacking context.
After we resolve to offer a component a z-index, our purpose is usually to carry or decrease that ingredient above/under another ingredient within the dad or mum stacking context. We aren’t intending to provide a stacking context on that ingredient! However it’s essential that we take into account it.
When a stacking context is created, it “flattens” all of its descendants. These kids can nonetheless be rearranged internally, however we have basically locked these kids in.
Let’s take one other have a look at the markup from earlier:
By default, HTML components shall be stacked in response to their DOM order. With none CSS interference, primary
will render on prime of header
.
We are able to carry header
to the entrance by giving it a z-index, however not with out flattening all of its kids. This mechanism is what led to the bug we mentioned earlier.
We should not consider z-index
purely as a approach to change a component’s order. We must always additionally consider it as a approach to kind a gaggle round that ingredient’s kids. z-index will not work until a gaggle is shaped.
Hyperlink to this heading
Hermetic abstractions with “isolation”
Considered one of my favorite CSS properties can be one of the obscure. I might wish to introduce you to the isolation
property, a hidden gem within the language.
Here is the way you’d use it:
After we apply this declaration to a component, it does exactly 1 factor: it creates a brand new stacking context.
With so many various methods to create a stacking context, why do we’d like one other one? Nicely, with each different methodology, stacking contexts are created implicitly, as the results of another change. isolation
creates a stacking context within the purest approach attainable:
-
No must prescribe a z-index worth
-
Can be utilized on statically-positioned components
-
Does not have an effect on the kid’s rendering in any approach
That is extremely helpful, because it lets us “seal off” a component’s kids.
Let’s take a look at an instance. Lately, I constructed this neat envelope part. Hover or focus to see it open:
It consists of a number of layers:
I packaged this impact up in a React part, <Envelope>
. It appears one thing like this (inline kinds used for brevity):
(In the event you’re questioning why Flap
has a dynamic z-index, it is as a result of it must shift behind the letter when the envelope is open.)
React part is sealed off from its setting, like a spacesuit. This spacesuit, nonetheless, has sprung a leak. Take a look at what occurs once I use it close to a <header>
with z-index: 3
:
Our <Envelope>
part wraps the 4 layers in a div, however it does not create a stacking context. Because of this, these layers can change into “intertwined” with different elements, just like the world’s most boring recreation of Tornado.
By utilizing isolation: isolate
on the top-level ingredient inside <Envelope>
, we assure that it will be positioned as a gaggle:
Why not create a stacking context the old school approach, with place: relative; z-index: 1
? Nicely, React elements are supposed to be reusable; is 1
actually the fitting z-index worth for this part in all circumstances? The fantastic thing about isolation
is that it retains our elements unopinionated and versatile.
Increasingly more, I am beginning to imagine that z-index is an escape hatch, much like !essential
. That is one trick that enables us to manage stacking order with out pulling the large pink z-index lever.
I am engaged on a follow-up tutorial the place we have a look at another methods to maintain z-index inflation down. Watch this house!
Sadly, I have not discovered a lot tooling to assist debug stacking-context points.
Microsoft Edge has an fascinating “3D view” that enables us to view stacking contexts:
That is an formidable concept, however truthfully I discover it fairly overwhelming. It is laborious to find a particular ingredient on this view, and I do not actually really feel prefer it helps me perceive the stacking contexts in my app.
There’s one different neat trick you should utilize typically: offsetParent
.
offsetParent
returns the closest ancestor rendered with a place
worth aside from static
. It crawls up the tree on the lookout for relative / absolute / fastened / sticky ancestors.
This isn’t an ideal answer. Not all stacking contexts use positioned structure, and never all positioned components create a stacking context! That mentioned, in apply, there does are usually a reasonably sturdy correlation between these two issues. On the very least, it is a place to begin.
If you realize of any tooling that may assist right here (or for those who create one!), let me know on Twitter
Replace: Felix Becker reached out to share a VSCode extension that highlights when stacking contexts are created:
This extension works on .css and .scss information (no CSS-in-JS assist).
Replace 2: Giuseppe Gurgone reached out to let me find out about this Chrome extension which provides a brand new “z-index” pane to the devtools.
Replace 3: Andrea Dragotta created an unbelievable browser extension that provides a bunch of super-important details about z-index and stacking contexts:
That is an superior device, and I have been utilizing it usually. Set up CSS Stacking Context Inspector:
Stacking contexts are a great instance of how CSS is constructed on “hidden mechanisms”. You possibly can spend years constructing interfaces with CSS with out realizing that they exist.
Except you explicitly take the time to find out about these mechanisms, your psychological mannequin will all the time be lacking items. And in case your psychological mannequin is even barely misaligned, it is solely a matter of time till that discrepancy causes issues.
CSS does not have warnings or error messages. When one thing shocking occurs, there isn’t any clear “subsequent step” to determine what went improper. These disruptions take us out of stream state and shake our confidence. I believe because of this so many front-end builders do not get pleasure from writing CSS.
When you construct up an instinct for the language, although, CSS turns into an absolute pleasure. I love writing CSS these days.
I need to assist different builders uncover this pleasure. I’ve created a complete self-paced on-line course that explains how CSS works at a deeper degree, and teaches the sensible abilities I take advantage of every single day to construct every kind of person interfaces.
It is referred to as “CSS for JavaScript Builders”, and it is accessible now. 😄