ybcteq
Last Updated: February 25, 2016
·
2.686K
· cameron
Cameron daigle

Drop-In Responsive Styles with Sass

Organizing responsive styles into your existing stylesheet is always a challenge, but a simple Sass mixin can provide you with a dead-simple way to inject responsiveness wherever it's needed.

Here's the scenario. You've got most of your responsive styles specified within @media calls, but sometimes, even nesting responsive styles feels like overkill. When only one or two items are changed responsively, lines of code can add up quickly. For example, say I have an element with a bunch of styles that only needs margin and font size to change at 800px or smaller:

.my_cool_element
  font-size: 24px
  margin-bottom: 40px
  // a ton of other stuff here
  @media screen and (max-width: 800px)
    font-size: 18px
    margin-bottom: 20px

There's a simple readability issue here: there might be a lot of lines of code in between the original and the responsive styles. Plus, if I want new styles at a second breakpoint (e.g. 480px for mobile), I'll need to list each attribute a third time under a new @media call.

This got me thinking. Given that I was finding myself using at most two breakpoints (roughly tablet-sized and roughly mobile-sized), and very often was only changing a few attributes per selector, was there a way to knock out all possible attribute values on one line?

Enter the responsive mixin.

= responsive($attr, $full, $mid:false, $narrow:false)
  #{$attr}: #{$full}
  @if $mid
    @media screen and (max-width: 800px)
      #{$attr}: #{$mid}
  @if $narrow
    @media screen and (max-width: 480px)
      #{$attr}: #{$narrow}

Here's the magic: with this mixin, I can specify all of my breakpoint values in one place. I just pass the attribute to responsive, along with each breakpoint's value.

.my_cool_element
  +responsive(font-size, 24px, 20px)
  +responsive(margin-bottom, 40px, 20px)
  font-size: 24px
  margin-bottom: 40px

So if I need to add more attributes at the 480px breakpoint, I can just pass a third value to the appropriate +responsive call:

.my_cool_element
  +responsive(font-size, 24px, 20px, 16px)
  +responsive(margin-bottom, 40px, 20px, 10px)
  font-size: 24px
  margin-bottom: 40px

I've found this technique to be tremendously useful for sprinkling my stylesheet with responsiveness. (I also recommend abstracting your breakpoint sizes into Sass variables, but that's outside the scope of this post.) Obviously you'll encounter scenarios in which you're changing 5 or 6 attributes, at which point that element will have outgrown this technique – but once I started approaching responsiveness with this mixin, I found it was cutting down on lots of verbose @media nesting.

So, give +responsive a shot! Maybe it'll make your life easier.

(originally posted on the Hashrocket blog)

Say Thanks
Respond

10 Responses
Add your response

16892
991b8de70c5edb062ddfe8cd9b59277e

Did you check your compiled css file? I haven't checked, but I could imagine, that this creates tons of @media queries with nested classes. Or is SASS clever enough to merge them into one?

over 1 year ago ·
16893
London bridge sq

@namxam, no.

Sass:

= responsive($attr, $full, $mid:false, $narrow:false)
  #{$attr}: #{$full}
  @if $mid
    @media screen and (max-width: 800px)
      #{$attr}: #{$mid}
  @if $narrow
    @media screen and (max-width: 480px)
      #{$attr}: #{$narrow}

.my_cool_element
  +responsive(font-size, 24px, 20px)
  +responsive(margin-bottom, 40px, 20px)
  font-size: 24px
  margin-bottom: 40px

.my_cool_element2
  +responsive(font-size, 24px, 20px, 16px)
  +responsive(margin-bottom, 40px, 20px, 10px)
  font-size: 24px
  margin-bottom: 40px

CSS:

.my_cool_element {
  font-size: 24px;
  margin-bottom: 40px;
  font-size: 24px;
  margin-bottom: 40px;
}
@media screen and (max-width: 800px) {
  .my_cool_element {
    font-size: 20px;
  }
}
@media screen and (max-width: 800px) {
  .my_cool_element {
    margin-bottom: 20px;
  }
}

.my_cool_element2 {
  font-size: 24px;
  margin-bottom: 40px;
  font-size: 24px;
  margin-bottom: 40px;
}
@media screen and (max-width: 800px) {
  .my_cool_element2 {
    font-size: 20px;
  }
}
@media screen and (max-width: 480px) {
  .my_cool_element2 {
    font-size: 16px;
  }
}
@media screen and (max-width: 800px) {
  .my_cool_element2 {
    margin-bottom: 20px;
  }
}
@media screen and (max-width: 480px) {
  .my_cool_element2 {
    margin-bottom: 10px;
  }
}
over 1 year ago ·
16897
Cameron daigle

@namxam it doesn't, but file size is an optimization issue for minification/gzip (heck, while I don't recommend it, plenty of people START with Bootstrap, which is 100+ KB out of the box), and selector speed is really not a concern on modern devices. There are plenty of other places in an app to optimize performance other than generated CSS :)

over 1 year ago ·
16898
991b8de70c5edb062ddfe8cd9b59277e

Yes, and I am not concerned about file size. I was just curious how SASS compiles such an approach. I think you are right, that this might be easier to read. But at the same time, it can increase the number of selectors significantly.

over 1 year ago ·
16899
Cameron daigle

Totally, agreed – it's no replacement for larger @media blocks, it's just for piecemeal stuff.

over 1 year ago ·
16902
Dead mouse

I've got a Compass plugin that sits about halfway between bare metal and your approach:
https://github.com/mikesten/compass-respond

It's a little more verbose but I find it to be very readable... Not every element needs to respond to every breakpoint so I can just pick and choose as needed.

over 1 year ago ·
16903
Cameron daigle

@mikesten yeah, we have similar shortcut mixins for regular @media queries where you're modifying a bunch of attributes rather than just one or two.

over 1 year ago ·
16914
Cd93483c1bb616efb5480b639dbb9ca3

there is always this if you are worried about too many media queries: https://github.com/aaronjensen/sass-media_query_combiner

That said, performance and size wise there is little to no difference.

over 1 year ago ·
16937
4db9ad794066a80ca87ee20d4c9d0413

Great!
I've made something similar some time ago to use with Foundation 5. https://gist.github.com/jofralogo/8067716

:)

over 1 year ago ·
16980

Very cool. As @aaronjensen alludes to, there exist post processors that afford automated media query packing. Pleeease is another option.

over 1 year ago ·