A Better Solution for Managing z-index with Sass

Share this article

Lately we have seen quite a few articles about managing z-index with Sass:

While that last article from Chris Coyier certainly helped me get started with my solution, none of the previous articles succeeded, from a technical standpoint, in presenting a simple and easy to use method.

Hence this article. I will walk you through my very simple system, showing you how I built it and how you can use it in your projects today.

Why the Need to Manage z-index Values?

First let’s answer this question: why do we need to manage z-index with Sass? We don’t see countless articles popping up about managing padding with Sass, right? So why do it for z-index?

Well, it has to do with the fact that z-index is mostly misunderstood. Moreover, accepting a unitless value between -infinity and infinity gives you plenty of occasions to screw things up.

So rather than using values like 9999999 and yelling at your screen because you don’t understand what’s going on, it may be interesting to use an extra tool — a CSS preprocessor in this case — to help improve things.

What’s the Concept?

The concept here is mostly the same in all articles on this topic: Build a function that accepts a keyword as an argument, which is mapped to a value. This value is the z-index that will be printed out.

Jackie Balzer uses lists. Doug Avery uses a mix of lists and maps. Chris Coyier uses maps. Actually, Chris Coyier’s solution is really close to what I have ended up with, except it lacks this little extra that makes the whole system great to use.

Ultimately we’ll come to this:

.element {
  z-index: z("modal");
}

Why Use a Function and not a Mixin?

Doug Avery uses a mixin in his article. The major difference with a function is the way you use it:

// With a mixin
// Me no likey!
.element {
  @include z("modal");
}

While I understand this is really a matter of preference, I really tend to avoid using mixins when there is only a single CSS property being output. The function is a perfect use case for something like that.

Opening the Beast

So we need a function accepting a single argument. Feel free to call it anything; I went with layer.

@function z($layer) {
  // ... where the magic happens.
}

What is the function doing anyway? It will look for the given argument in a layers map to see if it is mapped to a z-index value. If it is, it returns the value, else it returns an error message. So we need a map.

$z-layers: (
  "goku":            9001,
  "shoryuken":       8000,
  "default":            1,
  "below":             -1,
  "bottomless-pit": -9000
);

Two things here:

  1. I like to have my configuration variables out of the mixins/functions using them, in a dedicated file (e.g. _config.scss). Feel free to move it inside the z() function it you prefer.
  2. You can add/remove/update as many keys/values as you want, those are just examples.

Now back to our function.

@function z($layer) {
  @return map-get($z-layers, $layer);
}

At this point, we haven’t done much more than syntactic sugar for map-get($z-layers, ...). That’s actually pretty cool, because typing this thing over and over can quickly become annoying.

If the key exists in the map, it will return the index value mapped to it. If the key has not been defined, then it will return null. Whenever a property has a null value, the Sass compiler doesn’t output it.

So in case you call for an unknown key, Sass will just fail silently, which is not ideal. Let’s improve this by using the @warn directive to alert the developer (that’s you) in case the key doesn’t exist in the map:

@function z($layer) {
  @if not map-has-key($z-layers, $layer) {
    @warn "No layer found for `#{$layer}` in $z-layers map. Property omitted.";
  }

  @return map-get($z-layers, $layer);
}

There. In case you ask for an undefined key (for instance “SitePoint”), Sass will print the following in the console:

“No layer found for SitePoint in $z-layers map. Property omitted.”

Pretty cool, right?

Playing with the New Toy

Now that we are done building our function, it’s time to play with it! As you saw at the beginning of this article, using the function is fairly straightforward:

.modal {
  // ...
  z-index: z("modal");
}

.modal-overlay {
  // ...
  z-index: z("modal") - 1;
}

The idea is to always use this function when defining z-index values. Using it only part of the time makes no sense and pretty much kills the system.

Also, I think it’s better to keep the layer map as light as possible. The more layers you add, the more you make your Z scale complex. Try finding a couple of recurring values, map them to generic keywords, and you should be good to go.

Pushing Things Further with Nested Contexts

You are probably not without knowing that z-index values are not absolute. They are all related to their own stacking context. That basically means that if you try to make an element from Context-A appear on top of an element from Context-B, but Context-B is drawn on top of Context-A, then even an index of over nine thousand would not be enough.

Note: For more information on stacking contexts and z-index be sure to read this tremendous article by Philip Walton.

Now if we want to make our system aware of stacking contexts, we could have nested maps. For instance, if our modal is initializing a new stacking context and we want to order elements in it, we could update the map:

$z-layers: (
  "goku":            9001, 
  "shoryuken":       8000,
  "modal": (
    "base":           500,
    "close":          100,
    "header":          50,
    "footer":          10
  ),
  "default":            1,
  "below":             -1,
  "bottomless-pit": -9000
);

Problem is, we cannot easily fetch a value from nested maps, not with map-get in any case. Fortunately, building such a function is easy:

@function map-deep-get($map, $keys...) {
  @each $key in $keys {
    $map: map-get($map, $key);
  }

  @return $map;
}

This is enough. Simple, isn’t it? Now, we can write our modal module like this:

// New stacking context
.modal {  
  position: absolute;
  z-index: z("modal", "base");

  .close-button {
    z-index: z("modal", "close");
  }

  header {
    z-index: z("modal", "header");
  }

  footer {
    z-index: z("modal", "footer");
  }
}

Which would yield the following result:

.modal {
  position: absolute;
  z-index: 500;
}

/* This is `100` in the modal stacking context */
.modal .close-button {
  z-index: 100;
}

/* This is `50` in the modal stacking context */
.modal header {
  z-index: 50;
}

/* This is `10` in the modal stacking context */
.modal footer {
  z-index: 10;
}

To the Future!

Some day in the bright future we all dream of, we will have native CSS variables. Then we won’t need all this anymore. Instead, this is what will happen:

:root {
  --z-goku:            9001;
  --z-shoryuken:       8000; 
  --z-modal:            500;
  --z-default:            1;
  --z-below:             -1;
  --z-bottomless-pit: -9000;
}

.modal {
  z-index: var(--z-modal);
}

See? Pretty much the same thing, except it’s var() instead of z() and --key instead of key. Slightly longer to type, and probably a little more chaotic since those are individual variables and not a map. But it does the job well while keeping it native.

Final Thoughts

I don’t know about you, but I think this is a beautiful method to handle z-index in a Sass project. Not only does it keep things organized with every z-index stored in the layers map, but it is quite easy to have slight variations with operators as we’ve seen with our last example.

That’s it. Here is a Sassmeister demo to get you started. No reason not to apply this in your projects starting today.

Frequently Asked Questions (FAQs) on Managing Z-Index with Sass

What is the significance of managing z-index with Sass?

The z-index property in CSS is used to control the stacking order of elements on a webpage. However, managing z-index values can become complex in large projects. Sass, a preprocessor scripting language, can be used to manage z-index values more efficiently. It allows you to create variables, functions, and mixins, which can be used to create a more organized and maintainable codebase.

How does Sass improve z-index management?

Sass improves z-index management by allowing you to create a list or map of elements and their respective z-index values. This way, you can easily control the stacking order of elements by referring to the list or map, instead of manually adjusting individual z-index values. This approach reduces the risk of overlapping elements and makes your code more maintainable.

What are the common problems encountered when managing z-index?

Common problems when managing z-index include overlapping elements, maintaining a consistent stacking order, and dealing with large numbers of z-index values. These problems can lead to a messy and hard-to-maintain codebase. Using Sass can help alleviate these issues by providing a more structured and organized way to manage z-index values.

How can I start managing z-index with Sass?

To start managing z-index with Sass, you first need to install Sass and set up your project to use it. Then, you can create a list or map of elements and their respective z-index values. You can use Sass functions and mixins to manipulate these values and control the stacking order of elements.

Can I use Sass to manage z-index in existing projects?

Yes, you can use Sass to manage z-index in existing projects. However, it may require some refactoring of your CSS code. You would need to replace all instances of z-index values with references to your Sass list or map.

What are the benefits of using Sass for z-index management?

Using Sass for z-index management can make your code more organized, maintainable, and scalable. It reduces the risk of overlapping elements and makes it easier to adjust the stacking order of elements. It also makes your code more readable, as it provides a clear overview of all z-index values in one place.

Are there any limitations or drawbacks to using Sass for z-index management?

One potential drawback is that using Sass for z-index management requires a build process, as Sass needs to be compiled into CSS. This can add complexity to your project setup. However, the benefits of improved code organization and maintainability often outweigh this drawback.

Can I use Sass for z-index management in all browsers?

Yes, as Sass is a preprocessor that compiles into CSS, the resulting CSS code can be used in all browsers that support the z-index property. However, you should always test your code in multiple browsers to ensure compatibility.

How does Sass handle z-index conflicts?

Sass itself does not handle z-index conflicts. It’s up to the developer to manage z-index values in a way that avoids conflicts. However, Sass can make this task easier by providing a structured and organized way to manage z-index values.

Can I use Sass for z-index management in conjunction with other CSS methodologies?

Yes, Sass can be used in conjunction with other CSS methodologies, such as BEM or OOCSS. These methodologies can provide additional structure and organization to your CSS code, while Sass can be used to manage z-index values more efficiently.

Kitty GiraudelKitty Giraudel
View Author

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/they.

managing z-indexsassz-index
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week