Written by: steve ross on June 18th 2009

So it's been a while since Hampton Catlin first announced Haml -- a templating language originally anticipated primarily as a drop-in replacement for ERB in Rails. The attactive part about Haml for me was that semantic markup in HTML looks and feels different from how it is represented in the DOM and that difference leads to confusion creating selectors in stylesheets and Javascript. What a time waster.

How Hampton originally conceived Haml, and how it remains to this day, is as a whitespace-sensitive syntax. This is something you either love or you hate. There isn't much middle ground. Indentation implies containment. Here's an example of some Haml markup:


#header-block
  Haml is cool!
  Can't you just dig it?

The text is contained inside a DIV with an id of header-block. To me, this is natural, as I am obsessive about indenting my code anyhow. Just for the record, the output (which I can compile from the command line) is:


$ haml foo.haml
Haml is cool! Can't you just dig it?

Now to Sass and only just superficially. Let's assume I have a stylesheet called foo.sass (I know foo is a tremendously meaningful word, but stick with me). Then, to style this header, I will just:


#header-block
  :font
    :family   Arial, sans-serif
    :weight   bold
    :color    #311
  :text-align center

which visually relates exactly to the #header-block in my markup. Here's the output:


$ sass foo.sass
#header-block {
  font-family: Arial, sans-serif;
  font-weight: bold;
  font-color: #311;
  text-align: center; }

Notice how the font attributes have been expanded into their proper CSS attributes? This is a trivial example and doesn't even begin to scratch the surface of why Haml and Sass are cool, but let's install them.

Installing Haml and Sass

The only prerequisite for Haml and Sass is Ruby. If you are on a Mac, you will already have Ruby installed. Many Linux distros have Ruby or you can get it through one of the various package managers. If you are on Linux, you probably already know how to do this. On a PC, check out the "Windows Installer":http://rubyinstaller.rubyforge.org/wiki/wiki.pl. Not the very latest version of Ruby but it will do just fine.

Now that you have Ruby installed and added to your path, verify that by typing:

ruby -v

in a console window. You should get something like:


$ ruby -v
ruby 1.8.6 (2007-09-23 patchlevel 110) [i686-darwin9.1.0]

See, my Ruby installation isn't the latest and greatest either!

On to installing Haml and Sass.


gem install haml

That's it. If you don't have administrative privileges for the binary directory (say, on a Mac), just do:


sudo gem install haml

and type in your administrator password. If you ain't the administrator of your computer, you probably don't want to install software anyhow.

What About My Legacy Code?

You almost certainly have plenty of HTML, CSS, and maybe RHTML laying around. How about that? Well, there are one-step converters:

css2sass css-file outputs a neatly-formatted Sass file to the console. So, just do:

css2sass foo.css > foo.sass and you're off and running.

There is a parallel tool called html2haml. Same deal:

html2haml foo.html > foo.haml and you're ready to go.

For RHTML, which embeds Ruby, your mileage may vary. In particular, html2haml may miss the subtlety of begin/end code blocks. So, in an example containing embedded Ruby:


<% content_for :analytics do -%>MY_FINE_GOOGLE_ANALYTICS<% end -%>

and the result is:


$ html2haml foo.rhtml
- content_for :analytics do
MY_FINE_GOOGLE_ANALYTICS
- end

Not exactly right, as indentation signifies containment (remember?) and Haml considers Ruby blocks as containing code. The correct way to write this is:


- content_for :analytics do
  MY_FINE_GOOGLE_ANALYTICS

A fairly trivial edit.

Ok, So You Have Another Templating Language. Big Deal.

The big deals are multiple. First, over time you will discover that Haml makes you more productive. More than that it does what those in the Ruby community call "make programmers happy." Wow! What a claim. But really, that's the effect it has on me and on other developers I work with. One guy hired me to take on part of a project and I showed him Haml. His response the next day was "where have you been all my life?".

But Sass is the place where the greatest clarification of your intent happens. Hampton and "Nathan Wiezenbaum":http://nex-3.com/ have driven out Sass well beyond an alternative syntax for CSS. The best way to show this is by example. Say you have an indecisive client. I know that seldom happens, but suppose it did. You sit down with the client and decide that the background of the site should be pink and the links almost red.


body
  :background-color #FB77F8
  :color            #000
  a
    &:link
      :color        #FF182D

This kind of stuff tends to percolate throughout your styles, so you might have td > a that also had to be that attractive color red. So, using Sass variables, you can do a bit of refactoring. Refactoring in stylesheets! That's a big deal. Think about it:


!body_background_color =    #FB77F8
!body_text_color =          #000
!active_link_color =        #FF182D
!body_text_color =          #000

body
  :background-color= !body_background_color
  :color           = !body_text_color
  a
    &:link
      :color       = !active_link_color
  td
    a
      &:link
        :color     = !active_link_color

This compiles down to:


body {
  background-color: #fb77f8;
  color: black; }
  body a:link {
    color: #ff182d; }
  body td a:link {
    color: #ff182d; }

Notice how I dropped in the td I could reuse the variable? Another thing to notice is the specificity of the selectors. In CSS the most specific rule wins. The more accurately you describe the element you want to affect, the more reliably the browser applies the rule. Who hasn't been bitten by specifying a rule that is too general and affects elements other than those intended? But Sass encourages the nesting of elements to be quite specific about what you intend to style.

Sass Looks Like a Lot of Code and Syntax

At first blush, the number of lines of Sass it takes to generate a small amount of CSS, but the flexibility and clarity of this approach to describing styles, plus its reusability make it far easier to leverage some more advanced concepts like mixins. Here's a flavor of those, building on the above example:


!body_background_color =    #FB77F8
!body_text_color =          #000
!active_link_color =        #FF182D
!hover_link_color =         !active_link_color - #303030
!body_text_color =          #000

=link_color_set
  a
    &:link
      :color       = !active_link_color
    &:hover
      :color       = !hover_link_color

body
  :background-color= !body_background_color
  :color           = !body_text_color
  +link_color_set
  td
    +link_color_set

Now, any place a set of link colors needs to be specified, I just invoke the mixin link_color_set and presto! It adds the styles defined in the link_color_set module.

A couple of other things of note in the example above: First, I used arithmetic to create a slightly different but related hover link color. You don't have to use this, but arithmetic is handy for this use case. Second, with almost no work at all, I redefined every link_color_set to have a hover pseudo-class.

By separating the variable and mixin module definitions into separate files, you can import them and reuse them across all the stylesheets in your application. Heck, if you really like them, reuse them across multiple projects!

Some Places to Go From Here

Not discussed here is the integration between Haml/Sass and various Ruby Web frameworks such as Rails, Sinatra, Ramaze, merb, etc. You don't need to take a separate step to compile your Haml to HTML or your Sass to CSS. This happens on the fly as the first page is rendered. Only when you change your Haml or Sass does the file get recompiled.

Further Reading: