Using Null to Write Better Sass Mixins

So you know that scenario where you’ve written a pretty flexible mixin, but when you @include it, your mixin overwrites some styles that needed to be preserved?

At this point you have some really annoying options:

  1. refactor your mixin (after which you should check all previous instances of the mixin to make sure your refactoring doesn’t mess things up)
  2. overwrite the overwritten properties… again (and pray and pray and pray that there aren’t multiple media queries affecting those properties)
  3. write another mixin just for this specific situation that is really, really similar to your first mixin, but without the offending styles.

Luckily, there is also a not-at-all annoying option! You can use null values in your mixin (either as default values when you create the mixin, or as updated values when you @include it) to erase the style properties you don’t need.

Let’s look at an example where null values would come in handy. Imagine you wrote a really simple card mixin with some optional arguments for background and padding.


@mixin card($background: white, $padding: 10px) {
  background: $background;
  border-radius: 3px;
  display: block;
  padding: $padding;
}

Now let’s make this aggravating. Pretend you have a modal component. Most of your modals look the same, but one of them, .Modal–card, needs to look like a card. Lucky us, we have a card mixin we can @include for Modal–card.


@mixin card($background: white, $padding: 10px) {
  background: $background;
  border-radius: 3px;
  display: block;
  padding: $padding;
}

.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
  &.Modal--card {
    @include card;
  }
}

Which compiles to:


.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
}
.Modal.Modal--card {
  background: white;
  border-radius: 3px;
  display: block;
  padding: 10px;
}

Good! Now let’s say that the modal padding is really important and we need to preserve that, no matter what. And let’s also add some media queries, just for fun.


@mixin card($background: white, $padding: 10px) {
  background: $background;
  border-radius: 3px;
  display: block;
  padding: $padding;
}

.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
  @media only screen and (min-width: 40em) {
    padding: 30px;
  }
  @media only screen and (min-width: 60em) {
    padding: 40px;
  }
  &.Modal--card {
    @include card($padding: 20px); 
    #spoiler alert: this isn't going to work
  }
}

As expected, those media queries are not happy. Even though we’ve updated the card mixin padding when we included it, the modal padding adjustments in the media queries are getting overwritten. Take a look at how the CSS compiles. .Modal.Modal–card is so specific that the 20px padding remains, regardless of screen size.


.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
}
@media only screen and (min-width: 40em) {
  .Modal {
    padding: 30px;
  }
}
@media only screen and (min-width: 60em) {
  .Modal {
    padding: 40px;
  }
}

.Modal.Modal--card {
  background: white;
  border-radius: 3px;
  display: block;
  #since .Modal.Modal--card is more specific than .Modal 
  #this padding will overwrite the padding from the .Modal media queries
  padding: 20px;
}

Let’s fix this issue using null.


@mixin card($background: white, $padding: 10px) {
  background: $background;
  border-radius: 3px;
  display: block;
  padding: $padding;
}

.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
  @media only screen and (min-width: 40em) {
    padding: 30px;
  }
  @media only screen and (min-width: 60em) {
    padding: 40px;
  }
  &.Modal--card {
    @include card($padding: null);
  }

When our CSS gets compiled this time, the padding property from our card mixin is absent. This is awesome because now, our modal padding can trickle down through the card mixin.


.Modal {
  background: black;
  padding: 20px;
  position: absolute;
  z-index: 10;
}
@media only screen and (min-width: 40em) {
  .Modal {
    padding: 30px;
  }
}
@media only screen and (min-width: 60em) {
  .Modal {
    padding: 40px;
  }
}

.Modal.Modal--card {
  background: white;
  border-radius: 3px;
  display: block;
}

And that’s it. Now go forth and nullify all of your unneeded style properties! Or maybe create your Sass mixins using null as the default the value – what flexibility!

Common Sense Warning: Beware using null values with calculations, as null is not a number… unless your using a Sass length function, in which case it’s 1. >_<

 

Let us know what you think! By the way, are you looking for your next gig? Say hello.