Combine matching media queries with Grunt.js

CSS pre-processors like LESS are great. Being able to use variables for global breakpoints and nested media queries have made our front end code a lot more organised. Combining that with Grunt.js to build, test and minify it all, we (and anyone else who uses these tools) are on to a winner.

But there was always one thing bugging us, a small thing that could be overlooked, but at Building blocks it’s these little things that we like to work on. Being able to use nested media queries is great, but boy do they create some bloat. Repeated queries dotted all over the CSS file works fine, but could that be improved? Could we not merge matching media queries into one block?

After a few initial discussions, we were not too sure if it could be done. Obviously there can be unlimited amounts of media queries in any order. The rules behind it all would have to be rather smart. But then we assessed how we as a company actually use media queries.

Our responsive philosophy at Building Blocks is ‘mobile-first’. This will not surprise anyone, it’s a common industry practice these days. What this highlighted to us though was that 90% of our media queries were using repeated ‘min-width’ rules. These were the ones that we wanted to combine, so as long as we coded the LESS/CSS files ‘mobile-first’ we could output the media queries in an ascending order after the base (non media queried) rules. Still with me? Yes? Good.

The Grunt.js task

Grunt Logo

To learn about Grunt.js, take a look at the Grunt.js website.

So we had the idea; We wanted to combine matching media queries with Grunt.js and merging/append them to our CSS file.

At Building Blocks we have a nice little base project that we use to start up most new work. This basically includes a directory structure, a few default libraries, some reusable Javascript methods and a bit of common CSS. The best bit about the set up is Grunt.js. For those how have not heard of Grunt.js, simply put, it is a task based build tool built on Node.js that allows us to process LESS, combine Javascript, minify files .etc – Lots of cool stuff.

To achieve this goal, we decided to write a Grunt task that would run after the LESS files are processed (technically you wouldn’t need to use LESS, you could just run the the task over any CSS file). There were 6 steps the task would need to go through.

  1. Parse the CSS.
  2. Separate the media queries from the rest of the CSS.
  3. Loop over the media queries to find ones that match.
  4. Work out the render order.
  5. Output the processed CSS.
  6. Go to the pub (not actually part of the task, but it was Friday).

Note: In this post, I am not going to go into the steps required to build a Grunt contribution, that will need a whole different post that I will let John Cashmore deal with another time.

The result

Basically, after a bit of Javascript black magic we were able to match up the media queries and output the rules together. That was great, but the issue with ordering was still rearing its ugly head. We had to make a decision, which was made to keep the initial rules for ordering simple.

We only had a short amount of time to implement the initial 1.0 version of the task (we only had one of our innovation days to get it together). For this reason we decided only to sort via ‘min-width’ in ascending order. If ‘min-width: X’ then sort the media queries by ‘X’. The other queries (such as max-width, pixel density .etc) will be combined, just not sorted by any order other than how they were parsed from the CSS.

The main issue we forsee at this time is if developers were to mix measurements such as ‘em’ and ‘px’ in the media queries… but we are looking into that one. So, at the moment, this works lovely IF you write your media queries ‘mobile-first’ using the ‘min-width’ rule.

Is it worth it? On average (obviously depending on how many nested queries you use) we found it reduced the file size by around 10% which made us feel a lot happier!

The near future

We want to develop this much further. The plan is to try add more control over how the task is configured. At the moment we are not sure how to manage the ordering in relation to each of the different types of media query (is ‘min-width’ more important than ‘max-width’ ?). This type of control might could be set as ‘options’ so developers can choose things like ordering based on their project or how they prefer to code?

It is early days and we would really like your suggestions on how we could improve this. In the mean time though have a play, report an issue or even submit a pull request on GitHub.

Just want to include it as part of your project? (remember, its not perfect… yet) grab it through NPM:

Any questions?

If you need more information or have any questions just get in touch and we'd be happy to answer them for you.