Let’s get Mercator with this ish

Maps? Murps?

The verdict is out, Sass variable maps are pretty awesome. I am going to pretend I’ve known about them forever so that you think I’m cool. Jaykay, I just started using them.

For the uninitiated, Dennis Gaebel has an awesome Sass variable maps write up on his blog: Real Sass, Real Maps.

The basic idea is that you can store groups of Sass variables in a JSON-ish map:

  $colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

These can then be accessed using the map-get function:

  // using `map-get` in Sass
h1 { color: map-get($colors, primary); }

  h1 { color: #FFBB00; }

Inception

Now maps, being the lovely little things that they are, can be nested for added specificity and better organization:

  // color variable map
$colors: (
  // nested map inception
  primary: (
    base: #FFBB00,
    light: lighten(#FFBB00, 15%),
    dark: darken(#FFBB00, 15%),
    trans: transparentize(#FFBB00, 0.5)
  ),
  // and another. is the totem still spinning?
  secondary: (
    base: #0969A2,
    light: lighten(#0969A2, 15%),
    dark: darken(#0969A2, 15%),
    trans: transparentize(#0969A2, 0.5)
  )
);

To access a nested map, you simply nest the map-get:

  h1 {
  color: map-get(map-get($colors, primary), base);
}

The inner map-get grabs our primary map, then the last value base grabs that primary map’s base value.

Functions FTW

Functionally, nested maps are awesome. Unfortunately the syntax is a bit...meh. We can fix that nonsense with a @function:

  // retrieve color from $colors map ie. `color(primary, base)`
@function color($color-name, $color-variant) {
  // map inception
  @return map-get(map-get($colors, $color-name), $color-variant);
}

// now we use the function
h1 {
  color: color(primary, base);
}

It is lovely, isn’t it. We can even take it a step further. Let’s say we want to have nested maps and non-nested values in our map:

  // color variable map
$colors: (
  // non-nested values
  text: #FFF,
  background: #333,
  // nested map inception
  primary: (
    base: #FFBB00,
    light: lighten(#FFBB00, 15%),
    dark: darken(#FFBB00, 15%),
    trans: transparentize(#FFBB00, 0.5)
  ),
  secondary: (
    base: #0969A2,
    light: lighten(#0969A2, 15%),
    dark: darken(#0969A2, 15%),
    trans: transparentize(#0969A2, 0.5)
  )
);

We can then update our function to optionally receive the nested variant name:

  // retrieve color from $colors map ie. `color(base, primary)`
@function color($color-name, $color-variant:null) {
  // color variant is optional
  @if ($color-variant != null) {
    // map inception
    @return map-get(map-get($colors, $color-name), $color-variant);
  } @else {
    @return map-get($colors, $color-name);
  }
}

And use it like this:

  // using the function to get an non-map color
body {
  background-color: color(background);
}
// using the function to get a nested map color
h1 {
  color: color(primary, base);
}

Simplify ze Defs

Some of you may have noticed and not enjoyed the redundency in our variable maps:

  // color variable map
$colors: (
  primary: (
    base: #FFBB00,
    light: lighten(#FFBB00, 15%),
    dark: darken(#FFBB00, 15%),
    trans: transparentize(#FFBB00, 0.5)
  ),
  // ...
);

Notice how the base color #FFBB00 is repeated in hex form across all the values in the darken, lighten, and transparentize functions. Unfortunately, we cannot refer to a sibling value in the other values (ie @this.base), so the hex must be repeated. The workaround? Simply extract the hex into a variable outside of the map.

  // base color defs
$color-primary: #FFBB00;
// color variable map
$colors: (
  primary: (
    base: $color-primary,
    light: lighten($color-primary, 15%),
    dark: darken($color-primary, 15%),
    trans: transparentize($color-primary, 0.5)
  ),
  // ...
);

We can afford to be more verbose with our variable definition $color-primary because we don’t have to write it everywhere, just in our map definitions. Then, if we want to change our color scheme around, we can swap a single value instead of four values. Doing it this way is more a matter of preference, but I enjoy making the map completely color-agnostic by manipulating a color variable instead of hardcoding it.

If you want to be an ultra-streamline magic person, you can store your darken, lighten, and transparentize amounts and use them across the board for each of your colors:

  // base color defs
$color-primary: #FFBB00;
$color-secondary: #0969A2;
$color-shade-amount: 15%;
$color-trans-amount: 0.5;
// color variable map
$colors: (
  primary: (
    base: $color-primary,
    light: lighten($color-primary, $color-shade-amount),
    dark: darken($color-primary, $color-shade-amount),
    trans: transparentize($color-primary, $color-trans-amount)
  ),
  secondary: (
    base: $color-secondary,
    light: lighten($color-secondary, $color-shade-amount),
    dark: darken($color-secondary, $color-shade-amount),
    trans: transparentize($color-secondary, $color-trans-amount)
  )
);

When to do any of this

The times I find it best to use a @function like this is when your variable map is a single category of values and justifiably complicated. In addition to color schemes, things like container and device sizes are a great time to use them. Here are a few basic guidelines I’ve formed for myself:

  • If you only have a few variables in a group (ie two colors) on a small project or only refer to them a few times, just use vanilla variables.
    • no sense creating a function just to look cool.
  • Don’t create one giant “global variable” map:
    • like $vars: ( colors: (...), sizes: ( containers: (...), fonts: (...) ) )
    • your function to get the values will be oddly verbose and clutter-y
    • just make multiple functions per map...if each map is necessary

Whelp, that’s all I got.

We just looked at some helpful ways to harness Sass variable maps and functions. Don’t abuse this stuff or else you’ll trap yourself in a very confusing and verbose corner. Keep it simple and write pretty code.

If you want to see a demo of some of this stuff, I got a tiny (and ugly) playground here:

Credits: I can’t remember with certainty where I was initially exposed to this idea, but I know I read this article by Erskine Design at some point.


I am Jake Albaugh and am going to write this bio in first person. These days, I write on CodePen because I care more about it and you than I do about my personal site. Read more articles via my CodePen blog page. View my work on my CodePen profile. If you’re a hip millennial, “get at me” on my twitter @jake_albaugh.