Refactoring My “Simple” Sass Breakpoint Mixin

When something about your code just isn’t right, refactor it for clarity

It was just a few months ago when I wrote my concept for a Dead Simple Sass Mixin to Handle Responsive Breakpoints and as I continued to use it, I realized there was something that didn’t feel right — hidden complexity in the form of cleverness.

“Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage to move in the opposite direction.”
~ E.F. Schumacher

When I first wrote the mixin I was trying to simplify how I wrote specific breakpoints when working in a Bootstrap project, so for me it just made sense to hardcode Bootstrap-based labels (xs, sm, md, and lg) right into the mixin. Of course, what if I wanted to specify a custom breakpoint? Well that was easy too, you just setup a minimum or a maximum value and it was all taken care of for you.

@include breakpoint($min: 992px) { ... }

To account for this I would check the “type” of the input being submitted for the first attribute using Sass’s type-of function. This would help me determine which flow to take through the mixin. But is it all necessary? Could there be an even easier way to make a breakpoint mixin where the input was a consistent type but more flexible?

Setting Breakpoints with Maps

Since the mixin was using essentially a string value that was based around Bootstrap’s labels, Sass 3.3 and the new maps feature seemed to be a perfect way to define these attributes. This also decoupled the mixin from Bootstrap specific labeling and allows me to change frameworks without necessarily rewriting the mixin itself.

$xs: ( max:  767px );
$sm: ( min: 768px );
$md: ( min: 992px );
$lg: ( min: 1200px );

While I looked at the various map options I then decided since the $sm and the $md breakpoint actually exist in the middle of other breakpoints there would be conditions where I would want to specifically target one of them without it bleeding into the breakpoints above it. So I added two more default maps.

$sm-only: ( min: map-get($sm, min), max: map-get($md, min) — 1 );
$md-only: ( min: map-get($md, min), max: map-get($lg, min) - 1 );

This would assign two new maps of $sm-only and $md-only, grabbing the min value of the $sm and $md maps and setting the max to one less pixel than the breakpoint larger. This way I could edit the $sm and $md breakpoints without having to worry about editing these as well.

Building Out the Query Statement

The media query essentially has three possible conditions depending on what values are in the map. There is min only, max only, and min and max collectively. As I broke down the conditional checking it made sense that the only real result of having both min and max was the inclusion of the word “and” in the media query.

@media screen and (min-width: ...) and (max-width: ...) { }

So rather than having multiple if/elseif conditionals that write out the complete result it seemed to make more sense to append onto the query based on the results of each condition.

@mixin breakpoint($map) {
$query: “”;
@if map-has-key($map, min) {
$query: append($query, “(min-width: #{map-get($map, min)})”)
}
@if map-has-key($map, min) and map-has-key($map, max) {
$query: append($query, “and”)
}
@if map-has-key($map, max) {
$query: append($query, “(max-width: #{map-get($map, max)})”)
}
@media screen and #{$query} { @content; }
}

This way we start off with a blank query and slowly build onto that query only the items that we need. Doing it these way removes any duplication within the various conditions that existed in the previous version.

Altogether not only is the weight of the mixin much leaner, but with the attributes found in maps it makes even more expansive and clear as well. Remember too, that I use xs, sm, md, and lg because of my workflow. Those values could just as easily be mobile, table, desktop, and widescreen.

The complete refactoring of the mixin including my default map values.
“Our life is frittered away by detail. Simplify, simplify.”
~ Henry David Thoreau

I’m a User Experience Architect, musician, and martial artist trying to redefine how we prototype web applications working at GravityFree in Florida.

If you’ve enjoyed this post, please hit “recommend” and please say hello and introduce yourself on Twitter.

Show your support

Clapping shows how much you appreciated Tim Knight’s story.