clubmate.fi

A good[ish] website

Web development blog, loads of UI and JavaScript topics

How to use maps in Sass, syntax, examples, and other good things

Filed under: Styling— Tagged with: sass, object

Maps are so essential data type of Sass that it’s hard to imagine the time without them. LibSass also supports maps nowadays, so there’s no barriers to use them in day to day programming routines.

Maps in Sass are like associative arrays in PHP, objects in JavaScript, or Hashes in Ruby.

Defining a map

Map facts:

  • Maps must always be surrounded by parenthesis.
  • They have to be comma separated.
  • Both, key and value, can be any Sass object.

Basic map:

$primary-colors: (
  'red': '#ff0000',
  'green': '#00ff00',
  'blue': '#0000ff'
);

The quotes are not mandatory, they’re only needed if there's spaces or other weird characters. But it just feels nice to have them in there.

Like stated before "keys and values can be any sass object". Here’s all the Sass data types:

  • numbers (e.g. 1.2, 13, 10px)
  • strings of text, with and without quotes (e.g. "foo", 'bar', baz)
  • colors (e.g. blue, #04a3f9, rgba(255, 0, 0, 0.5))
  • booleans (e.g. true, false)
  • nulls (e.g. null)
  • lists of values, separated by spaces or commas (e.g. 1.5em 1em 0 2em, Helvetica, Arial, sans-serif)
  • maps from one value to another (e.g. (key1: value1, key2: value2))

This means, for instance, that the value can be a list. In the below map, keys are mapped to two values: hex and RGB:

$primary-colors: (
  'red': (
    #ff0000,
    rbg(255, 0, 0)
  ),
  'green': (
    #00ff00,
    rbg(0, 255, 0)
  ),
  'blue': (
    #0000ff,
    rbg(0, 0, 255)
  )
);

Nice!

More on that later on in the article.

Accessing and manipulating maps

We’ve got a plethora of functions at our disposal:

NameExplanation
map-get($map, $key)Returns the value in a map associated with a given key.
map-merge($map1, $map2)Merges two maps together into a new map.
map-remove($map, $keys…)Returns a new map with keys removed.
map-keys($map)Returns a list of all keys in a map.
map-values($map)Returns a list of all values in a map.
map-has-key($map, $key)Returns whether a map has a value associated with a given key.
keywords($args)Returns the keywords passed to a function that takes variable arguments.

And the good old @for, @each, and @while loops. Here's a simple loop example using the $primary-colors map we defined earlier:

@each $color in $primary-colors {
  .thing {
    content: '#{$color}';
  }
}

Now the $color inside the loop, includes the whole shebang, key and value. To get them separately, define them separately:

@each $color-name, $color-code in $primary-colors {
  .color-#{$color-name} {
    background: $color-code;
  }
}

Here’s a SassMeister demo:

Play with this gist on SassMeister.

Managing z-index with a @mixin

Here's a nice use case for maps, this is lifted from Hugo Giraudel's blog post.

// Start with a map
$z-layers: (
  'cog': 50,
  'overlay': 40,
  'toc': 30,
  'iframe-loader': 25,
  'tooltip': 20
) !default;

@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);
}

That breaks down to:

  • @if not map-has-key($z-layers, $layer) — if you gave it a value that isn't in the map, give a friendly warning.
  • @warn — is the warning :) "...prints the value of a SassScript expression to the standard error output stream", NOTE: @warn prints it to the stream not to the page.
  • @return map-get($z-layers, $layer) — this returns a value associated with a given key, the $layer being the key that was passed to the function. This is the only essential line of the mixin, the if statement is only for error handling.

Lists as values or keys

Like mentioned before, key or a value can be any Sass object, for instance a list. Let's write a little rgba background colour mixin. You know how old browsers need an rgb fallback if rgba is used. So, here's a map that has both rgba and rgb values associated with a single key.

// Map
$primary-colors: (
  'red': (
    rbga(255, 0, 0, 0.5),
    rbg(255, 0, 0)
  ),
  'green': (
    rbga(255, 0, 0, 0.5),
    rbg(0, 255, 0)
  ),
  'blue': (
    rbga(255, 0, 0, 0.5),
    rbg(0, 0, 255)
  )
);

// Use the map
@mixin colors-w-fallbacks($color) {
  @if not map-has-key($primary-colors, $color) {
    @warn "No color found for #{$color} in $primary-colors map. Ktnxbye.";
  }

  @each $color-name, $color-code in $primary-colors {
    @if $color-name == $color {
      // The rgba value
      background: '#{nth($color-code, 2)}';
      // And the rgb as a fallback
      background: '#{nth($color-code, 1)}';
    }
  }
}

Surprisingly this won't work:

@mixin colors-w-fallbacks($color) {
  background: nth($color, 2);
}

It returns the following error: "List index is 2 but list is only 1 item long for `nth'", cause it won't see the value as a list, but as a variable. More on that in the following demo:

Play with this gist on SassMeister.

Extract values or keys from a map

First I was like:

// Extract map values to a list
$point-values: ();
@each $point, $value in $breakpoints {
  $point-values: append($point-values, unquote(nth($value, 2)), comma);
}

Then I realized there’s a helper method for that:

$list-of-values: map-values($map);

Same goes for keys:

$list-of-keys: map-keys($map);

Conclusions

I think Sass has finally broke into the realm or "fully fledged programming language" when it introduced maps, the last piece of the puzzle so to speak. This post just scratches a surface, maps provide ample opportunities to write DRY and beautiful code.

Comments would go here, but the commenting system isn’t ready yet, sorry.

  • © 2022 Antti Hiljá
  • About
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!