Generating Alternative Stylesheets for Browsers Without @media

By  on  

If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the mobile view — not good.

An alternative is to create two separate stylesheets — a regular one to be served to modern browsers, and another without media queries, served to older browsers that don't support them. But is this maintainable? Is it reasonable to keep a separate stylesheet for a small subset of visitors?

Turns out that, as Jake Archibald described, Sass can be used to automate a great part of the process. In fact, breakpoint managers such as include-media or Sass MQ provide a mechanism for dealing with this quite easily and with no extra maintainability effort.

In this article, I'll describe how to use include-media to generate an alternative stylesheet without media queries, as well as to how to integrate that process in the workflow of popular build tools.

A first approach

So how do we go about generating this alternative stylesheet? We want to get rid of all @media statements, but it's not as simple as unwrap each one of them all and use its contents.

For example, let's imagine a grid of articles. On a mobile view, each article should take the full width of the screen, but as the viewport width grows, more articles should be fitted into one row. To do that, we gradually decrease the width of the article relative to its container with a few media queries — by the time we get to 1400px, we'll be able to fit 8 articles per row.

/* Original rules (1) */
.article {
    width: 100%;

    @media (min-width: 768px) {
        width: 50%;

    @media (min-width: 1024px) {
        width: 25%;

    @media (min-width: 1400px) {
        width: 12.5%;

/* All media queries flattened (2) */
.article {
    width: 100%;
    width: 50%;
    width: 25%;
    width: 12.5%;

Without any special treatment, IE 8 would simply ignore all media queries and therefore render full width articles, even on a large screen (1). However, if we blindly flatten all media queries and use their contents, the browser will end up using the last rule at all times (width: 12.5%, in this case), even thought it was originally intended for exceptionally large screens only (2).

That's not ideal, as we've gone from ignoring all media queries to the other extreme of including them all — which on a typical site probably means going from a mobile view to a really large one. A more reasonable approach is to pick a specific width and select only the media queries that contribute to that view.

Enter include-media

Moving into include-media land, let's start with the same list of breakpoints.

$breakpoints: (
    'small': 320px,
    'medium': 768px,
    'large': 1024px,
    'super-large': 1400px

It's fair to assume that our IE 8 users won't be on a phone or a tablet, so it seems reasonable to serve them the website as it looks on the large view, so 1024px. Let's take a look at a few media queries from one of the modules of our website, part of _module1.scss.

.module1 {
    // This rule interests us, as it affects 'large'
    @include media('>=phone') {
        color: tomato;

    // This one doesn't, as it's between 'medium' and 'large' (excluding)
    @include media('>medium', '<large') {
        color: chocolate;

    // Not this one either, it affects only 'super-large'
    @include media('>=super-large') {
        color: wheat;

With include-media, you can simply tell the library that you want to generate a version of the stylesheets without media queries support, and specify which breakpoint you want to emulate. To do that, you simply set two variables ($im-media-support and $im-no-media-breakpoint respectively).

A typical scenario is to have two copies of the main SCSS file: the normal one stays as is (main.scss), and the alternative one with the variables set (main-old.scss).

// main.scss

@import 'include-media';
@import 'module1';
// main-old.scss

$im-media-support: false;
$im-no-media-breakpoint: 'large';

@import 'include-media';
@import 'module1';

// Resulting CSS
.module1 {
    color: tomato;

You can even specify which media expressions to accept when flattening media queries. For example, if you have a media query that targets retina devices, you probably don't want to include its contents in the alternative style sheet, even if it matches the emulated breakpoint.

$im-no-media-expressions: (

// This is retina only, we don't want this!
@include media('>=medium', 'retina2x') {
    color: olive;

Integrating build tools

Integrating this workflow into your build tools is quite simple and makes the whole process seamless. I'm including example tasks for both Grunt and Gulp (sorry Broccoli users, I never eat my greens).

// Gruntfile.js

module.exports = function(grunt) {
    sass: {
      dist: {
        files: {
          'css/main.css': 'sass/main.scss',
          'css/main-old.css': 'sass/main-old.scss'


  grunt.registerTask('default', ['sass']);
// Gulpfile.js

var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('sass', function() {
  return gulp.src(['sass/main.scss', 'sass/main-old.scss'])

gulp.task('default', ['sass']);

With that in place, the build tool will generate both style sheets automatically, outputting them to css/main.css and css/main-old.css.

Serving the style sheets

Finally, we just need to serve the appropriate style sheet depending on the browser, using good old-fashioned conditional comments.

<!--[if lte IE 8]>
    <link rel="stylesheet" href="css/main-old.css">
<!--[if gt IE 8]><!-->
    <link rel="stylesheet" href="css/main.css">

And that's it. IE-friendly mobile-first responsive websites!

You can get include-media at

Eduardo Bouças

About Eduardo Bouças

Eduardo is a Portuguese web developer based in London, working as Lead Developer for Time Inc. UK. He's passionate about the web, clean design, elegant code and robust solutions.

Recent Features

  • By
    Vibration API

    Many of the new APIs provided to us by browser vendors are more targeted toward the mobile user than the desktop user.  One of those simple APIs the Vibration API.  The Vibration API allows developers to direct the device, using JavaScript, to vibrate in...

  • By
    CSS vs. JS Animation: Which is Faster?

    How is it possible that JavaScript-based animation has secretly always been as fast — or faster — than CSS transitions? And, how is it possible that Adobe and Google consistently release media-rich mobile sites that rival the performance of native apps? This article serves as a point-by-point...

Incredible Demos

  • By
    CSS Fixed Position Background Image

    Backgrounds have become an integral part of creating a web 2.0-esque website since gradients have become all the rage. If you think gradient backgrounds are too cliche, maybe a fixed position background would work for you? It does provide a neat inherent effect by...

  • By
    HTML5 Context Menus

    One of the hidden gems within the HTML5 spec is context menus. The HTML5 context menu spec allows developers to create custom context menus for given blocks within simple menu and menuitem elements. The menu information lives right within the page so...


  1. Interesting approach, but why not use something like respond.js which adds basic mediaquery support?

    • That’s a perfectly valid approach as well.

      This article came about after we’ve implemented this fallback method as part of include-media and I guess it’s a less ambitious approach than respond.js. Instead of trying to make media queries work on older browsers, we just accept the fact that they don’t and try to serve a “static snapshot” of the website at a certain breakpoint instead.

    • I see, was just wondering if there was some particular reason of doing it like this instead of just throwing respond.js at it. I guess in a sense this is a bit simpler and less error-prone than using a script.

  2. According to a conditional comment

  3. You may want to exclude Windows Phone 7 in the conditional comment: [if (lte IE 8)&(!IEMobile)] (

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!