Life is now easier, Create media query mixins with rulesets - LESS CSS

Life is now easier, Create media query mixins with rulesets - LESS CSS

LESS now has a function called rulesets which allows you to create a mixin that wraps content in a media query.

For a while us who LESS have been slightly jealous of SASS, as you can pass a content block to a mixin. This is extremely handy especially when it comes to media queries. For example in SASS you can do this:

Example:

// Mixin 
@mixin responsive($maxWidth) {
    @media only screen and (max-width: $maxWidth) {
        @content;
    }
}

// Usage 
.bacon {
    width: 100%;

    @include responsive(450px) {
        float: left;
        margin-top: 10px;
    }
}

/* Outputted css */
.bacon {
  width: 100%;
}

@media only screen and (max-width: 450px) {
  .bacon {
    float: left;
    margin-top: 10px;
  }
}

As you can see I can pass a content block to a mixin and it is wrapped in a media query. This is a nice, reusable way of abstracting that boilerplate code that we have to write in responsive pages. Well the good news is, as of LESS v.1.7, rulesets can be passed to a mixin.

What is a ruleset?

From lesscss.org, a ruleset allows:

wrapping of a css block, defined in a mixin

In the same way that we abstracted the wrapping media query code to a mixin in SASS, with rulesets we can do the same. Let's repeat the example above but with LESS.

Example:

// Mixin 
.responsive(@maxWidth; @rules) {
    @media only screen and (max-width: @maxWidth) {
        @rules();
    }
}

// Usage 
.bacon {
    width: 100%;

    .responsive(450px, {
        float: left;
        margin-top: 10px;
    });
}

/* Outputted css */
.bacon {
  width: 100%;
}

@media only screen and (max-width: 450px) {
  .bacon {
    float: left;
    margin-top: 10px;
  }
}

Brilliant! We get exactly the same results as the SASS version. @rules is the parameter that will pass in our content block. Declaring rules() within our media query will output the content.

What else can we do with this?

Supporting IE8 and below has always been a pain when it comes to responsive layouts as there is no support for @media, making media queries useless. Jake Archibald came up with a popular approach to solve this problem. His method uses variables and media query mixins to allow a separate IE friendly CSS file to be generated. One of the great things about this is the ability to reuse your existing media query'd code. Once set you can just forget about it. Here is Jake's article on this approach.

IE-friendly-mobile-first CSS with Sass 3.2

Ever since I discovered this article I wanted to use something like this but with LESS. Unfortunately it was not possible, until now!

IE friendly mobile-first CSS with Less 1.7

Here is the LESS version of Jakes approach. Remember to check out Jakes original article.

Layout.less

This file contains the page layout

.content-margins {
    margin: 10px;

    .respond-min(60em; {
        margin: 20px;
    });

    .respond-min(75em; {
        margin: 40px;
    });
}

.primary {    
    .respond-min(45em; {
        float: left;
        width: 70%;
    });

    & .content-margins {
        margin-right: 30px;
    }
}

utils.less

Here comes the meat behind this approach.

@fix-mqs: false;
@old-ie: false;

// Mixin that will create the fixed IE styles    
.respond-min(@width; @rules) when (@old-ie = true) and (@fix-mqs >= @width) {
    @rules();
}

// Mixin that will wrap our content in a media query
.respond-min(@width; @rules) when (@old-ie = false) {
    @media screen and (min-width: @width) {
        @rules();
    }
}

LESS does not have a function to create if statements but we can use it's equivalent, mixin guards. In the same style as @media the mixin is only used when the conditions are met. In this case @old-ie must be true and @fix-mqs must have a value greater than or equal to the width specified in the mixin declaration.

all.less

The master page file.

@import 'utils';
@import 'layout';

all-old-ie.less

This generates the IE<9 stylesheet.

@import 'all';
@old-ie: true;
@fix-mqs: 65em;    

CSS

The resulting IE only CSS file would look like this.

.content-margins {
  margin: 10px;
  margin: 20px;
}
.primary {
  float: left;
  width: 70%;
}
.primary .content-margins {
  margin-right: 30px;
}

All the styles for the 65em breakpoint are there and not a media query in sight!

Summary

Rulesets are a great addition to LESS, allowing us to abstract media query boilerplate code away into a mixin. Being able to create an IE only stylesheet is a huge win, meaning we can throw away any JavaScript polyfill scripts we may have been using.