SASS maps and mixins

SCSS

I’ve really been enjoying SASS maps recently for easily extending components with branding styles. We get something pretty nice in the end, with a couple caveats. If you are using node-sass, you won’t have access to the @at-root directive, which makes mixins a bit less extendable. We get extra bloat in the end, but for smaller projects I think it is negligible… for a time.

Intro to SASS maps

SASS maps are pretty simple in concept: a key-value store. For us, our map will be organize where the key will be the class alias we will to describe that color, and two values: a branding color (for borders and backgrounds) and a text color (lighter colors will want darker text, and darker schemes lighter text). Let’s say we also have some site-wide colors set for us as variables. We end up with something like this:

// color variables used across our application
$green: #00b675;
$gold: #ffc003;
$flora: #00d797;

// initial scss map
$branding-primary: (
  ('green',       $green,     $white), 
  ('gold',        $gold,      $white),
  ('flora',       $flora,     $white)
);

It would be great if every time we write a new component, we could create all n variations of it using the same mixins. So labels, alerts, cards, badges, etc. could all share the same code responsible for branding variations.

With our map, the great thing is that now we can easily iterate through and declare the same class variations for all of these components. Let’s create a mixin that will take our map, and generate some classes that sets a background color, border color (if border width is supplied), and a text color:

@mixin branding($extend: false) {
  @each $class, $bg, $color in $branding-primary{
    &.#{$class} {
      background-color: $bg;
      border-color: $bg;
      color: $color;
      
      @if $extend == true {
      
	 &.lighten {
	   background-color: lighten($bg,5%);
	 }

	 &.darken {
	   background-color: darken($bg,5%);
	 }
      }
    }
  }
}

I also added a method signature for the mixin, so we can generate light and dark versions if we want to down the road.

What we have works, but we can only use it for our $branding-primary map. If we want to support different branding schemes (a primary and secondary, maybe), our solution is a little less extendable. If make our mixins a bit more functional, we can achieve better modularity:

//  we will call this within our components
@mixin branding-primary() {
  @include branding($branding-primary);
}

//  our functional mixin
@mixin branding($map, $extend: false) {
  @each $class, $bg, $color in $map {
    &.#{$class} {
      background-color: $bg;
      border-color: $bg;
      color: $color;
      
      @if $extend == true {
      
	 &.lighten {
	   background-color: lighten($bg,5%);
	 }

	 &.darken {
	   background-color: darken($bg,5%);
	 }
      }
    }
  }
}

Now we can create multiple mixins that will pull in different maps and generate classes for our components. Some components we might want multiple variations, others we might not. Buttons, labels, and alerts come to mind.

We can use our new mixin with our components like this:

// some example declarations
.alert {
	@include branding-primary();
	border-radius: 2px;
	display: inline-block;
}

.badge {
	@include branding-primary();
	border-radius: 50%;
	display: block;
	width: 10px;
	height: 10px;
	text-align: center;
}

Which will output to:

.alert {
	border-radius: 2px;
	display: inline-block;
}

.alert.green {
	background-color: #00b675;
	border-color: #00b675;
	color: white;
}

.alert.gold {
	background-color: #ffc003;
	border-color: #ffc003;
	color: white;
}
// .. you get the point

Moving beyond

I have found applications for this strategy for font-size variations for components as well. Creating a map of a class alias to a font sizes for all components makes an easy interface for front-end devs to say, tweak the style of a particular component. In the end, it would be great if somebody could easily change the color and size of any component:

<div class="alert green small">Success!</div>