DEV Community

Claire Parker-Jones
Claire Parker-Jones

Posted on

Drying Out CSS Selectors with Sass Maps

First attempt: repetitive selectors

I recently wrote some Sass code that looked similar to this:

$color-apple: green;
$color-lemon: yellow;
$color-strawberry: red;


.apple-button {
  background-color: $color-apple;
}

.strawberry-button {
  background-color: $color-strawberry;
}

.lemon-button {
  background-color: $color-lemon;
}

// This compiles to:

// .apple-button {
//   background-color: green;
// }
//
// .strawberry-button {
//   background-color: red;
// }
//
// .lemon-button {
//   background-color: yellow;
// }
Enter fullscreen mode Exit fullscreen mode

(No I'm not working on a website for a greengrocers, the fruit theme is just for example purposes! 🍏🍓🍋)

At first glance, it looks OK.

But this code is pretty repetitive. The structure of each selector is the same, except for one word in the name and the colour value.

Also, this pattern doesn't scale efficiently. What if I want to add a blueberry-button and a grape-button? I would need to write more nearly identical selectors. What if I want to apply the colour to the font instead of the background instead? I'd have to change the CSS property on multiple lines. This is inefficient!

How could we rewrite this code to be shorter and dryer? That is, to follow the DRY principle of don't repeat yourself? Like this:

Refactor: dry selectors

$fruit-map: (
  apple: green,
  lemon: yellow,
  strawberry: red
);

@each $fruit, $fruit-colour in $fruit-map {
  .#{$fruit}-button--dry {
    background-color: #{$fruit-colour};
  }
}

// This compiles to:

// .apple-button--dry {
//   background-color: green;
// }
//
// .lemon-button--dry {
//   background-color: yellow;
// }
//
// .strawberry-button--dry {
//   background-color: red;
// }
Enter fullscreen mode Exit fullscreen mode

A Sass map is a collection of key-value pairs, much like an object in JavaScript. In this example, $fruit-map is a map. The fruit name is the key, and the colour is the value.

We can use the Sass each directive to iterate over each pair. We assign the key and value to variables, which we can use to construct our selector using variable interpolation (i.e. this syntax #{$foobar}).

This code scales well. If we want to generate a new selector, we only need to add a new key-value pair to the map, e.g. blueberry: blue. If we want to assign the colour to the font instead of the background, we only need to change the CSS property on one line instead of many. Great!

Enhancement: multiple values

But wait, we can take this further! Now each of the fruit-themed buttons also has a different border style. How can we adapt our map to contain multiple values for each key? Try this:

$fruitier-map: (
  apple: (green, 1px solid black),
  lemon: (yellow, 2px dashed grey),
  strawberry: (red, 3px dotted orange)
);

@each $fruit, $fruit-colours in $fruitier-map {
  $bg-colour: nth($fruit-colours, 1);
  $border-style: nth($fruit-colours, 2);

  .#{$fruit}-button--fruitier {
    background-color: #{$bg-colour};
    border: #{$border-style};
  }
}

// This compiles to:

// .apple-button--fruitier {
//   background-color: green;
//   border: 1px solid black;
// }
//
// .lemon-button--fruitier {
//   background-color: yellow;
//   border: 2px dashed grey;
// }
//
// .strawberry-button--fruitier {
//   background-color: red;
//   border: 3px dotted orange;
// }
Enter fullscreen mode Exit fullscreen mode
  • Convert the value for each key to a map. Add the colour and the new border value to the map.
  • Access each map element using the nth function and assign them to variables.
  • Use these variables to create our dynamic CSS selector with interpolation, as before.

The final Sass code is easy to extend and maintain, and also dry. The comprehensive Sass documentation contains more details about these features and many others.

Top comments (8)

Collapse
 
briankephart profile image
Brian Kephart

Nice! I've been using Sass for awhile, and I didn't know about these features.

Collapse
 
nektro profile image
Meghan (she/her)

Wow! TIL

Collapse
 
ben profile image
Ben Halpern

Me too!

Collapse
 
baamenabar profile image
B. Agustín Amenábar Larraín

I just used this pattern last week to generate a bunch of "brand classes". It helps a lot with cleaning verbose files, and makes selectors very consistent.

But be aware that debugging a class generated like this can be tricky, even with sourcemaps.

Do not over use it, or maintenance will be hell for anyone not an expert in Sass.

A usually surround these tricks with thorough documentation in the comments.

Collapse
 
rhymes profile image
rhymes

Amazing, didn't know scss could do that. Thank you!

Collapse
 
longchhun profile image
Longchhun

Enhancement using multiple value great tips.

Collapse
 
dannetherton profile image
Dan Netherton 👨‍💻

I REALLY need to incorporate the use of SASS maps more in my projects. So useful!

Great post, Claire! 👍🏻

Collapse
 
zeyduzgun profile image
Zeynep Düzgün

This is amazing though!