Musings on Preprocessing

Published by Chris Coyier

I've been using Sass for pretty much everything I do recently. Here's some musings on the journey. From hold-ups, to trip-ups, to turn-offs. From apps and teams to workflows and syntax.

You Gotta Work Locally

The biggest contributor to me getting on the bandwagon was giving up my going-commando live FTP editing ways. Yeah, Coda is awesome, but it's bad habit forming. It makes it way too easy to work live instead of local1.

Working local is full of obvious advantages. Namely, 1) It's fast 2) You can edit stuff to your heart's content without worry you're screwing up a live site and 3) It allows you to effectively work on a team through version control (more on that later).

So just do it. If you work only on static sites, you can just start doing it. Work from a folder somewhere on your machine. If you work on PHP sites (e.g. WordPress, Joomla, PHP, Vanilla, CodeIgnitor, CakePHP, and a million more) then using MAMP (my screencast) is ideal. Of course there are variants of MAMP for all platforms.

If you are using something like Ruby or Python, well, chances are you know how to set up that stuff anyway so you're fine.

Now that I'm working primarily on projects that run on my local machine, using preprocessors is easy. I use a couple fantastic apps that I'll cover later.

Command line blah blah blah

I'm a designer! I don't know how to use the command line nor should I need to.

That's a very common thing to hear regarding SASS. Here's the thing: I'm right there with you. I hate the command line. You don't need to. I don't, nearly ever, for anything2.

Other turnoffs

As childish as this may seem, another reason it took me so long to get on the preprocessor bandwagon was the crowd. And I'm not alone here.

It's hard to say anything at all about preprocessors, let alone something vaguely negative, without getting pounced on.

For a long time I thought: I write CSS everyday. I know CSS pretty well. My workflow is fine. I'm productive. Why does anything need to change?

The real answer is that nothing needs to change if you don't want it to. If you're perfectly happy doing what you are doing: godspeed.

I can tell you that after making the jump, I am actually more productive. And I write better CSS. And the projects I work on are in better, more maintainable shape because of it. And in some cases, faster3. My advice is: don't let people get on your case. Just do what you gotta do. If you get some time to try it out, do it. And do it on a real project. Just tinkering around doesn't count. You gotta really try it to see how it might work with your day to day.

The Apps

I only have experience with Mac apps. Sorry. I'm sure there are pretty good ones for other platforms.

The app that got me started on all this preprocessor goodness was LiveReload (screencast). I'm still a fan. It now available on the App Store for $9.99. It is a menu bar app where clicking the icon in the menu bar opens a window of options.


LiveReload: the menu bar icon, the app window, and a browser with the browser extension installed.

You tell LiveReload to watch a particular folder. When any file in that folder changes, it triggers preprocessing. It can do a whole bunch of preprocessors. For CSS: LESS, SASS (w/ Compass), or Stylus. For JavaScript: CoffeeScript or IcedCoffeeScript. For HTML: HAML, Jade, Slim, or Eco.

After it's preprocessed, it reloads the page in whatever browser(s) you have the browser extensions installed in. If what you changed was just CSS, it injects the styles into the page without a page refresh. This is particularly useful where you are styling something on a page in a particular state. Let's say a click triggers open a dialog box and you're trying to style that dialog box. With LiveReload, you can leave that dialog box open and the newly injected styles will affect it, meaning not a lot of wasted time clicking and re-clicking.

But I've recently switched to CodeKit. CodeKit's UI is nicer, has more features (e.g. image optimization, JS concatenation) and less weird limitations (you choose whether or not file are preproccessed and where they go, unlike LiveReload where once you import it you can never get it to compile). The only thing I miss is LiveReload's ability to inject CSS without reloading the page, a handy feature for sites with complex states that require multiple steps to get the page back into that state after a reload. Update April 2012: CodeKit is now doing live injection of CSS.

There are a couple of things I don't like about LiveReload though. One is the screen where you tell it the output paths for the complied source:

I hate this screen

Notice the grayed out ones? It's trying to be smart and notice that you've included that file elsewhere. Thus it won't compile. But what if you want to compile it for some other reason? Too bad, no way to do it. It's just not a great UI and it can be a bit tricky to get started using.

And thus enters CodeKit. CodeKit has a beautiful UI.

Mmmmm. UI lovelies.

It's very easy to change the file paths to where files compile to. And I've never had to teach it, it seems to pick up on the directory structure and get it right from the get go. It will gray out files that are included too, but you can easily turn that on and off.

CodeKit is limited to LESS, Sass, Stylus, CoffeeScript, and HAML. I've found this to be a little limiting, as I've used Jade to preprocess HTML on a few projects. Jade is capable of HTML includes, which can be tremendously useful.

The error reporting on CodeKit is very nice. It's clear what's going on and it feels good when you resolve it.

Contrast that to LiveReload in which a little window slides down from the upper right of your screen with badly clipped text and no close button, and simply slides away once you've resolved it.

CodeKit also is unable to do the style injection that LiveReload can. This is how the creator feels about that:

Update: CodeKit does do live injection now.

Ultimately I think CodeKit is my favorite right now. What sets it above is all the additional features. It can automatically bless CSS. It can concatenate and minify JavaScript. It can tell you what files import any other particular file. And perhaps my favorite: it can one-click optimize images.

Teams

Another possible hold up to getting on preprocessors: working on a team. Specifically a team where multiple people are in and out of the same CSS files you are. If that's the case, all those people need to be set up with whatever preprocessing app you are using (you could use different ones, that's fine. Best if they are all running same core versions of the preprocessor). You can't have some people editing the .css files and other folks editing the .scss files. Those changes to the .css files are just going to get overwritten the next time someone compiles and commits. Preprocessors are unforgiving that way. They don't say "hey looks like there is some different stuff in this .css file from when I last complied it, want to take a look first?" Nope, they just write over it.

It's doable though. In a short period of time, we moved all of Wufoo to SASS. Actually, we moved a bunch of it to LESS, then some of it to Stylus, then all of it to SASS. Oof da. Credit there to Kevin. We also have all new development on SurveyMonkey using a big set of design patterns that are all using SASS.

If anyone has any team strategies for preprocessors, that would be valuable to share!

Compass

There are folks out there I'm sure that are upset at me for not using Compass. I know, I know. I should be. But obviously I'm slow on this stuff. I like understanding things as deeply as I can before proceeding and Compass is still a little fancy pants for me. If anyone has no idea what Compass is, it's a big 'ol set of add-ons for SASS. It means, essentially, that you don't have to write stuff that you'd likely have to write the same way project after project. CSS3 mixins, grid helpers, sprite helpers, file path helpers, and tons more.

My problem is that I'm not ready to relinquish all that stuff yet. I want to write my own. I'm sure the day will come when I give that up, but it hasn't come yet.

Little things that have tripped me up

This article started out with that title before ballooning into all this. But what the hey, I figure I'll still include these little tidbits that caused me a few moments of grief while learning.

Since I write my own CSS3 mixins, I might have a mixin for box-shadow like this:

@mixin drop-shadow ($x: 1px, $y: 1px, $blur: 2px, $spread: 0, $alpha: 0.25) {
  -webkit-box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha);
  -moz-box-shadow:    $x $y $blur $spread rgba(0, 0, 0, $alpha);
  box-shadow:         $x $y $blur $spread rgba(0, 0, 0, $alpha);
}

It's full of sensible defaults, so the most standard usage can just be:

.module {
  @include drop-shadow();
}

Or if I want to get more specific, I can specify each part:

.module {
  @include drop-shadow(5px, 5px, 2px, -2px, 0.5);
}

But what if I want to do multiple box shadows? This mixin isn't ready for that. For those, I also prepare super generic mixins that are the spec name:

@mixin box-shadow ($string) {
  -webkit-box-shadow: $string;
  -moz-box-shadow:    $string;
  box-shadow:         $string;
}

This one only takes a single param, anything I want to give it. So the usage could be:

/* Bad! doesn't work! */
.stack {
   @include box-shadow(
        1px 1px   0 rgba(0,   0,   0,   0.100),
        3px 3px   0 rgba(255, 255, 255, 1.0),
        4px 4px   0 rgba(0,   0,   0,   0.125)
   );
}

But that doesn't work. All those commas in there will confuse SASS. It will think you're trying to pass multiple parameters to a mixin that is only set up to accept one. You can't just put quotes around the value you are trying to pass either, that doesn't work. The answer is to use the double-parens technique:

@include box-shadow(( whatever, you, want, just, one, param ));

Another thing that screwed me up was trying to use variables within values, where the variable was only a part of the value. At it's most basic, variable are super easy:

$red: #F00;

.red {
   color: $red;
}

But I was trying to do:

$path: "/images/";

body {             /* Bad! No! */
   background: url($path + texture.jpg);
}

In order to make that work, you gotta use the hash/brackets thing:

$path: "/images/";

body {
   background: url(#{$path}texture.jpg);
}

// or

body {
   background: url($path + "texture.jpg");
}

And one last little thing that might be confusing. The & character has special meaning while doing nesting. It equals the selector up until that point. For instance:

a {
  color: red;
  &:hover {
    color: pink;
  }
}

turns to:

a { 
  color: red;
}
a:hover {
  color: pink;
}

You've probably seen example like this before. It doesn't quite sell it to do simple examples like this, but believe it or not, nesting feels natural very quickly and you'll love it.

But back to that & character.

You don't need to do this:

.module {
   & h3 {
   }
   & p { 
   }
}

That's just implied because of the nesting, you don't need to use the & there. You only need it when it's not going to be a descendant selector (space). Things can get extra sexy when you use the & later in the selector. Like if you use Modernizr to detect for multiple backgrounds:

body {
  .multiplebgs & {
  }
  .no-multiplebgs & {
  }
}

That gives you a really clean fork in how you want to handle the situation of support and non-support, all while keeping the code logically nested within the body { } statement.

That Dang FTP Workflow

Even if I've convinced to you work locally, get on version control, and use preprocessors, there is still that lingering issue of deployment. Your workflow might actually take a step backwards in efficiency if you came from live editing to now working locally, and when you want to go live you gotta manually upload changed files to the server. I'm afraid I don't have a great answer for you.

Nettuts covered it with "The Perfect Workflow," but that relies on a GitHub post-commit hook hitting a PHP file that `git pull`s which doesn't work on any server I have access to and is rare for a shared host to even allow that as I understand it.

This is where app-based cloud hosting really shines. Apps like Heroku for Ruby or PHPFog for PHP work into your Git-based version control workflow flawlessly. You can have it so you have one command for pushing to the repository and another for deploying live, or have it so when you push to the repository it automatically goes live.

Those cloud hosts don't work for everyone though. Back when I did agency work, the vast majority on it was just sites on some generic shared hosting. You could always install Git on those servers, then after you push stuff up to your repository, SSH into the server and git pull from there. That's better but still kinda janky and requires command line usage.

Ideally, apps like Git Tower could manage your Git Repos and also handle FTP, uploading files on command that it knows are out of date.

For those stuck on FTP4 workflows who aren't afraid of command line stuff, this looks promising.

The Other CSS Preprocessors

As I've made clear, I've chosen SASS for now as my CSS preprocessor of choice. But what about LESS and Stylus?

LESS was the first one I tried and honestly I still like it. I quite like how all the classes you write are automatically re-useable as mixins. Like:

.screen-reader-text {
   position: absolute;
   top: -9999px;
   left: -9999px;
}
label {
  .screen-reader-text;
}

That's darn useful and more succinct than SASS would be. But I've been told that mixing classes and the concept of mixins together is fundamentally flawed. I'm not sure how, but there you have it. If someone wants to explain that I'd be anxious to hear.

One major turnoff I had with LESS was when I was getting errors just by using the standard @keyframe animation syntax. So ultimately I had to break that stuff off into another file, but those files had to be .css so as to not trigger the error, then I also couldn't @import them (literally include contents) because of the file extension. Maybe they've fixed that, not sure.

Stylus is nice too, largely because of it's flexibility. It's not going to bitch at you nearly as much as the others. Things like braces and semicolons are optional, not required one way or the other. While Stylus is nice and powerful and robust, I ultimately think the development behind it isn't strong enough to instill confidence in me. It's largely one dude (TJ Holowaychuk).

SASS ultimately won out because it's the most mature, easiest to find information and help about, seems to have the most active and robust development, and has the least bugs.

Tutorials Around Here and Final Words

Just for the record, I'm not going to start putting SASS in basic CSS tutorials around here. CSS will very likely outlive SASS. And besides, I think understanding CSS is far more important than some specific preprocessor way of doing things. In fact, if a CSS newbie asked me if they should learn SASS as they are learning CSS, I'd probably say no. Learn how CSS works and then later see how preprocessors can help you. That's the opposite of how I feel about JavaScript and jQuery, where I feel learning jQuery first is fine. Weird.

Essentially I think you should know what writing really good CSS is like. What that final CSS file looks like. The very file that the browser reads and processes to style websites. Then use a preprocessor to make that final, fabulous CSS file easier to write and maintain.


1 Yeah, it has Subversion built in, but it feels a bit tacked on. If you're into Subversion (and that's fine, it's easy) I think you're better off with Versions.

2 Except for little things like this or at work where I need to start up local servers running Python and stuff like that. But even then, begrudgingly.

3 At Wufoo, we moved from combining and minifying scripts on the fly with PHP to combining and minifying through preprocessors, which alleviated server load and made for even more efficiently compressed CSS.

4 Obviously by FTP I mean SFTP.