Modular CSS with Media Queries and Sass

Modular blocksMost developers nowadays are recognizing that if you’re developing large applications that have different views and states, it is best to take a modular or object-oriented approach to your CSS development.

When you throw media queries into the mix, however, the you can lose some of the benefits of modularity — code that’s easy to read, update, and maintain.

Let’s look at different ways we can write our media queries, starting with what might be the most common approach — especially with those not yet using a preprocessor.

The Common Approach

To demonstrate what I think the majority of developers have done with media queries up to this point, here’s a simple example:

.box-module {
  float: left;
  background: #ccc;
  margin-bottom: 1.2em;
}

.some-other-module {
  border: solid 4px #222;
  padding: 1.2em;
  margin-bottom: 1.2em;
}

/* ... other non-media query styles would go here ... */

@media screen and (max-width: 720px) {
  .box-module {
    float: none;
    clear: both;
    margin-bottom: .5em;
  }

  .some-other-module {
    border: solid 2px #222;
    padding: .8em;
    margin-bottom: .5em;
  }
}

@media screen and (max-width: 320px) {
  .box-module {
    margin-bottom: .2em;
  }

  .some-other-module {
    padding: .2em;
    margin-bottom: .2em;
  }
}

/* ... other media query styles would go here ... */

In the first two rule sets in this example, we have two separate elements on the page styled without media queries. (As a side point, those first two rule sets could be our mobile first styles, but it really doesn’t matter for the purpose of this theoretical example.)

After we declare those primary styles on our two elements, we then declare our media queries, repeating the same selectors, but with altered styles to suit the media features defined in the media queries.

The key part to take note of here is how this would look if we had thousands of lines of code. If that were the case, the media query styles that apply to the same modules would be significantly separated from the original styles for those same elements. Not ideal, especially when you’re trying to keep related chunks of CSS together.

A More Modular Approach

The purpose of modularizing our CSS is to help it become much more flexible and scalable, but also to help make it easier to read and easier to maintain. As we can see from the previous example, that approach (where you throw all your media queries at the bottom of the stylesheet) can, to some extent, hinder maintainability.

Let’s rewrite the example from above using a more modular approach:

.box-module {
  float: left;
  background: #ccc;
  margin-bottom: 1.2em;
}

@media screen and (max-width: 720px) {
  .box-module {
    float: none;
    clear: both;
    margin-bottom: .5em;
  }
}

@media screen and (max-width: 320px) {
  .box-module {
    margin-bottom: .2em;
  }
}

.some-other-module {
  border: solid 4px #222;
  padding: 1.2em;
  margin-bottom: 1.2em;
}

@media screen and (max-width: 720px) {
  .some-other-module {
    border: solid 2px #222;
    padding: .8em;
    margin-bottom: .5em;
  }
}

@media screen and (max-width: 320px) {
  .some-other-module {
    padding: .2em;
    margin-bottom: .2em;
  }
}

This might seem a bit odd and counterproductive to those who are accustomed to putting their media queries at the bottom of their CSS file. This approach is discussed as an option in Jonathan Snook’s book on modular CSS, where he discusses changing state with media queries.

Even before I started reading Jonathan’s book, I’ve considered this approach, but pretty much immediately discarded the thought for probably the same reason that many of you are cringing at it right now: The fact that this clearly goes against the DRY (Don’t Repeat Yourself) principle.

Pros and Cons

As we’ll see in the following sections, if you’re using Sass, the repetition becomes less of a factor and, from what I can tell, many developers are adopting this method.

But first, let’s consider the pros and cons of this approach for pure CSS that’s not using Sass.

Cons:

  • Lots of repetition, which modular CSS discourages
  • More code, which could amount to hundreds of extra lines, thus larger files
  • Could be confusing to future maintainers, especially if not well documented
  • If you change a single media query state (e.g. “max-width: 720px”), you have to change it for all modules

Pros:

  • Easier to read/understand/maintain for the original developer(s)
  • Could be much easier to read/understand for future maintainers, especially if well documented
  • Easier to write the initial code (more on this below)

Resolving Some of the Cons

To purists, the cons may seem like big problems. But I don’t think they’re as big as they seem, even though they may appear to outweigh the pros.

First, I’m not concerned about the larger amount of code. If I’m minifying and gzip’ing, the difference in code quantity will be negligible.

Second, as suggested by the final “pro” point, if you’re writing your media query for a module right next to the initial styles for the module itself, I believe this will make it significantly easier when debugging that particular part of the code. Since you have all the different styles for a particular module together — including all the different state changes via media queries — you’ll quickly be able to make any changes and won’t have to put off debugging the same module again at smaller breakpoints.

Modular Media Queries with Sass

When you factor in a preprocessor like Sass, this sort of thing becomes exponentially easier. If you’re not familiar with some of Sass’s media query-specific capabilities, it’s worth checking out that part of their documentation, which talks about doing this exact thing.

So let’s rewrite our example from above using Sass with nesting and variables:

$media: screen;
$feature: max-width;
$value: 720px;
$value2: 320px;

.box-module {
  float: left;
  background: #ccc;
  margin-bottom: 1.2em;

  @media #{$media} and ($feature: $value) {
    float: none;
    clear: both;
    margin-bottom: .5em;
  }

  @media #{$media} and ($feature: $value2) {
    margin-bottom: .2em;
  }

}

.some-other-module {
  border: solid 4px #222;
  padding: 1.2em;
  margin-bottom: 1.2em;

  @media #{$media} and ($feature: $value) {
    border: solid 2px #222;
    padding: .8em;
    margin-bottom: .5em;
  }

  @media #{$media} and ($feature: $value2) {
    padding: .2em;
    margin-bottom: .2em;
  }

}

Here, in addition to variables, we’re using Sass’s nesting capabilities, which don’t require repeating the selector inside the media query block. This will compile to basically what we did in the Sass-less modular example above.

But we’re not finished yet. Let’s make one final improvement to this code, using a fairly new Sass feature.

Modular Media Queries Using Sass with @content

We’re going to improve on this even more by using Sass’s ability to pass content blocks to a mixin.

And I should point out that this is nothing new; the official Sass blog discussed this shortly before Sass 3.2 was released, and a few other blogs have discussed the benefits of using Sass to keep media query styles near the styles they override.

Here’s our updated code using Sass’s @content directive:

$media: screen;
$feature: max-width;
$value: 720px;
$value2: 320px;

@mixin modular-mq($breakpoint) {
  @if $breakpoint == medium {
    @media #{$media} and ($feature: $value) { @content; }
  }
  @else if $breakpoint == small {
    @media #{$media} and ($feature: $value2) { @content; }
  }
}

.box-module {
  float: left;
  background: #ccc;
  margin-bottom: 1.2em;

  @include modular-mq(medium) {
    float: none;
    clear: both;
    margin-bottom: .5em;
  }

  @include modular-mq(small) {
    margin-bottom: .2em;
  }

}

.some-other-module {
  border: solid 4px #222;
  padding: 1.2em;
  margin-bottom: 1.2em;

  @include modular-mq(medium) {
    border: solid 2px #222;
    padding: .8em;
    margin-bottom: .5em;
  }
 
  @include modular-mq(small) {
    padding: .2em;
    margin-bottom: .2em;
  }
}

The examples with the variables and whatnot are not necessarily going to be done like that. You can hardcode the values right into the mixin, or you could have some conditional logic that decides what the values will be. This is more or less a theoretical example.

Conclusion

Even with the two Sass examples, you’re still, to some extent, repeating your media query breakpoints for every selector that needs an override. So I can understand if many developers don’t like this approach.

Overall, I think the concept of keeping related styles near each other is much more in line with what we like to see in CSS and especially in modular or OOCSS. I always find it annoying when I have to sift through two different parts of a stylesheet to make a change to a single element on the page — a problem that has occurred much more often with the advent of media queries. I think this approach would resolve this type of thing.

I should also mention that there are some projects that aim to help in this area including Sassy Media, Susy, sass-mediaqueries and probably others that I haven’t yet seen. (Update: Scott Kellum tweeted to me about Breakpoint, which is worth checking out just for the Point Break movie homage.)

So how has everyone been doing this sort of thing? Do you mind having your media queries scattered throughout your code, repeating the same queries for different selectors?

Advertise Here

20 Responses

  1. Mike:

    Two of the cons (repetition, and having to change values in multiple places) simply disappear if you stop thinking of media queries as these global, page level things, and instead use values appropriate for where that particular module starts to break. If two modules happen to have the same values in their media query, that’s still DRY, since you’re not repeating information, you just have two bits of information that — for the time being — happen to have the same value. You should be able to change the one without having to worry about the other.

  2. simmu:

    I believe this approach is great for a small size application. When you are working with a bigger site like Facebook, this approach will create a lot of cons you mentioned by magnitude. Again using sass might make it look smaller but the end result will still be huge.

  3. I use the Sass plugin Breakpoint 2 for this (https://github.com/canarymason/breakpoint/issues/35). I picked it in large part to deal with legacy IE. (I use the “No Wrapping Selector, Separate Stylesheet” approach.)

    I work on relatively small sites, so the extra code is not that much extra. And of course it’s nice to have all the code for each module in one place.

  4. I am a little on the fence when it comes to this style of working. The extra bloat it creates is not nothing – sure, gzip compresses repeated strings pretty well but if you’re repeating a long string 20-30 times throughout a document it adds up!

    Currently I’m using a hybrid of the two extremes – using media queries at the bottom of each partial. This gives me 5-10 sections that are not too far away from the original selector. Ideally, there would be a way in SASS to allow all media queries to be combined and put at the bottom of the resulting stylesheet.

  5. Lovick3013:

    Using multiple CSS files can help separate the logic. Add root CSS styles in a parent style sheet to define the immutable properties of each element. Attach a separate style sheet(s) to modify element properties based on media queries. Essentially, create CSS classes that inherit from shared base classes. A preprocessor certainly reduces the lines of code but the overall concept remains the same. Each “breakpoint” is an object that inherits from “website”, either directly or successively, so If you find yourself with successive, duplicate property assignments then you’re probably doing it wrong. The principals of DRY become unavoidable.

  6. Ferdy:

    I guess it is a matter of preference. I still prefer the classic approach: single media queries at the bottom of my main style sheet. Each has one line only: the inclusion of the partial SCSS file for that breakpoint.

    You could argue that this means that one has to switch constantly between multiple files to get the full styles for an element, but I counter that quite strongly. Those partials are tiny and deal with exceptions only. Media query specific styles should be exceptional, if you have a lot of styles in there, possibly you are doing things wrong. Likely you have a problem in your default styles.

    To illustrate this point: I’m doing a responsive redesign of a web application that has 119 unique template pages. Still, the responsive partials are so small that their code fits on a single screen. Therefore, file switching is not a practical problem.

    I think in this article you are successful in minimizing the cons of the inline approach, but I still don’t think the pros are that convincing. But again, it’s a matter of taste.

  7. Very comprehensive and informative article.

    Thanks

  8. In my large, modular projects, where we have a builder running to compress and combine css, I like to have a (helluva) lot of css files, pretty much one for each block and element; so stuff like: “buttons.css” , “inputs.css” and “header.css” , “login.css” , “logo.css”… etc, and keep the files quite small ( less than 60 lines ) and put specific media queries in each file. again it’s repetitive and doesn’t help the file size, but the maintainability is superb and the builder takes care of smooshing it down… with a clever builder it would put all the repeated media queries together, too, I guess :)

  9. Useful mixin using @content, I wasn’t aware of this feature!

  10. Is there any performance problems when you serve a browser a CSS file with many replicated media queries? Does anyone know if it affects speed?

    • Not really, the speed implications are tiny compared to transforms, animations, size of page (if it’s really big), etc.

    • I agree with Daniel. I haven’t heard anything about performance issues with extra media queries, so I don’t think it’s a big deal.

      CSS performance should be one of the last things on your list when considering page performance. Htttp requests, using a CDN, gzip, concatenation, minification, JS performance — all of those areas and more should be the focus of performance optimization.

      Once you have those ironed out, then focus on smaller things like CSS performance, but I would not spend unusual amounts of time optimzing CSS before I do all those other things.

  11. I think this is the modular way of writing media queries, the last thing I’d like would be that sass could group the mediaqueries only on the production CSS, so I can divide them in files to build mobile first and maybe serve them only when needed with something like eCSSential.

  12. A good e-book to learn more about Modular CSS: http://smacss.com/

  13. ChrisN:

    In “Modular Media Queries Using Sass with @content” section above there is a little code error ….

    
    @mixin modular-mq($breakpoint) {
      @if $breakpoint == medium {
        @media (#{$media} and $feature: $value) { @content; }
      }
      @else if $breakpoint == small {
        @media (#{$media} and $feature: $value2) { @content; }
      }
    }
    

    Opening bracket before #{$media} should actually be before $feature:

    
    @mixin modular-mq($breakpoint) {
      @if $breakpoint == medium {
        @media #{$media} and ($feature: $value) { @content; }
      }
      @else if $breakpoint == small {
        @media #{$media} and ($feature: $value2) { @content; }
      }
    }
    

    Excellent article though! I shall be using @content and the media query mixin - thanks!

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field. If you use keywords, your comment will be deleted, or your name will be replaced with the alias from your email address. No foul language, please. Thank you for cooperating.

Instructions for code snippets: Wrap inline code in <code> tags; wrap blocks of code in <pre> and <code> tags. When you want your HTML to display on the page in a code snippet inside of <code> tags, make sure you use &lt; and &gt; instead of < and >, otherwise your code will be eaten by pink unicorns.