Managing z-index without loosing your hair

I’ve run into issues time and time again managing z-index. Sure, most of the time, z-index related issues are far from complex.

However, I’ve come to a passionate dislike of magic values and numbers. What is 58 or 3000 after all? It’s an arbitrary number. Wouldn’t it be a lot nicer to have layers in CSS, just like in Photoshop or Illustrator ?

And sure enough, a quick search revealed multiple articles around z-index management.

CSS Layers instead of Z-Index

After years of using Sass daily, I’ve switched over to Stylus (that probably deserves an article of it’s own), and sure enough – a quick search search revealed an already made Stylus mixin in a Github gist.

I’m using a slightly modified version of that.


Configure the layers.

The configuration file contains all the layers and their z-index. I’m not using dots or hashes in names, so I can avoid adding quotes as well. This makes it just a little bit tidier for my taste.

z_layers = {
// Generic
background: 1,
loading-cover: 5000,
progress: 5500,
// #header
Header: 800,
Footer: 50,
// #Wrapper
Wrapper: 30,
// Menu
Menu: 3000,

view raw
hosted with ❤ by GitHub


The z-index manager mixin

I’ve modified the mixin in a few ways:

  1. I’m calling it layer. Easier to type than z_index, and it makes sense to me.
  2. I’ve added unused_z_layers, which is going to help with debugging ( see below )
  3. I’ve also added adjust option to the mixin.
// Note, that `z_layer` variable must exist already, so this file has to be included after `layers.styl`
unused_z_layers = clone(z_layers)
layer(group, adjust = 0)
// Find unused z-layers
if group in unused_z_layers
used = remove(unused_z_layers, group)
z-index: z_layers[group] + adjust

view raw
hosted with ❤ by GitHub



The adjust option comes in really handy when dealing with local changes, without having to create a new layer for absolutely everything.

For example, if you have a layer for the menu, and you want to place a second div just under the menu, instead of having to create Menu: 500, Thing_Under_Menu: 490, I just use layer(Menu, -10).

So the usage looks something like this:

.Wrapper {
.Thing-5 {
layer(Wrapper, 10) // place .Thing-5 10 levels below .Wrapper

view raw
hosted with ❤ by GitHub

The Debug

Having a lot of  layers sometimes results in unused layers that are defined. This helps me keep track of that. In case

Stylus will already throw an error if I try to use layer(Mooooo) if Moooo doesn’t exist. That’s good.

However, what about the inverse? When Moooo is defined, but is unused? That’s where the debug comes in:

if( length(unused_z_layers) > 0 )
p( '—–' )
warn("Unused layers found")
p( '—–' )

view raw
hosted with ❤ by GitHub

That’s going to throw a console warning when an unused layer is found.

I always include this after everything else, and on each compile it’s going to check for unused layers.

That’s it.

It’s a really simple approach, but has helped me keep my sanity when managing z-index in complex layouts.



  1. Better solution is not to use z-indexes at all. You should add elements to correct location, instead of popping up some elements with any kind z-index value.

    1. For sure! If you can pull that off for your site – that’s great! You can also avoid taking your car to the repair shop if you don’t own one! 🙂

Leave a Reply

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