Write Better Media Queries with Sass

By  on  

Let's face facts: media queries can be a pain. They're difficult to write and they tend to get duplicated a lot. Sass includes a few helpful features that make media queries easier to work with. This article will show you these tricks and how you can use them to simplify your stylesheets.

The Basics

Let's take a look at a simple example.

p {
  font-size: 16px;
}

@media (min-width: 768px) and (max-width: 1023px) {
  p {
    font-size: 18px;
  }
}

@media (min-width: 1024px) {
  p {
    font-size: 20px;
  }
}

Here, we've set the font size of paragraphs to 16 pixels. When the viewport width is greater than or equal to 768 pixels, and less than or equal to 1023 pixels, the font size is 18 pixels. When the viewport is greater than or equal to 1024 pixels, the font size is 20 pixels.

Right away, you should be struck by how much code is required to do something as simple as responsive typography. As projects grow larger, the above style of writing media queries usually leads in one of two directions:

  • Media queries are hardcoded in the code and copied where they're needed. If we want to change a media query, we have to do it in many places.
  • The project is split up by viewport sizes. It's jarring and time-consuming to jump to multiple places to update a single element's styles.

Shortening Things Up

Sass allows variables to be interpolated. This means that we can move our media queries into variables and reuse them.

$tablet: "(min-width: 768px) and (max-width: 1023px)";
$desktop: "(min-width: 1024px)";

p {
  font-size: 16px;
}

@media #{$tablet} {
  p {
    font-size: 18px;
  }
}

@media #{$desktop} {
  p {
    font-size: 20px;
  }
}

Now, we can use the $tablet and $desktop media queries anywhere in our project without duplicating code. However, it's still annoying that our media queries are separate from the code they style. The Sass folks fixed this problem by allowing media queries to be nested.

$tablet: "(min-width: 768px) and (max-width: 1023px)";
$desktop: "(min-width: 1024px)";

p {
  font-size: 16px;

  @media #{$tablet} {
    font-size: 18px;
  }

  @media #{$desktop} {
    font-size: 20px;
  }
}

Mixins to the Rescue

The code above works, but it's a bit difficult to read. We can clean it up by using Sass's mixins. Mixins are groups of styles that can be reused throughout your code. One of the nifty features of mixins is they can use blocks of content. This lets us write mixins like this:

$tablet-width: 768px;
$desktop-width: 1024px;

@mixin tablet {
  @media (min-width: #{$tablet-width}) and (max-width: #{$desktop-width - 1px}) {
    @content;
  }
}

@mixin desktop {
  @media (min-width: #{$desktop-width}) {
    @content;
  }
}

Now, we can rewrite our example above to use these new mixins.

p {
  font-size: 16px;

  @include tablet {
    font-size: 18px;
  }

  @include desktop {
    font-size: 20px;
  }
}

We're not just limited to device sizes. We can write media queries for many other useful things, such as Chris Coyier's retina media query or a print media query.

@mixin retina {
  @media
    only screen and (-webkit-min-device-pixel-ratio: 2),
    only screen and (min--moz-device-pixel-ratio: 2),
    only screen and (-o-min-device-pixel-ratio: 2/1),
    only screen and (min-device-pixel-ratio: 2),
    only screen and (min-resolution: 192dpi),
    only screen and (min-resolution: 2dppx) {
    @content;
  }
}

@mixin print {
  @media print {
    @content;
  }
}

Wrapping Up

Media queries are great, and with Sass they can be even better. The next time you use one, remember these tricks to help make your code more readable and easier to maintain.

Landon Schropp

About Landon Schropp

Landon is a developer, designer and entrepreneur based in Kansas City. He's the author of the Unraveling Flexbox. He's passionate about building simple apps people love to use.

Recent Features

  • By
    Write Better JavaScript with Promises

    You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll...

  • By
    Conquering Impostor Syndrome

    Two years ago I documented my struggles with Imposter Syndrome and the response was immense.  I received messages of support and commiseration from new web developers, veteran engineers, and even persons of all experience levels in other professions.  I've even caught myself reading the post...

Incredible Demos

Discussion

  1. Ali Amini

    Thank you.
    I think the code be heavy with this way, isn’t it?

    p{
    	font-style: 18px;
    	@include tablet{
    		font-style: 20px;
    	}
    	@include desktop{
    		font-style: 25px;
    	}
    }
    
    h1{
    	font-style: 25px;
    	@include tablet{
    		font-style: 30px;
    	}
    	@include desktop{
    		font-style: 35px;
    	}
    }
    

    and compiled:

    p {
      font-style: 18px;
    }
    @media (min-width: 768px) and (max-width: 1023px) {
      p {
        font-style: 20px;
      }
    }
    @media (min-width: 1024px) {
      p {
        font-style: 25px;
      }
    }
    
    h1 {
      font-style: 25px;
    }
    @media (min-width: 768px) and (max-width: 1023px) {
      h1 {
        font-style: 30px;
      }
    }
    @media (min-width: 1024px) {
      h1 {
        font-style: 35px;
      }
    }
    
  2. I’m using a respond-mixing @import respond-to('medium') and a breakpoint-configuration in order to handle the media queries. https://github.com/macx/sass-mixins/blob/master/src/_layout/_respond.scss

  3. Brian

    This is great and it’s easier to read, but could potentially bloat your code a lot, adding each breakpoint under it’s selector increases file size considerably, specially on large scale sites. I’m not saying this is a bad idea for pre-compiled code, but it’d be very useful if SASS could group all breakpoints into groups.

    • @brian, I don’t think that is actually an issue once the CSS is GZIP’d. It’s good at handling things like that.

  4. Eugene

    @Brian, gulp-cssmin can do that (https://github.com/chilijung/gulp-cssmin), it groups all similar pieces of CSS into buckets. The code isn’t readable but it helps to reduce it’s size.

  5. Here is an alternative http://codepen.io/dsheiko/pen/KeLGy

    It works like that:

      @include media( "retina" ){
        border: 2px dotted #000;
      };
      @include media( "screen", ">bp1Width", "minWidth", "<bp1Width" ){
        background: green;
        color: white;
      };
      @include media( "screen", "<minWidth" ){
        background: blue;
        color: white;
      };
    
  6. I was looking for a good way to solve media queries with retina-display and dpi and this looks quite good!

    Thanks for sharing :)

  7. Excellent post. I prefer this method to structuring our files by breakpoint type. When files are structured by breakpoint you can end up with some developers in a team adding duplicate code because of improper overriding or difficulty finding the correct location of code.

    Also, it seems with gzip the bloat of extra media queries is minimal.
    http://sasscast.tumblr.com/post/38673939456/sass-and-media-queries

  8. Hey, we started using this solution in our team. We found it works really well! https://davidwalsh.name/write-media-queries-sass

  9. Nazar

    Note that you can do like that ($laptop, $desktop):

    @media #{$laptop}, #{$desktop}{
        background-size: 60% auto;
    }
    

    I like that!

  10. In my case i have $mobile, $tablet and $desktop. What if i have to write down media query for $not_mobile, do you mixin for this?

  11. Daniel Tonon

    mq-scss is the most powerful media query mixin I’ve come across.

    https://www.npmjs.com/package/mq-scss

    You can do crazy stuff like this:

    $MQ-example: (
      (inside, 1500px, 1200px) plus (min-height, 800px),
      (max, 600px)
    );
    
    @include mq($MQ-example) {
      // styles for screens less than 600px OR between 1500px and 1200px wide that are also a minimum of 800px tall
    }
    

    That’s a bit of a ridiculous example but it shows off a lot of mq-scss’s features.

  12. devendar

    why we write # before {$laptop} what was the use..?

    can you please explain me.

  13. Meghan

    It’s a small thing… but wouldn’t most people want to carry this style through the horizontal tablet view?
    And then start the larger size at 1025, not 1024?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!