Wednesday, September 2, 2009

I Hate Sass, but I Love Compass

Generally speaking, I have a rule about generators. Don't ever use markup that generates more markup if the final product is straightforward anyway, and doesn't require too much typing.

I have another rule: don't get involved with code generators that remove your ability to use good existing tooling, unless they generate orders of magnitude more code and are orders of magnitude easier to understand. This is why I never got into Modello - not even when working on the Maven or Plexus. It takes nearly as much XML to generate a trivial amount of fairly trivial code.

For these reasons, amongst others, I hate SASS. It takes something trivial and straightforward with great tooling (CSS) and turns it into marginally less, but equally readable code (YAMLish). It also convolutes two styles of markup (stylesheet language which describes markup, and some YAMLish code which is markup). It just reeks of those xml-based scripting languages - this is a type of recursion up with which I cannot put. Or so I thought.

Enter: Compass.

It was for frameworks like this that frameworks like Blueprint CSS were created. It's symbiotic - without both, each are only marginally interesting. But together they become something more - like the Wonder Twins.

The Problem With CSS Frameworks

I'm going to pick on Blueprint, though the problem exists universally. The benefits of a pre-defined CSS framework are severalfold: attractive defaults across several browsers, nice typography for printing, and a common reference-point for merging other toolkits into your application (plugins, eg, jquery). Blueprint is great for HTML element defaults, and even for defining simple CSS class names like "highlight" or "hide". But an entire framework? Blueprint (and others) also have a rich set of definitions for layouts (be it "columns", "grids", "table", etc.). The nightmare is using them.

Here is an example of a value "23" set in the last column, with a specific width, prepended by 20 columns, and appended by 1, from Blueprint's own documentation:
<div class="prepend-20 span-1 append-1 last">23</div>
This harkens back to the dark-ages of the web: mixing content and layout. Though the mixing is done via css, the problem is that the CSS (more flexible than, say, a table) is still describing how the HTML should be displayed (however vaguely), not what it is.

Keeping Semantic

Up until now, we are stuck. With only HTML and CSS available, it is difficult to utilize Blueprint grids without giving something up. We can either do the above (describe layout in HTML's class attributes), or attempt to define a friendlier CSS. Let's pretend (with a stretch of the imagination) that the div declared above contains my age. A descriptive, layout neutral HTML would appear thusly.
<div class="my-age">23</div>
(Or better yet, in HAML:)
.my-age 23
Now that's separation! Nice, semantic HTML. No layout. Of course, now we have a problem: Blueprint knows nothing about the new class "my-age". How can I explain to Blueprint that the CSS class I just created should act like "prepend-20 span-1 append-1 last"? In short: you cannot. Not without hacking up your Blueprint CSS file (non-portable - you'll lose a lot of the benefit of using a CSS framework), or simply copying the Blueprint code into your own custom CSS class (better, but you've now completely missed the benefit of using Blueprint).

How Compass Aids Separation

Compass is a CSS generation framework. Actually, let me back up. SASS is a CSS generation framework, while Compass is the glue between Blueprint (and other CSS frameworks) and SASS. Remember me saying I hated SASS? It's true. But Compass pre-packages these CSS frameworks and makes them available as mixin modules... and that I love. So let's get on with it.

Assuming I have Compass/SASS installed and operational, fixing the above problem is trivial.
@import blueprint
What Compass does is provide those Blueprint mixins (+prepend, +span, +append, +last - complements of @import blueprint), while SASS provides much of the rest, compiles the given markup to CSS (yuck). What you get is the kind of inheritance lacking in CSS proper.

Opening the Blueprint Grid CSS file we see the classes we used are defined as this:
.prepend-20 { padding-left: 800px;}
.span-1 {width: 30px;}
.append-1 { padding-right: 40px;}
.last { margin-right: 0;}
But after Compass/SASS we get a new CSS file generated, which contains the following:
body {
padding-left: 800px;
width: 30px;
padding-right: 40px;
margin-right: 0;
We get to keep our Blueprint pristine, while able to keep markup semantic and layout separate. What's more, is that beyond the brute-force method of copying Blueprint class-for-class, Compass provides a host of other useful mixins. In fact, the above SASS can be simplified as something more succinct:
@import blueprint
+column(20, true)

Wrap Up

What I stated in the intro is still true. Markup that generates markup often lacks any sort of real tooling, and this is no exception. What I find myself doing is writing CSS using CSSEdit against the SASS-generated CSS. Once I'm happy with the changes visually, I make changes in the SASS file. Is it extra effort? Yes - but not much. And the payoff to project bootstrapping speed, along with the overall maintainability, makes it worth it. Try it on your next small Rails project (along with HAML) - you may find it hard to go back.

No comments: