My Media Query Mixin

I've written quite a lot about media queries; Playing with LESS then Sass(SCSS) has made me create mixin upon mixin to help my front-end development workflow. I think I've finally settled on something (famous last words!) that works for me and want to share it. Because sharing is good, right?

and the winner is…

@mixin mq($point, $IE9: false, $query1: min, $query2: width) {
    @if $IE9 == true{
        .lt-ie9 & {
            @content;
        }
    }

    @media (#{$query1}-#{$query2}: $point / $doc-font-size +em) {
        @content;
    }
}

You say what?

This mixin allows you to define your major or minor breakpoint in pixels (it automatically converts to EMs), define the media query whether it be min or max, width or height, device height or device width. The mixin has 4 variables in it, most of which are predefined.

It also fantastically has the option to copy the media query content and create a snippet prefixed with an class that'd be picked up by IE8 or less as specified in your HTML with this code -

<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->

So because it can give min,max,width,height options let's go through the examples.

What's up $doc-font-size

In my _variables.scss I have two variables that are related to the document font-size. One for line-height and one for font-size. The $doc-font-size variable in the mixin above needs to be defined in your Sass for this to work. Mine is generally set to 16 and my variables would read


$doc-font-size: 16;
$doc-line-height: 24;

Although, we won't be using the line-height in this mixin or any examples

Examples...

1) A simple min-width media query

So your media query you'd write in SCSS would be -

.example {
    @include mq(320) {
        width: 100%;
    }
}

Which would result in -

@media screen and (min-width: 20em) {
    .example {
        width: 100%;
    }
}

So giving the mixin no other arguements than a numeric value (a pixel value) would give you the staple media query of min-width.

2) A simple min-width media query with IE fallback

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, true) {
        width: 100%;
    }
}

Which would result in -

.lt-ie9 {
    .example {
        width: 100%;      
    }
}        
@media screen and (min-width: 20em) {
    .example {
        width: 100%;
    }
}

So giving the pixel value and the second argument as true would mean the mixin would give the CSS for your conditional IE8 class.

3) A simple max-width media query

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, false, max) {
        width: 100%;
    }
}

Which would result in -

@media screen and (max-width: 20em) {
    .example {
        width: 100%;
    }
}

Now adding the third argument of max, keeping the $IE9 variable is false creating a simple max-width media query.

4) A simple max-width media query with IE fallback

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, true, max) {
        width: 100%;
    }
}

Which would result in -

.lt-ie9 {
    .example {
        width: 100%;      
    }
}        
@media screen and (max-width: 20em) {
    .example {
        width: 100%;
    }
}

Changing the $IE9 argument to true creates a simple max-width media query plus the IE8 conditional class.

5) A simple min-height media query

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, false, min, height) {
        width: 100%;
    }
}

Which would result in -

@media screen and (min-height: 20em) {
    .example {
        width: 100%;
    }
}

By changing the forth variable to height, and stating the middle two ($IE9 and $query1) as they're defaulted in the mixin creates a simple min-height media query.

6) A simple min-height media query with IE fallback

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, true, min, height) {
        width: 100%;
    }
}

Which would result in -

.lt-ie9 {
    .example {
        width: 100%;      
    }
}        
@media screen and (min-height: 20em) {
    .example {
        width: 100%;
    }  
}

Changing the second variable of the mixin ($IE9) to true would give you the simple min-height mixin with the conditional class too.

7) A simple max-height media query

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, false, max, height) {
        width: 100%;
    }
}

Which would result in -

@media screen and (max-height: 20em) {
    .example {
        width: 100%;
    }
}

Here we're keeping the IE9 variable as false, making the min/max/min-device/max-device as max and width/height as height to create a simple max-height media query.

8) A simple max-height media query with IE fallback

So your media query you'd write in SCSS would be -

.example {
    @include mq(320, true, max, height) {
        width: 100%;
    }
}

Which would result in -

.lt-ie9 {
    .example {
        width: 100%;      
    }
}

@media screen and (max-width: 20em) {
    .example {
        width: 100%;
    }
}

Here we've just given the $IE9 variable a value of true giving a simple max-width media query and a conditional class for IE8.

From these examples I hope you can see that instead of min or max you can also use min-device and max-device so that you can target 'devices' if you wish to.

Major Breakpoints

I work with two kinds of CSS3 media queries in mind when developing responsive websites. Major and minor breakpoints. As mentioned in a slidedeck from Stephanie and Bryan of Yiibu.

With Sass you can create some variables for what you like to determine as 'major' breakpoints and include them as necessary in the media query mixin.

For example you could have predefined major breakpoints like this -

$mobile: 320; 
$tablet: 760; 
$desktop: 1200; 

You can then include it in the mixin like this -

@include mq($mobile, false, max) {
    .example {
        width: 100%;
    }
}

Which would give you this result -

@media screen and (max-width: 20em) {
    .example {
        width: 100%;
    }
}

Nesting that media query

One of the great things about Sass is the ability to nest your CSS. But you can also nest mixins. This means you can nest this mixin. That's pretty cool. So as an example you can do something like this -

.example {
    @include mq(320) {
        width: 300px;
        @include mq(480, false, max) {
            background: red;
        }
    }
}

Would give you this result -

@media (min-width: 20em) {
    .example {
        width: 300px;
    }
}

@media (min-width: 20em) and (max-width: 30em) {
    .example {
        background: red;
    }
}

.class > mixin or mixin > .class

From all the examples I've given above I've been putting the media query mixin within the .class but you can do this the other way around and with Sass it outputs the same code. As shown in this example -

@include mq(320) {
    .example {
        width: 300px;
        @include mq(480, false, max) {
            background: red;
        }
    }
}

This gives the same results -

@media (min-width: 20em) {
    .example {
        width: 300px;
    }
}

@media (min-width: 20em) and (max-width: 30em) {
    .example {
        background: red;
    }
}

Media Query Bubbling or Nesting

As I've written previously I write my media queries 'inline' with the element I'm editing rather than tagging it on to the bottom of my stylesheet. This, I feel, allows for better code organisation and a more OO 'modular' approach. This makes it easy to find the code you may need to edit and allows for a better 'mobile first' approach.

Support for IE6 and 7

There are two ways you could do this using the mixin. Forget about it like i do and plumb something like my set of Universal IE7 Sass files which is an updated 'Sass-ified' version of Andy Clarkes Universal IE6 codebase. Or if you need to support IE6 and 7 a little better than that you could use something like this code snippet -

.lt-ie9 & { 

    // put the IE8 and below CSS declarations here
        /* IE8 and below */ 

     // if you need IE7 as well then prefix the CSS with a *
        /* IE7 and below */  

    // if you need IE6 as well then prefix the CSS with a _
        /* IE6 */ 
}

Where you would be using CSS hacks to will Internet Explorer 6 and 7 into submission.

Portrait / Landscape mixin

Because you can nest media queries within each other. I've also created a little mixin for defining the portrait or landscape media query.

@mixin orient($orientation: landscape) {
    @media (orientation : $orientation) {
        @content;  
    }
}

So if you write a media query like this

@include mq(768) {
    @include orient {
        .example {
            width: 100%; 
        }
    }
}

It would give this result -

@media (min-width: 48em) and (orientation: landscape) {
    .example {
        width: 100%;
    }
}

As you can see this mixin also has a variable for the landscape/portrait predefined as landscape. If you want a portrait media query you can write something like this -

@include mq(768) {
    @include orient(portrait) {
        .example {
            width: 100%; 
        }
    }
}

It would give this result -

@media (min-width: 48em) and (orientation: portrait) {
    .example {
        width: 100%;
    }
}

Of course you can just include the media query by itself if you need to.

100% pure adrenaline!

After all this, what I've effectively done is recreate a Compass plugin by Sam and Mason - Breakpoint. You could say I've reinvented the wheel a little. But I like my mixin, it works for me and how I want to code. Also I don't use Compass because I like 'my own' code and to know what each mixin I use is doing. But that's just me. If you're already a Compass user your mindset might be in favour of using this plugin. As with all things, it depends.

Copy and Paste

As well as copying and pasting the code above, I've also added it on codepen and to my (work in progress (just started)) Sass mixin library - Sassifaction