Concise Sass Mixin for Media Queries
--
Using sass for media queries is pretty popular — there’s plenty of libraries just for this. Sass is a lot more powerful (especially with dart-sass) than people who have been using Sass for a long time might realize, and we can write some really expressive mixins and functions these days.
End result first:
$breakpoints: (md: 616px, lg: 1000px, xl: 1288px);h1 {
@include at-each(2rem, md 3rem, lg 4rem) using ($size) {
font-size: $size;
}
}
Code is meant to be read by developers first and foremost. Before I explain what the above snippet does — make a guess. Sass like this isn’t about saving characters for the code’s sake, it’s about writing code with clear intent.
For each argument to the at-each()
mixin, it treats the first argument (if present) as a breakpoint and the second as a parameter to pass along to the mixin’s @content
. That is, the above code compiles to
h1 {
font-size: 2rem;
}
@media screen and (min-width: 616px) {
h1 {
font-size: 3rem;
}
}
@media screen and (min-width: 1000px) {
h1 {
font-size: 4rem;
}
}
Using the mixin results in clearer code and no duplication (you only reference font-size
in one place.) It lowers the difficulty in changing single CSS value per breakpoint down to only adding one more meaningful line.
Here’s the source.
@use "sass:map";$breakpoints: (md: 616px, lg: 1000px, xl: 1288px);@mixin at-each($map...) {
@each $bp, $arg in $map {
@if $arg {
@if map.has-key($breakpoints, $bp) {
$bp: map.get($breakpoints, $bp);
}
@media screen and (min-width: $bp) {
@content($arg);
}
} @else {
@content($bp);
}
}
}
- First up, we’ll use the newer sass modules syntax and
@use "sass:map";
. You could easily rewrite this to use the (deprecated) unnamespaced sass map functions instead. - Next, Sass supports arbitrary arguments via a spread-like operator. Since we’re going to loop over
$map
right away, it’s a great fit here. - We’re using
map.has-key()
andmap.get()
to look up breakpoints by key from our$breakpoints
map. Note that, if there’s no match it’ll just use the first argument as the breakpoint, meaning something like@include at-each(400px purple, 500px gold) {
will work exactly the way the developer might hope. - Next, while
@content
is certainly not a new feature, you might not know that it can take arguments. Arguments passed in get assigned to variables when you using the mixin by theusing ()
syntax.@include at-each(...) using ($size) {
assigns the first argument passed@content
to$size
.
Other examples of things you can write with this mixin:
.card {
border: 2px solid silver; @include at-each(20px, md 30px, lg 40px) using ($padding) {
padding: $padding;
}
}.cell {
@include at-each(750px 33.3%, lg 25%, 1250px 20%) using ($width) {
width: $width;
}
}
Here’s a codepen to play around with: