A mixin and SASS structure for simple media query support
22 Apr 2013
It started with a bit of inspiration...
Following both attendance of the excellant RWD summit and reading about the SASS media query mixin by Always Twisted, I decided to re-think my project SASS structure going forward. Check this baby out:
@mixin media-query-bp($unit, $query1: min, $query2: width, $target: "") {
// Check if breakpoints are in use
$mqtarget: "";
@if $media-queries-supported == true {
@if $target != "" {
@media #{$target} and (#{$query1}-#{$query2}: #{$unit}) {
@content;
}
}
@else {
@media (#{$query1}-#{$query2}: #{$unit}) {
@content;
}
}
}
@else {
.no-mq {
@content;
}
}
}
This modified version of the aforementioned media query mixin allows for an optional "target" to be set (screen, tv, handheld, etc). Default is assumed to be no target (same as writing "all"). This media query mixin can be called in the following way:
@include media-query-bp(30em) {
body {
background-color: #fff;
}
}
This results in the following:
@media (min-width: 30em) {
body {
background-color: #fff;
}
}
OR the following:
.no-mq
{
body {
background-color: #fff;
}
}
What is $media-queries-supported all about?
Put simply, it is a variable set before the mixins are imported to denote whether media queries should be output or not. By using this simple variable defined once in each of "mq.scss" and "no-mq.scss", whenever the mixin is called, differing output can be generated to each of the resulting "mq.css" and "no-mq.css", meaning we can target browsers that do not support media queries effectively, without all of the media query CSS being loaded first (which won't even be used. What a waste of bandwidth, time and most likely, someones data allowance!)
Additional arguments to the mixin
The media query mixin may be called with additional (although totally optional) arguments. These are:
$query1 : First part of a parameter declaration (as in the "min" part of min-width). "min" is assumed to be the default and will be used should no value be specified.
$query2 : Second part of a parameter (as in the "width" part of min-width). "min" is assumed to be the default and will be used should no value be specified.
$target : Device to target, such as "handheld". "All" is assumed should nothing be specified (this results in no output, as "all" and no device are assumed to be the same thing according to the spec).
Additional usage example
@include media-query-bp(420px, max, height, screen) {
body {
background-color: #fff;
}
}
This results in the following:
@media screen and (max-height: 420px) {
body {
background-color: #fff;
}
}
Organising your SASS files for minimal maintenance
Next up is a way to arrange SASS project files to minimise the amount of maintenance needed as time moves on. By importing all of your SASS using an "_import.scss partial", we can create two top-level SASS project files, namely "mq.scss" and "no-mq.scss". Both files contain two lines, as per the examples below:
mq.scss:
$media-queries-supported: true;
@import "imports";
no-mq.scss:
$media-queries-supported: true;
@import "imports";
_imports.scss looks like so:
// Import variables
@import "variables";
// Import mixins
@import "partials/mixins";
// Import reset:
@import "partials/reset";
// Import everything else
@import "partials/typography";
@import "partials/colour";
@import "partials/layout";
Lastly, using IE conditional comments, we can add a "no-mq" class on our html element like so:
<!--[if lt IE 9]><!--><html class="no-mq" lang="en"><!--<![endif]-->
Additional tip to save bandwidth
Using a slightly different conditional comment hack as above, we can ensure that we only serve one stylesheet depending on the version of IE, again utilising IE conditional comments:
<!--[if lt IE 9]> <link rel="stylesheet" href="css/no-mq.css"><![endif]-->
<!--[if gte IE 9]><!--><link rel="stylesheet" href="css/mq.css" /><!--<![endif]-->
The first line will only ever be interpreted by IE 8 and below. The second line will be interpreted by IE9 and every other browser. Sure, it's a conditional comment hack, but surely one less HTTP request in today's device-agnostic web can only be a good thing?