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
    Create a CSS Cube

    CSS cubes really showcase what CSS has become over the years, evolving from simple color and dimension directives to a language capable of creating deep, creative visuals.  Add animation and you've got something really neat.  Unfortunately each CSS cube tutorial I've read is a bit...

  • By
    Convert XML to JSON with JavaScript

    If you follow me on Twitter, you know that I've been working on a super top secret mobile application using Appcelerator Titanium.  The experience has been great:  using JavaScript to create easy to write, easy to test, native mobile apps has been fun.  My...

Incredible Demos

  • By
    Upload Photos to Flickr with PHP

    I have a bit of an obsession with uploading photos to different services thanks to Instagram. Instagram's iPhone app allows me to take photos and quickly filter them; once photo tinkering is complete, I can upload the photo to Instagram, Twitter, Facebook, and...

  • By
    Fullscreen API

    As we move toward more true web applications, our JavaScript APIs are doing their best to keep up.  One very simple but useful new JavaScript API is the Fullscreen API.  The Fullscreen API provides a programmatic way to request fullscreen display from the user, and exit...

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!