Sass and z-indexes: a (slightly) better way

Doug Avery, Former Senior Developer

Article Category: #Code

Posted on

Z-indexes: They're a huge problem.

Okay, they're not a huge problem. Still, there comes a moment on every large project where I'm opening several files to figure out why the header is above the modal but under the breadcrumb but over the content area. It's a common CSS issue that takes only a minute to resolve, but just for fun, let's ask ourselves man's most important question:

The answer: yes! With a little Sass magic, you can automate the +1/-1 stacking of z-index items and keep your hands clean of all those gross numbers.

Notes:

  1. The basic solution depends on Sass maps, which might not be available in your version of Sass. For a map-less version, read alternate solutions
  2. I'm using .scss syntax for these examples, although I prefer the indented syntax. Currently, .scss syntax is better for maps, because it allows multi-line rules. If this bugs you as much as it does me, comment here.

Basic example

http://codepen.io/averyvery/pen/jpyax

Start with a list

Begin with a simple list of the item names you want. These will be the keys you reference later, when you're building actual components:

 $z-indexed-items: 'footer', 'content', 'sidebar', 'content-announcement', 'header', 'modal', 'survey-overlay';

Iterate and map

Now, step over your list and create a map that uses your items for keys, and your counter for the z-index:

 $z-index-map: ();
$counter: 1;

@each $item in $z-indexed-items {
 $z-index-map: map-merge($z-index-map, ($item: $counter));
 $counter: $counter + 1;
}

Mix in

z is a simple mixin that returns the z-index from the map you've created. Done!

 @mixin z($key) {
 z-index: map-get($z-index-map, $key);
}

@include z('survey-overlay); // sets z-index to 7

Alternate solutions

Nested maps

http://codepen.io/averyvery/pen/Hmwhb

 $z-indexed-items: (
 'global': (
 'footer',
 'header'
 ),
...

@include z('global', 'header'); // sets z-index to 2

One global set of z-indexes is useful, but it's not going to totally replace z-index for you. Here's an example of the same idea (pre-building a map of z-indexes, then performing look-ups) in a two-tier map that uses groups.

Selectors instead of keys

http://codepen.io/averyvery/pen/gIpjy

 // sets .footer z-index to 1, .header to 2
$z-indexed-selectors: '.footer' '.header';

Iterating over selectors might be simpler if you're defining a basic global stack, but as the site grows it's probable going to come back to bite you. Tricky selectors, media queries, and dynamic class changes can trip up this approach.

List matching

http://codepen.io/averyvery/pen/xondH

 $modal-z: 'photo', 'caption', 'title', 'close-button';

.close-button {
 @include z('close-button', $modal-z);
}

The above methods have a downside: they require you define your z-index stacks up front, but in a large system you might want to write them as needed alongside your components. In that case, you can use a mixin that iterates over a provided list, like in this example.


Admittedly, z-index stacks aren't the most pressing problem in web development, but hopefully this post outlined a few clever ways to use Sass to abstract them (or other issues) out of your code. If you have any suggested solutions or questions about the idea, let me know in the comments!

Related Articles