May 31, 2013

Automatic Sass Imports with Sass Globbing

If you'€™re using Sass to preprocess your stylesheets, you've probably discovered partials. A partial is a sub-stylesheet, intended to be included as part of a main stylesheet rather than being processed into its own file. Partials are great for building more organized stylesheets, but managing lots of separate includes by hand can be a drag. Thankfully, we can use the sass-globbing gem to make use of partials without the extra overhead.

A Partial Introduction

Creating a partial is really easy. It'€™s just a regular Sass file with a leading underscore: _my-partial.scss. When I compile this file, Sass won'€™t generate a my-partial.css file; it expects it to be imported into another stylesheet. For example, to pull this file into my main stylesheet, I can use the Sass @import directive:

// main.scss
@import 'my-partial';

Notice that I don'€™t write the leading underscore when importing the partial. Also, it'€™s best to leave off the file extension: it'€™s simpler to write, and if I wanted to switch to using Sass'€™s indented syntax, I wouldn'€™t have to change my import statement.

Now I have the freedom to organize my stylesheet source code any way I want and still deliver a single file to keep HTTP requests down. Or, I could even output several customized stylesheets from a common codebase.

Partials in Practice

So the general idea of organizing code is nice, but let'€™s look at some real-world benefits by looking through the lens of some recent developments in the CSS world that aren'€™t about preprocessors at all.

CSS is going modular. As sites and apps get more complex, writing a single giant stylesheet only encourages me to chuck new rules at the bottom and not really think about the big picture. This is especially true if the existing code is already hard to understand, poorly organized, and full of repetitive styles targeting specific elements on specific pages.

Projects like OOCSS, SMACSS, BEM and others are all trying to address this at the level of application architecture, thinking about how to separate responsibilities and banish unruly CSS from projects. With Sass partials, we can have our file structure reflect our architectural strategy. Since I’m a fan of SMACSS, I might structure my Sass like so:

 sass/
 _base.scss
 modules/
 _forms.scss
 _typography.scss
 layouts/
 _layout-small.scss
 main.scss
 

Here's what my main.scss might look like:

// main.scss
@import 'base';
@import 'modules/forms';
@import 'modules/typography';
@import 'layout'

This is a great start, but as I build this out I'€™ll need to add new modules and layouts. It's natural to just add a new file, say modules/_buttons.scss and then wonder why the new styles aren'€™t showing up even though Compass or Sass is watching for changes. After a minute or two of confusion, I'€™ll realize what happened. "Gahhh, I forgot to add an @import statement in main.scss!"

There is such a thing as being too modular, but even a small project can reasonably have have over a dozen modules. It'€™s just a pain to manage all of these imports by hand.

Sass Globbing to the Rescue

We really need a way to automate our imports. Turns out, there'€™s a gem for that. Sass-globbing is a Ruby gem for Sass from the creator Compass, Chris Eppstein. It lets you import directories or whole directory trees with a single @import statement:

// import a directory's contents (does not import sub-directories):
@import 'dir/*';
// recursively import a directory's contents and any sub-directories:
@import 'dir/**/*';

To start using the gem, I'€™ll install it from the command line:

$ gem install sass-globbing

If you'€™re using Compass (and you should be), add sass-globbing support to your project by requiring it in your Compass configuration file:

# config.rb
require 'sass-globbing'

Now I can update my main.scss to import my modules and layouts directories in one go:

// main.scss
@import 'base';
@import 'modules/*';
@import 'layouts/*';

Fantastic. I can now add partials to my heart'€™s content without having import statements slow me down.

Import Order Blues

There is one wrinkle. Say I'€™m developing mobile-first, and I've finished roughing in my layouts/_layout-mobile.scss. Now I want to add a desktop layout, so I create layouts/_layout-desktop.scss. These styles should be imported after layout-mobile.scss, but "€“uh-oh"€“ sass-globbing always imports files in alphabetic order. What to do?

The simplest solution is simply to number the files. We'€˜ll change the layout files to:

layouts/
 _1.layout-small.scss
 _2.layout-wide.scss

As a bonus, we can see the load order of our files right in the file browser. Neat and tidy. The best part is, our stylesheets just re-compiled correctly, and we never touched main.scss.

Done and done

So now we can have our cake and eat it too. We have a practical, maintainable project structure for our stylesheets, and we'€™ve cut out a whole bunch of configuration that would otherwise be a downside. Happy globbing!