Making IE < 9 behave with responsive development
Responsive web development can be tricky at the best of times, but even more so if the site in question has to support any version of Internet Explorer below version 9 (most of the time it's just IE 8 you need to tame due to the low usage statistics of IE <= 7). Old IE makes things harder because media queries (the backbone of responsive websites) aren't supported, so all code inside media query blocks is ignored. Below are the various solutions I've tried out which I wasn't 100% happy with:
- JS based media query polyfills (Respond.js, css3-mediaqueries.js)
- Performance issues, also what about users with no JS and old IE? - Mobile-first / progressive enhancement
- This is a great solution, I use a similar method on this site, but what if the client insists the layout needs to look the same on both modern browsers and old IE? - Only ever using
max-width
based media queries
- On larger sites this will lead to horrible CSS as it will require a lot of style resetting at certain breakpoints.
The solution I've found to be the best does require a bit of setup but once you're up and running supporting old IE has never been so easy. It also require Sass, in my opinion this functionality alone is enough to try Sass if you haven't before (yes, this solution is that useful). In it's simplest form the file structure consists of:
- project/
- css/
- _base.scss
- _config.scss
- lt-ie-9.scss
- style.scss
- index.html
_base.scss
is the file which contains all of your SCSS / CSS, you can write it straight into there or if you prefer to be modular about it you can import all of your CSS components into that file. With no project CSS thrown in there all it's doing is importing the _config.scss
file:
@import "config";
_config.scss
contains a map of your common breakpoints along with a media query mixin:
/**
Specify breakpoints here.
*/
$breakpoints: (
'small': '(min-width: 480px)',
'medium': '(min-width: 768px)',
'large': '(min-width: 1024px)'
);
@mixin media-query($media-query, $lt-ie-9-support: false) {
/**
If we're not in the old IE stylesheet, then output the media query block.
*/
@if $is-lt-ie-9-stylesheet == false {
@each $name, $declaration in $breakpoints {
@if $media-query == $name and $declaration {
@media only screen and #{$declaration} {
@content;
}
}
}
}
/**
If the media query has been set to support old IE and we are in the old IE
stylesheet, then output the code inside the media query block but strip the
actual media query from around it.
*/
@if $lt-ie-9-support == true and $is-lt-ie-9-stylesheet == true {
@content;
}
}
lt-ie-9.scss
consists of the following, this file will compile to lt-ie-9.css
when ran through a Sass compiler:
$is-lt-ie-9-stylesheet: true;
@import "base";
style.scss
consists of the following, this file will compile to style.css
when ran through a Sass compiler:
$is-lt-ie-9-stylesheet: false;
@import "base";
Almost done now, the last step is to add the reference to the CSS files into the HTML like so:
<!--[if (lt IE 9) & (!IEMobile)]>
<link rel="stylesheet" href="css/lt-ie-9.css">
<![endif]-->
<!--[if (gte IE 9) | (IEMobile)]><!-->
<link rel="stylesheet" href="css/style.css">
<!--<![endif]-->
This tells the browser which CSS file to use based on what browser the page is being view on, if the browser is IE and below version 9 it will load lt-ie-9.css
, however if the browser is either IE version 9 and above or not IE at all it will load style.css
.
Usage
In _base.scss
you'd write your SCSS / CSS as per normal but when you want to use a media query you'd do it like so:
.element {
@include media-query(small) {
width:50%;
}
}
If you want the code in the media query to be read by old IE then you just simply add a second argument of true
to the media-query
@include
like so:
.element {
@include media-query(small, true) {
width:50%;
}
}
The above code will output the following in the style.scss
file:
@media only screen and (min-width: 480px) {
.element {
width:50%;
}
}
And the following in the lt-ie-9.scss
file, stripping the media query wrapped around it:
.element {
width:50%;
}
There you have it, almost no effort required to make old IE behave with media queries, I've used this method in a few projects of mine now and I haven't had any issues at all. I actually use this method by default on my personal front-end boilerplate which you can check out here.
Other uses
This isn't just useful for responsive stuff either, it can also be used as an alternative to Paul Irish's IE conditional classes on the <html>
element. With this method you'd target elements being viewed in a specific version of IE like so:
.element {
/*html*/.lte9 & {
width:50%;
}
}
The problem with that method is the IE specific styles would get loaded be the browsers no matter what browser it is, downloading CSS which doesn't get used is just wasted bytes which can add up and cause a slow-loading site. With the Sass method you'd do it like:
.element {
@if $is-lt-ie-9-stylesheet == true {
width:50%;
}
}
Or if you want CSS outputted only in the non-IE stylesheet you'd do:
.element {
@if $is-lt-ie-9-stylesheet != true {
width:50%;
}
}
This way the browser specific CSS only ever gets loaded by the browser which actually needs it, awesome right?