Sass: The @media Directive

One of the cornerstones of responsive design is the media query. The @media directive provides a mechanism for serving different styles to different devices with different characteristics. Sass both supports and extends the @media rule.

Illustration of a film projector

Last week I began a series about @-rules and directives. I talked about CSS @-rules in general and then walked you through a few Sass specific rules. Today I want to focus on one specific @-rule you likely use in your projects. I want to talk about the @media directive and what Sass adds to the directive.

The @media Directive

The @media directive in Sass works pretty much like the CSS @media rules you already use. Sass adds one very nice feature to the CSS rule, though it’s not without some controversy. Sass lets you nest media queries inside the CSS rulesets they modify.

For example the following is permitted in Sass.

1
2
3
4
5
6
7
.container {  
 max-width: 95%;

 @media screen and (min-width: 30em) {  
   max-width: 80%;  
 }
}

If you get annoyed scrolling up and down a stylesheet between a set of rules and the media queries at the end of your document, you’ll appreciate nested @media directives. Again the merits of nesting media queries is not without debate and I’ll get that in a bit.

First here’s how the previous Sass compiles to CSS.

1
2
3
4
5
6
7
8
9
.container {  
  max-width: 95%;  
}

@media screen and (min-width: 30em) {  
 .container {  
   max-width: 80%;  
 }
}

Notice that the @media rules bubbled up out of the ruleset for .container as CSS doesn’t support the nested syntax.

For some people nesting media queries this way helps make their Sass code more readable and ultimately more maintainable.

You can also nest @media rules inside other @media rules.

1
2
3
4
5
6
7
@media screen {  
  .container {  
    @media (min-width: 30em) {  
      max-width: 80%;  
    }
  }  
}

When nesting one @media directive inside another, the queries will be combined using the AND operator when the Sass is compiled to CSS.

1
2
3
4
5
@media screen and (min-width: 30em) {  
 .container {  
   max-width: 80%;  
 }
}

The media queries (screen and min-width: 30em) have been combined into a single media query that contains them both.

Expressions in @media Directives

In Sass, @media directives can also include expressions like variables, functions, and operations. Since I haven’t covered either of the latter two yet, here’s an example using variables.

1
2
3
4
5
6
7
8
9
10
$media: screen;  
$width: 30em;

@media #{$media} {  
  .container {  
    @media (min-width: $width) {  
      max-width: 80%;  
    }
  }  
}

First I created two variables ($media and $width) and then I used them in place of their values (screen and 30em) inside the @media directives.

Notice that I used variable interpolation, #{}, to include the variable $media, but didn’t when I included $width in the nested media query. In the latter case I simply include the variable. Either way is allowed.

The Sass compiles to the same CSS as the previous example.

1
2
3
4
5
@media screen and (min-width: 30em) {  
 .container {  
   max-width: 80%;  
 }
}

Should You Nest Media Queries?

I’ve been frustrated more than once by having to scroll up and down a stylesheet to compare properties and values inside and outside media queries, especially when the original selector is near the top of the document and my media queries are at the end.

If you’ve ever felt the same, you’ll probably appreciate that you can nest your media queries and have them bubble up during compile.

However, you should understand the downside to nesting @media rules in Sass. While each @media directive will bubble up, they don’t combine, which could potentially leave a lot of repetitive @media directives in your CSS.

For example say you have two containers with different values for their max-widths, but each container changes the value of its width property under the same conditions (min-width: 30em).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.container-1 {  
 width: 95%;

 @media screen and (min-width: 30em) {  
   width: 80%;  
 }
}

.container-2 {  
 width: 90%;

 @media screen and (min-width: 30em) {  
   width: 75%;  
 }
}

Ideally you’d want the compiled CSS to show a single media query with the rules for both selectors inside a single media query. Perhaps something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.container-1 {  
  width: 95%;  
}

.container-2 {  
  width: 90%;  
}

@media screen and (min-width: 30em) {  
 .container-1 {  
   width: 80%;  
 }

 .container {  
   width: 75%;  
 }
}

Unfortunately Sass can’t currently do that. Instead the Sass above compiles to the following with two separate media queries using the exact same conditions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.container-1 {  
  width: 95%;  
}

@media screen and (min-width: 30em) {  
 .container-1 {  
   width: 80%;  
 }
}

.container-2 {  
  width: 90%;  
}

@media screen and (min-width: 30em) {  
 .container {  
   width: 75%;  
 }
}

For this example the extra bit of code isn’t a big deal, but for a real project the duplicate queries add file weight and also make it difficult to see everything that changes inside media queries with the same set of conditions. If you nest them, they’ll be spread throughout your document.

I don’t think there’s an automatic right or wrong answer to the question posed by this section. There are pros and cons to nesting and whether you nest your @media directives or not is up to you.

If you work on larger projects and/or tend to modify everything in a single media query at the same time, you probably don’t want to nest your @media rules.

On the other hand if you work on smaller projects and/or tend to modify one component of the design at a time you might prefer to nest your @media rules.

For example I’m more likely to adjust only one component of the design at a time. I find it easier to have all my navigation code, both inside and outside media queries, near each other when working on a site’s navigation. I also tend to work on smaller projects. I more likely to nest my @media directives because of both.

In general nesting your @media rules will make your code easier to read, but lead to less DRY CSS after compiling. That may or may not be an issue depending on the specifics of your project.

Closing Thoughts

Sass adds the ability to nest media queries inside the selectors they modify. This can be a good way to keep related code together and you may find it easier to maintain nested @media rules. I generally do.

However, depending on your project and how you prefer to work, you may find the cons of less DRY CSS outweigh the benefits of having styles inside media queries next to the styles they modify.

I don’t think there’s a right or wrong way when it comes to nesting @media directives and the choice is more personal preference than anything else.

Next week I’ll talk about another @-rule that you may have used before, the @import rule. I’ll walk you through using the directive in Sass an then show you how you can use @import to organize your file and directory structure so you can write more modular and reusable code.

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *