My-Media Mixin: Old IE Fixin’ Without JS

Over the course of this year, we've been asked quite a few times how we structure our CSS for responsive projects. For most of 2012 we've been using a system that allows us to serve a responsive experience to user agents that support media features and a non-responsive, larger experience ("desktop," if you like) to user agents that don't support media features (primarily IE8 and older).

To achieve this, we've used SCSS aggregates that contained media queries and SCSS partials for each breakpoint that had no media queries. This allowed us to create two aggregate files — one with media queries (mq.scss) and one without (nomq.scss). These two aggregates would perform a SCSS @import on all the same breakpoint-based partials (e.g. _30em.scss or _600px.scss). In the head of the document, we have then linked to the mq.css file for all user agents and the nomq.css file inside a conditional comment for older IE. See Nicolas Gallagher's post on this same kind of technique.

It's a bit complicated to read, but fairly simple to execute.

Enter My-Media

I've known for awhile now that other folks have been using newer media query features in SCSS to place their styles directly inline — in the place where that element is being styled. I like this idea because it keeps styles grouped logically. However, it means that older IE (or any user agent that doesn't support media features) will be served only the styles outside of media queries. This is not a huge deal if you're writing your CSS "desktop-first." However, we start with the smallest styles and build up at Sparkbox, so old IE would generally get a linear, single column layout.

Now, some people are ok with this, but I felt there might be another way. After a bit of digging and some fun conversations with my guys (Rob Harr, Rob Tarr, Adam Simpson and Ethan Muller, in particular), we have an interesting solution.

It's called My-Media. It consists of two different versions of two SCSS mixins. The two mixins are:

  1. my-media: a replacement for @media in your SCSS
  2. my-base: which all other styles use

These two mixins are defined differently in two separate files. One for user agents that support media queries:

1 2 3 4 5 6 7 8 9 10 11 12 13
// _my-media-mq.scss
@mixin my-base {
// just pass the content through
@content;
}
 
@mixin my-media($theQuery, $serveToOldIE: true) {
// interpret the media query passed in
// and wrap the content in that query
@media #{$theQuery} {
@content;
}
}
view raw _my-media-mq.scss hosted with ❤ by GitHub

And one for user agents that don't support media queries:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// _my-media-nomq.scss
 
@mixin my-base {
// never serve the base styles to old IE
// (it will already receive this in mq.css)
// note, SCSS won't compile unless you use @content
@if false {
@content;
}
}
 
@mixin my-media($theQuery, $serveToOldIE: true) {
// allows us to exclude queries from old IE
// by passing "false"
@if $serveToOldIE {
@content;
}
}
view raw _my-media-nomq.scss hosted with ❤ by GitHub

These are used in your SCSS like so:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// _styles.scss
 
// all styles not in media queries are served through "my-base"
@include my-base {
header, section, aside, footer {
background-color: #ddd;
margin: 1em 5%;
padding: 1em 5%;
width: 80%;
float: left;
}
}
 
// all media query styles are served through "my-media"
@include my-media("(min-width: 40em)") {
section, aside {
width: 30%;
}
aside {
float: right;
}
}
 
// the "false" parameter hides these styles from old IE
@include my-media("(min-width: 60em)", false) {
section {
width: 45%;
}
aside {
width: 15%;
}
aside {
float: right;
}
}
view raw _styles.scss hosted with ❤ by GitHub

Then, the aggregate files build our two CSS files uniquely by redefining these mixins like so:

1 2 3 4 5 6 7 8 9 10
// mq.scss
// For user agents that support media queries
@import "my-media-mq"; // where my-base and my-media are defined
@import "styles";
 
 
// nomq.scss
// For user agents that don't support media queries
@import "my-media-nomq"; // where my-base and my-media are defined
@import "styles";

By redefining the mixins for each user agent, we're able to control what is put into the generated CSS. This gives us the ability to serve a large resolution layout to old IE, build with mobile-first CSS, and put our media queries right inline where it makes the most sense.

The one drawback is that it does require you to put all styles in either my-base or my-media. I think this could be worth it, so we'll be experimenting with this more in the coming weeks!

Thanks again to my team for all the insight and feedback. Great ideas are never built single-handedly...

Ideas on how to make this better? Chime in, please!

Follow-up: since publishing this post, several smart people have pointed me to Breakpoint. This looks pretty amazing and shares a lot of the same goals. We'll be investigating it for sure!