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 and entrepreneur based in Seattle. He's the author of the Free Flexbox Starter Course and Unraveling Flexbox, a book on how to create modern, responsive layouts in CSS. He's passionate about building simple apps people love to use.

Recent Features

  • By
    Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

  • By
    Page Visibility API

    One event that's always been lacking within the document is a signal for when the user is looking at a given tab, or another tab. When does the user switch off our site to look at something else? When do they come back?

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!

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