Using SASS for mobile first design AND having old IE behave properly

This post is about how SASS @content blocks can be used to do mobile first responsive design and having IE < 8 fallback to a default desktop site without doing loads of copying and pasting. Its based on jake archibalds excellent idea for doing this.

The idea is that you generate 2 css files, one for modern browsers that can handle media queries, and one for older browsers that can’t. Since this is handled through SASS compilation, there’s no need to manually keep styles in synch by copying and pasting code.

There are the following files –

  • _variables.scss – since I’m trying to replicate the concept of bootstrap here, the variables file contains all the, um, variables. For my cut-down version, this includes the media query break-points and a couple of defaults.
    //mq breakpoints
    $screen-sm:         768px;
    $screen-tablet:     $screen-sm;
    
    $screen-md:         992px;
    $screen-desktop:    $screen-md;
    
    $screen-lg:         1200px;
    $screen-lg-desktop: $screen-lg;
    
    $container-tablet: 720px;
    $container-desktop: 940px;
    $container-lg-desktop: 1140px;
    
    // Grid system
    $grid-columns:              12;
    $grid-gutter-width:         30px;
    
    $fix-mqs: false !default;
    $old-ie: false !default;
    

    This means that by default, media queries will be used (they won’t be fixed), and old-ie content blocks will be ignored.

  • _utils.scss – This contains the mixins that help control output.
    
    @mixin respond-min($width){
        @if $fix-mqs {
            @if $fix-mqs == $width {
                @content;
            }
        }
        @else {
            @media (min-width: $width){
                @content;
            }
        }
    }
    
    @mixin old-ie {
        // Only use this content if we're dealing with old IE
        @if $old-ie {
            @content;
        }
    }
    

    So, the respond-min mixin gets a width parameter passed to it. If the variable $fix-mqs is true (which it isn’t by default) and is a value equal to the width passed to the mixin, the content of the mixin will be shown using the @content directive. If its false (by default), the content will be shown in a media query using the passed width.

    The other mixin (old-ie), allows us to define bits of content that will only be output in the ie stylesheet.

  • _layout.scss – this file controls the actual CSS detail and would import all necessary SASS files. If using a bootstrap-type system, this would mean importing the grids, type, navbar files etc. These files would make use of the mixins in utils.scss instead of directly using media queries. An example of this in use is the nav list used in the demo.
    
    #nav {
        ul { 
            //...
            li {
                list-style-type: none;
                background-color: #ddd;
                text-align: center;
                padding: 5px;
                width: 100%;
                @include respond-min($screen-desktop){
                    float: left;
                    margin-right: 10px;
                    width: auto;
                }
            }
        }
    }
    

    This bit of code defines the default styling (which is mobile-first) and then what happens when the $screen-desktop width is reached. By default, the list elements will be 100% width. On a desktop device, they are floated left. However, by using respond-min instead of a native media query, when compiled, the ie stylesheet will output the following –

    
    #nav ul {
      /*...*/
      #nav ul li {
        list-style-type: none;
        background-color: #ddd;
        text-align: center;
        padding: 5px;
        width: 100%;
        float: left;
        margin-right: 10px;
        width: auto; }
    

    Notice that since only the content of the ‘respond-min’ mixin is output, not the media query, the 100% width of li’s is overridden in IE, forcing it to default to the desktop style.

  • style.scss – This imports the variables, utils and layout SASS files.
  • style-old-ie.scss – This imports the same files but sets the 2 variables that by default are false.
    @import 'variables';
    $old-ie: true;
    $fix-mqs: $screen-desktop;
    @import 'utils', 'layout';
    

    So any calls to the ‘old-ie’ mixin will be output and the default size that will be targeted is the desktop.

Originally, I thought that by making the respond-min mixin conditional test a ‘==’ instead of a ‘>=’ was an improvement.

@if $fix-mqs == $width {
    @content;
}

This would mean that there would be less duplication in the old-ie-style css file. Imagine an element that defines styles for the default mobile, a tablet view, and also a desktop view. In the old-ie-style, these would all be output, creating bloat. By setting the conditional statement to ‘==’, it would only output one rule. By setting the ‘$fix-mqs’ variable to $screen-desktop, it would only output the desktop (and mobile) rules. Still duplication, but not as bad.

However, there is a flaw in this. Take an element like this…

#div {
    display: block;
    width: 100%;
    @include respond-min($screen-tablet){
        float: left;
        margin-right: 10px;
        width: 50%;
    }
    @include respond-min($screen-desktop){
        width: 30%;
    }
}

This would mean that in the old-ie-style the tablet view is never output, meaning the element is not floated. In this case, float:left could also be added to the desktop block but it results in duplication, something we’re trying to avoid – especially manual duplication.

Also, in the case of using a bootstrap type system whereby html elements are marked-up with classes defining how they behave at different screen widths, using ‘==’ would definitely screw things up there. The old-ie stylesheet needs to make the rules for the widest device width the default ones.

So, we’re back to having to use ‘>=’ in the respond-min mixin until some way can be found to remove duplicates in css. I guess, as Jake points out, by compressing and gzipping the content, its not a massive issue. I just don’t like duplication 😉

Leave a Reply