Sass and Media Queries: What You Can and Can’t Do

by on 15th March 2012 with 17 Comments

screenshot

Preprocessors like Sass are helping us flex our development muscles in nearly every area of our CSS. Variables, mixins, inheritance and many more great features make coding easier and more concise than ever.

So what about leveraging Sass for responsive design, or more specifically, for media queries? Are there any features of Sass that pair particularly well with media queries? Is there anything you should avoid? Join me as I experiment and discover the answers.

How Do Basic Sass Features Work With Media Queries?

I’ve done a lot of experimentation lately with Sass and media queries to see what works and what doesn’t. The results are fairly mixed: some things work and others don’t. Overall, there’s really nothing impressive enough to completely change the way you use media queries, but it’s still worth it to go through a few examples that illustrate how the two work together.

Variables Inside of a Media Query: Yes

Our first test examines whether or not a variable set in Sass carries over to the body of a media query. The question is one of scope: Can I set a variable outside of a media query and then use it within a media query?

/*Example 1 Sass*/
$fontsize: 10px;

@media only screen and (max-width : 300px){
  .someclass {font-size: $fontsize;}
}

It turns out that this works just fine. Our generic fontsize variable effectively set the font-size to 10px inside of the media query.

/*Example 1 Compiled CSS*/
@media only screen and (max-width : 300px){
  .someclass {font-size: 10px;}
}

Variables As Breakpoints: No

My next question was whether or not I could use variables to set my breakpoints. I’m not sure that this would ever be a good idea but more basic than that question was whether or not it’s even possible. What happens when I try to do this?

/*Example 2 Sass*/
$breakpoint: 300px;

@media only screen and (max-width : $breakpoint){
  .someclass {font-size: 10px;}
}

This is apparently not a valid use of Sass variables as this code throws an error and will not compile. I tried the same thing in LESS and received equally useless results. This time it compiled without throwing an error but the value wasn’t carried over properly.

Extending Inside of a Media Query: Sort Of…

Extending works with media queries, but it’s important to know how it works because the results might not be what you think.

Our first example uses @extend like it’s meant to be used. Here I told the errorTwo class to inherit the styles of the errorOne class and set the color to blue.

/*Example 3 Sass*/
@media only screen and (max-width : 300px){  
  .errorOne {
  font-size: 10px;
  font-weight: 400;
  }
  
  .errorTwo {
    @extend .errorOne;
    color: blue;
  }
}

Predictably, this produces exactly the result that we want. The @extend doesn’t care that it’s inside a media query, it works the same as it does anywhere else.

/*Example 3 Compiled CSS*/
@media only screen and (max-width : 300px){  
  .errorOne, errorTwo {
  font-size: 10px;
  font-weight: 400;
  }
  
  .errorTwo {
    color: blue;
  }
}

So what happens when we fiddle with this structure a little bit and place part of our code outside of the media query? Here I’ve set the styles for errorOne and errorTwo outside the media query and then attempted to use some questionable inheritance practices by resetting the errorTwo styles inside of the media query via an extend of errorOne.

/*Example 4 Sass*/
.errorOne {
  font-size: 10px;
  font-weight: 400;
  }

.errorTwo {
  font-size: 20px;
  font-weight: 200;
}

@media only screen and (max-width : 300px){  
  .errorTwo {
    @extend .errorOne;
    color: blue;
  }
}

Here’s the compiled CSS from this code. The hope was that the errorOne styles outside of the media query would be applied to errorTwo inside of the media query, but instead @extend worked like it was designed to: it combined the selectors in the original location and applied the shared styles.

/*Example 4 Compiled CSS*/
.errorOne, errorTwo {
  font-size: 10px;
  font-weight: 400;
  }

.errorTwo {
  font-size: 20px;
  font-weight: 200;
}

@media only screen and (max-width : 300px){  
  .errorTwo {
    color: blue;
  }
}

It’s important to note that the code above is very different than the intended CSS, which is shown below. Here errorOne and errorTwo are individually defined with different styles, and then when the media query takes effect errorTwo is set to match errorOne with the addition of a color change.

/*Example 4 Intended CSS*/
.errorOne {
  font-size: 10px;
  font-weight: 400;
  }

.errorTwo {
  font-size: 20px;
  font-weight: 200;
}

@media only screen and (max-width : 300px){  
  .errorTwo {
    font-size: 10px;
    font-weight: 400;
    color: blue;
  }
}

Mixins Inside of a Media Query: Yes

Just like with variables, we can declare a mixin outside of a media query and apply it inside of a media query. For instance, here I set up a font mixin with some text styles and then called it inside of a media query.

/*Example 5 Sass*/
@mixin font {
  font-size: 20px;
  font-family: Helvetica, Verdana, sans-serif;
  text-transform: uppercase;
}

@media only screen and (max-width : 300px){  
  .someclass {@include font;}
}

/*Example 5 Compiled CSS*/
@media only screen and (max-width : 300px){  
  .someclass { 
    font-size: 20px;
    font-family: Helvetica, Verdana, sans-serif;
    text-transform: uppercase;}
}

Roll a Media Query Into a Mixin: No

From here you might be wondering if you can actually just roll a whole media query into a mixin and then pass in some arguments when you want to implement it. As far as I can tell, there’s no way to do this in Sass, LESS or Stylus.

Nesting Media Queries

Thus far we’ve run into no huge surprises. For the most part, everything works about like you’d expect with the possible exception of breakpoints not accepting variables. Our next topic of exploration is a feature that’s built into Sass that was specifically created for media queries: nesting.

Sass allows you to nest all kinds of code. It’s certainly not a requirement when coding with Sass but the feature is there if you want it. For instance, let’s say we wanted a more concise way to write the following CSS:

/*Example 6 Plain CSS*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
}

.button:hover {
  background: #aaa;
}

With Sass, we can save ourselves the trouble of creating a separate declaration block and just nest the hover styles inside of the button class.

/*Example 6 Sass*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
  
  :hover {
    background: #aaa;
  }
}

This same logic can be used to apply media queries to specific items. Instead of creating a dedicated section that includes all of your media queries, you could nest them all and thereby pair them with the styles that they’re modifying. Here’s an example:

/*Example 7 Sass*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
  
  :hover {
    background: #aaa;
  }
  
  @media only screen and (max-width : 300px){
    width: 100px;
    height: 100px;
  }
}

/*Example 7 Compiled CSS*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
}

.button:hover {
    background: #aaa;
  }
  
@media only screen and (max-width : 300px){
  .button {
    width: 100px;
    height: 100px;
  }
}

Pros and Cons of Nesting Media Queries

The idea behind nesting media queries is a decent one, at least on the surface. As I mentioned above, it sticks the media queries in with the original styles that they’re modifying, which is a decent organizational schema. It also cuts down on the necessary repetition of the selectors, which is a good thing.

Unfortunately, in my opinion, these benefits are outweighed by the fact that this produces some messy code. This issue clearly presents itself as your code becomes more complex. Here we have two different elements, each taking advantage of the nested media query feature.

/*Example 8 Sass*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
  
  @media only screen and (max-width : 300px){
    width: 100px;
    height: 100px;
  }
}

.nav {
  width: 960px;
  margin: 10px;
  padding: 5px;
  
  @media only screen and (max-width : 300px){
    width: 300px;
    margin: 5px;
  }
}

The Sass above will compile to the CSS below. As you can see, each media query is written out individually and appears right after the styles that it’s associated with.

/*Example 8 Compiled CSS*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
}

@media only screen and (max-width : 300px){
  .button {
    width: 100px;
    height: 100px;
  }
}

.nav {
  width: 960px;
  margin: 10px;
  padding: 5px;
}

@media only screen and (max-width : 300px){
  .nav {
    width: 300px;
    margin: 5px;
  }
}

If you’ve been working with responsive design, then you probably immediately have some problems with the way this code is compiled. To see what I mean, let’s look at how I would code this out by hand in pure CSS if I were doing it manually.

/*Example 8 Manually Written CSS*/
.button {
  width: 300px;
  height: 100px;
  background: #eee;
}

.nav {
  width: 960px;
  margin: 10px;
  padding: 5px;
}

/*MEDIA QUERIES*/
@media only screen and (max-width : 300px){
  .button {
    width: 100px;
    height: 100px;
  }
  
  .nav {
    width: 300px;
    margin: 5px;
  }
}

Call me crazy, but I think this is superior to what Sass is spitting out. I have a real problem with the fact that identical media query declarations aren’t combined. We see here that there was no reason to write out the 300px query twice when we could simply write it out once and throw both declaration blocks inside.

Further, the organization here is cleaner and easier to follow. Typically, when you’re working with media queries you’re in for a lot of tweaking. With the Sass way, you’re forced to hunt through your code and find every piece that needs changing. If your SCSS file contains hundreds of lines of code, this could be brutal. With vanilla CSS, we can put all of our media queries in one place and tweak them together as a unit.

Control Directives

Though I’m not particularly thrilled with any of the results that I received from mixing Sass with media queries, one area that I do think merits some exploration is control directives. This is a touchy subject because, if you’re not a programmer, control directives seem way too complex for CSS. Just keep in mind before you leave me a nasty comment that I’m just playing with some possibilities here.

Given the nature of media queries, I think control directives could hold some interesting functionality. After all, a media query itself is a lot like an if statement: if the viewport = x, do y. Using some fancy Sass, we can take this much further. See if you can figure out what’s going on here.

/*Example 9 Sass*/
$thewidth: 500px;

@mixin changecolor {
  @if $thewidth < 400px {color:blue;}
}

.someclass {
  width: $thewidth;
}

@media only screen and (max-width : 900px){
  .someclass {
    $thewidth: $thewidth - 100px;
    width: $thewidth;
    @include changecolor;
  }
}

@media only screen and (max-width : 800px){
  .someclass {
    $thewidth: $thewidth - 100px;
    width: $thewidth;
    @include changecolor;
  }
}

Basically, I set up an item with a width that reduces its width by 100px for every media query. Then I set up a mixin that changes the color of the element if its width gets below a certain point.

This technique could be useful for a lot of different things such as changing a script font to something standard when the size gets too small. Here's a slightly modified version that watches until the object is less than 400px wide and then sets the height to half the width.

/*Example 10 Sass*/
$thewidth: 500px;

@mixin changewidth {
  @if $thewidth < 400px {height: $thewidth / 2}
}

.someclass {
  width: $thewidth;
  height: 300px;
}

@media only screen and (max-width : 900px){
  .someclass {
    $thewidth: $thewidth - 100px;
    width: $thewidth;
    @include changewidth;
  }
}

@media only screen and (max-width : 800px){
  .someclass {
    $thewidth: $thewidth - 100px;
    width: $thewidth;
    @include changewidth;
  }
}

Here's how this compiles. As you can see, the height wasn't changed on the first media query because the width isn't less than 400px. On the second media query though the width is 300px so the height was changed to 150px. The nice about this is that the compiled code is concise and doesn't have any of the crazy logic from above.

/*Example 10 Compiled CSS*/
.someclass {
  width: 500px;
  height: 500px;
}

@media only screen and (max-width : 900px) {
  .someclass {
    width: 400px;
  }
}

@media only screen and (max-width : 800px) {
  .someclass {
    width: 300px;
    height: 150px;
  }
}

Conclusion

I would've really loved to have presented a discussion about how Sass is a game changer when it comes to media queries, but at this point in time I think the opposite is true. It's probably best to steer clear of anything but the most basic Sass features in your implementation of media queries. Feel free to use variables and mixins just like always (accept in the breakpoint declaration), but watch out for inheritance working differently than you expect and think twice before nesting a media query because in the long term you're probably creating more work for yourself.

One exception that I can think of is control directives, which are fairly advanced and hold some fascinating possibilities when combined with media queries.

Have you tried implementing Sass in conjunction with media queries? Did you discovered any cool tricks or did you decide to avoid the practice altogether? Let us know in the comments.

Comments & Discussion

17 Comments

  • http://www.lexi-soft.co.uk umesh ramidi

    nice article with complete explanation step by step

  • LessLessMoreMore

    “Preprocessors like Sass are helping us flex our development muscles in nearly every area of our CSS. Variables, mixins, inheritance and many more great features make coding easier and more concise than ever.”

    People who write these Sass/Less articles act like CSS is Perl or Java. It’s CSS! Flex what muscles? Using variables and crap to make CSS feel more like a language when it was already dirt-simple to begin with? Aside from situations like using one variable to assign a color to thousands of elements, which can be updated by changing that one variable, some Sass/Less stuff almost seems like needlessly complicating things. For one, because you have to learn Sass/Less when you probably already have CSS mastered.

    FYI, I’ve dabbled with Sass, Less and Stylus. I genuinely think this is nothing more than a fad. Like most things in web development. It’s hot now. In a year or two it won’t be.

  • http://www.thevectorbox.com The Vector Box

    Thank you for posting this I found the information very useful, great breakdown.

  • http://r.osey.me Rose

    @LessLessMoreMore I disagree that it’s just a fad! I’ve been working with CSS for over ten years, and I hopped onto the SASS bandwagon a few months ago. I absolutely adore it. Especially when paired with compass, SASS is sooo awesome to work with.

    I think the big thing isn’t variables (although I do like being able to set something like
    $main-font: Helvetica, Arial, Sans-serif; and not having to type the whole thing out more than once), the big thing is mixins. The power of mixins is soo huge.

    For example:

    +border-radius(4px) will compile to *all* possible extensions (-moz, -webkit, etc).

    It gets even more beautiful when you do something like
    +border-right-radius(4px) and it has to output all of this for you:

    -webkit-border-top-right-radius: 4px;
    -webkit-border-bottom-right-radius: 4px;
    -moz-border-radius-topright: 4px;
    -moz-border-radius-bottomright: 4px;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;

    Not having all that clutter your file is a big thing. I love keeping everything so much simpler. And setting up my own mixins that are popular for a site I’m working on (here’s an example mixin that creates a faded line underneath an element using gradients. It’s a lot of code and it’s so nice to just ‘include’ it whenever I need it.)

    =horizontal-faded-line($first-color: #000, $second-color: #000, $how-far: 5%)
    &:after
    display: block
    content: “”
    +filter-gradient($first-color, $first-color, horizontal)
    $experimental-support-for-svg: true
    +background-image(linear-gradient(left, rgba($first-color,0) 0%,rgba($first-color,1) $how-far,rgba($first-color,1) 60%,rgba($first-color,1) 100%-$how-far,rgba($first-color,0) 100%))
    height: 1px
    width: 100%
    position: absolute
    bottom: 0
    left: 0
    &:before
    display: block
    content: “”
    +filter-gradient($second-color, $second-color, horizontal)
    $experimental-support-for-svg: true
    +background-image(linear-gradient(left, rgba($second-color,0) 0%,rgba($second-color,1) 20%,rgba($second-color,1) 60%,rgba($second-color,1) 80%,rgba($second-color,0) 100%))
    height: 1px
    width: 100%
    position: absolute
    bottom: -1px
    left: 0

    —–

    As for SASS and media queries, I’m still trying to figure out the best way to handle them! I’m excited to try out some of the examples on this page! :)

  • LessLessMoreMore

    @Rose: TL;DR, and I still disagree. Nothing wrong with the CSS that’s worked fine for nearly 15 years. Am I saying Sass/Less/etc are bad? No. Anything that gets the job done for YOU is never bad. I’m just disagreeing with their huge explosion right now, because they’re not necessary. Good or bad, they’re “hot” right now. That makes them a fad.

  • LessLessMoreMore

    p.s. @Rose: 10 years of CSS experience? You look like a teenager. College-age at best. I’m reserving my right to express skepticism in this case.

  • Rose

    Hey now, that’s bordering on troll talk! I’m 25. So fair enough, ten years ago I was fifteen and all my experience stemmed from personal projects and experimentation, but that doesn’t make it invalid! :)

  • Joshua Johnson

    C’mon LessLessMoreMore, play nice and keep it above the belt. Your opinions are valid, but keep your personal attacks in check and argue like a grown up.

    As for your comments, the point is not whether or not it’s a fad. The fact is that TONS of people are jumping on this bandwagon, meaning there’s a genuine desire for these kinds of features in the greater CSS community. You have every right to hate on preprocessors, but you should at least hone in on the proper argument. I’ve not seen a single developer suggest that preprocessors are necessary. The very fact that they output vanilla CSS proves that they aren’t necessary. But they are a useful tool that save some developers a lot of time.

    I also fundamentally disagree with this statement: “Nothing wrong with the CSS that’s worked fine for nearly 15 years…”

    Given this argument CSS3 shouldn’t exist! Why add new features? CSS was fine 15 years ago and it’s fine now. If it ain’t broke, don’t fix it!

    Telling people to stop innovating and thinking of ways to improve key web technologies is against literally everything the greater web development community stands for. The web dev community isn’t worse off for ideas like Sass, it’s better for them, even if they’re only passing fads. It gives life to new ideas and a better web in the long run.

  • http://www.mavoric.net David

    Nice article didn’t know you could use variables in CSS and always wondered if they would ever include it.

    It would however be quite nice to know the browser compatibility includes the extent of mobiles.

  • http://r4y.com.mx Raymundo Rabago

    I think the problem is to maintain the semantics of the code, for that one must learn to use both CSS and LESS/SASS.
    I still don´t know what should be the correct way to work or whether this should include time savings

  • http://jimmydid.it/ Jim Silverman

    Lesslessmoremore, CSS hasn’t worked fine for 15 years. That’s why table-based layouts existed. They were a stop-gap measure for the deficiencies of CSS. The language caught up.

    Preprocessors are the new tables. I can almost guarantee that CSS4 includes much of the functionality from them.

  • http://twitter.com/ariaminaei Aria

    Great article. Saved me quite a bit of experimentation time.

  • http://www.svengiesen.de Sven Giesen

    For the $breakpoint example..

    Untested, but i think (max-width : #{$breakpoint}) should work.

  • http://moox.fr/ MoOx

    Breakpoints are possible with the latest version. I use this behavior here https://github.com/MoOx/compass-recipes/blob/master/stylesheets/recipes/_media-queries.scss

  • http://chriseppstein.github.com Chris Eppstein

    Hi there,

    Sass developer here. Wanted to let you know about some new features we have in the next release that will really help with media queries.

    First we will allow script to author full media queries to be used via interpolation like so:

    
    $iphone: "only screen and (max-width : 320px)";
    @media #{$iphone} { ... }
    

    Second, we will allow any uninterpolated scripting on either side of the : in a parenthized query expression. Like so:

    
    $min-width-name: min-device-width;
    $max-width-iphone: 320px;
    @media screen and ($min-width-name: $max-width-iphone + 1px)  { ... }
    

    Lastly we’ll allow you to pass content to a mixin so you can build a generic respond-to mixin like this: https://gist.github.com/1215856#file_6_media_queries.scss

    Cheers,
    Chris

  • http://blog.founddrama.net Rob F.

    Nice write-up, Josh.

    You may want to update examples 6 and 7 though — they’re not technically correct. To get the kind of output you’re describing, you need to use the & symbol (or else the :hover pseudo won’t be properly “attached” the example .button class).

  • Keith

    I was playing around with sass and media-queries today and I couldn’t figure out why the variable wasn’t working as the breakpoint. This just confirmed to me that I’m not crazy and I’ll just have to set the breakpoints without variables.

    And LesslessmoreEtc… the tone of your posts were condescending. What would the person’s age have to do with anything. If she’s got the experience, so what if she was 15 when she first tried it. I’m almost 40 and been doing this since Mosaic was hot. Sass/Less is not a fad. It’s really useful and saves a lot of time. As someone used to developing and using variables and functions in php, java, javascript, perl, I welcomed this technology with open arms. Another advantage of it is using multiple sass files and having sass concatenate them all into one compressed css file.
    Don’t just put something down because you don’t like it.

Subscribe

Membership
About the Author