Railsmagazine60x60 Beautifying Your Markup With Haml and Sass

by Ethan Gunderson

Issue: Vol 2, Issue 1 - All Stuff, No Fluff

published in June 2010

Ethan gunderson

Ethan Gunderson is a Software Apprentice at Obtiva, a Chicago based agile consultancy. He loves programming day and night, much to his girlfriend's dismay. While not digging through code, he can also be found drinking craft beers, being a slight coffee snob, and losing games of Settlers of Catan.

Obtiva.com

ethangunderson.com

twitter.com/ethangunderson

If there's one thing that I dislike about Rails, it's ERB. It's not just ERB either, it's views in general. Often referred to as the ugly step-sister, views are neglected in MVC frameworks, Rails included. Enter Haml and Sass, two templating languages that aim to take the pain away from developing views and stylesheets.

Haml, short for XHTML Abstraction Markup Language, and Sass, short for Syntactically Awesome StyleSheets, are templating languages that express HTML and CSS in an outline form with a white space defined structure (think Python). They are capable of producing the same markup of their more verbose counterparts, but also add things like filters and css variables to the party.

Haml and Sass are based on a few primary principles:

  • Markup should be beautiful
    Let's face it, ERB looks like garbage. The nature of Haml and Sass' nested, whitespace defined structure ensures that line noise is kept to a minimum. The result is markup that is extremely easy to read, understand and change.
  • Markup should be meaningful
    Since Haml and Sass are whitespace defined, every character matters. No keystroke goes to wasted markup. And, since Haml and Sass has nesting qualities, the frameworks know when  an element or selector is closed.
  • Markup should be well indented
    Have you ever opened up an ERB template only to find that it's completely unreadable due to its indentation? Various styles of closing tags, some on new lines, some on the same line, some missing all together. It can turn into a real mess. Thankfully, since Haml is whitespace defined, this kind of situations are nearly impossible.
  • Markup should be DRY
    HTML and CSS are anything but DRY. HTML is incredibly verbose, with constant opening and closing of tags. CSS selectors are repeated, possibly multiple times in large projects. Every keystroke should be important, not meaningless fluff.

Installation

Haml and Sass come bundled together in one gem. It's important to note, however, that they are not dependent on each other. You can use them independently. To install, run:

sudo gem install haml

At this point, you can add Haml and Sass to your Rails project by either adding the gem to your environment.rb file, or by adding it as a plugin, like so:

haml --rails /path/to/project

Usage

Now that the gem is installed, go ahead and run

haml -help
sass -help

to see what options you have at the command line. Other than what you'll find listed there, you'll also have access to a couple of converters shown below:

html2haml /path/to/html /path/to/haml
css2sass /path/to/css /path/to/sass

These converters are great for playing around with syntax or porting over an older project.

You'll also have access to an interactive sass console, similar to irb. Here is an example of using Sass to subtract two color values in the interactive console

sass -i
>> #fffff - #111
#eeeeee

Haml

/system/graphics/354/large/haml.jpg?1277141113

Syntax

The fundamentals that make up a Haml document are:

  • % (percent character) - HTML Element
  • . (period character) - Class
  • # (pound character) - ID

Let's take a look at a really basic html file:

haml-syntax-example1.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Look, we're using Haml!</title>
  </head>
  <body></body>
</html>

And now, the equivalent Haml:

haml-syntax-example1.haml

!!!
%html
  %head
    %title Look, we're using Haml!
  %body

I think it's important that you notice the things that I'm not doing in the Haml example. There's no crazy doc string that no one can remember, no needless closing tags. All we have is extremely easy to read markup.

Let's get a little more complex:

haml-syntax-example2.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Look, we're using Haml!</title>
    </head>
    <body>
      <div id='content'>
        <div class='post' id='first'></div>
      </div>
    </body> 
</html>

And the equivalent Haml:

haml-syntax-example2.haml

!!!
%html
  %head
    %title Look, we're using Haml!
  %body
    %div#content
      %div.post#first

As you can see, we've added a div with id "content" that contains a single div with a class of "post" and an id of "first". But, there's a little more we can do here. Since the div element is used so much, it is actually the default element. If you define a class or id without specifying an HTML element, a div is used. With that in mind, our previous example can be refactored to this:

haml-syntax-example3.haml

!!!
%html
  %head
    %title Look, we're using Haml!
    %body
      #content
        .post#first

Ruby

Inserting Ruby evaluated code into a document is accomplished  by using the equal character (=). The code is evaluated and then inserted, exactly like <%= %> in ERB. You can also use the = at the end of a HTML element tag.

Running Ruby is accomplished using the hyphen character (-). Ruby blocks also don't have to be closed in Haml, they are closed for you based on indentation. A block is evaluated whenever markup is indented past the evaluation character.

Interpolation can also be accomplished in plain text using #{}.

%span Hello my name is #{author.name}.

With that info in mind, let's see how you would write a common ERB file in Haml

haml-erb-example1.erb

ERB

<% @posts.each do |post| %>
  <span class='title'><%= post.title %></span>
  <div class='post'><%= post.content %></div>
  <span class='author'><%= post.author %></span>
<% end %>

haml-erb-example1.haml

Haml

-@posts.each do |post|
  %span.title= post.title
  .post= post.content
  %span.author= post.author

Filters

In Haml, a colon character (:) signifies that a filter is being used. The filter takes the indented block of text and passes it to whatever filter program is being called. The result of the filter call is then added to the rendered html. Haml comes with several filters out of the box. Plain, javascript, cdata, ruby, erb, markdown just to name a few. It is also possible to write your own filters.

An example of using a Javascript filter:

filter-example.haml

:javascript
  alert('Whoa! This is Javascript!');

Rendered result:

filter-example.html

<script type='text/javascript'>
  //<![CDATA[
    alert('Whoa! This is Javascript!');
  //]]>
</script>

Sass

/system/graphics/355/large/sass.jpg?1277141149

Syntax

Typically CSS is riddled with repeated names. Take a look at the following example:

sass-syntax-example1.css

#footer {width: 850px; padding: 5px; margin-bottom: 10px; font-weight: 900; font-size: 1.2em;}
#footer img {padding: 10px; float: right;}
#footer a {text-decoration: none;}

Now compare it to the equivalent Sass:

sass-syntax-example1.sass

#footer
  width: 850px
  padding: 5px
  margin-bottom: 10px
  font-weight: 900
  font-size: 1.2em
  a
    text-decoration: none
  img
    padding: 10px
    float: right

I find this a lot more easier to write and read. Since Sass is whitespace defined, the first selector isn't indented at all, and everything indented under it will either be a property on that selector, or a nested rule.

There are two different ways to write properties. Besides the example above, you may also move the colon to the beginning of the property. This may help you tell the difference between properties and nested rules.

sass-syntax-example2.sass

#footer
  :width 850px
  :padding 5px
  :margin-bottom 10px
  :font-weight 900
  :font-size 1.2em
  a
    :text-decoration none
  img
    :padding 10px
    :float right

For consistency sake, we will be using the 'attribute:' style for the rest of this article. It's also important to note that in the earlier versions of Sass, indentation had to be two spaces. However, with the 2.2 release of Sass, this is no longer true. The only requirement is that the spacing be consistent throughout the stylesheet.

Nested properties

In an effort to keep the markup DRY, Sass provides a way that we can clean up the previous example even more. Just like selectors, you can nest properties. That allows us to get rid of the hyphens in the font definitions.

sass-nesting-example-1.sass

#footer
  width: 850px
  padding: 5px
  margin-bottom: 10px
  font
    weight: 900
    size: 1.2em
  a
    text-decoration: none
  img
    padding: 10px
    float: right

Variables

Easily one of the best features of Sass, is the ability to define variables for use throughout your stylesheet. Variables can be anything from font families to color definitions. Attributes are assigned a variable with the equal sign instead of the colon.

sass-variables-1.css

h1 {
  color: #2887e5; }
 
#footer {
  font-family: Arial;
  font-color: #2887e5; }

Could be written like this:

sass-variables-1.sass

!font_family= Arial
!blue= #2887E5
 
h1
  color= !blue
 
#footer
  :font
    :family= !font_family
    :color= !blue

It's not hard to imagine all the uses for this. For example, being able to redefine a color scheme by only changing a couple of variables instead of hunting and pecking.

Variable math

Nope, you didn't read that wrong, you can actually perform basic math on variables! If you recall, I actually did that in the interactive sass example. Let's go back to the console for another look:

sass -i
>> !width = 5px
5px
>> !extra_width = 30px
30px
>> !extra_width - !width
25px

This gives a lot of flexibility when defining your stylesheets. Take defining a header height for instance.

!base_height= 40px
!tall_header_height= !base_height * 1.33
!short_header_height= !base_height * .66

By using those three variables while designing the header of my site, it allows for easy changes. All I would need to do is change the !base_height variable, and the rest of the header would scale accordingly. Pretty cool!

Mixins

Mixins allow you to reuse entire sections of your stylesheet.

sass-example-mixin-1.css

#header a{text-decoration: none; color: black;}
#footer a{text-decoration: none; color: black;}

Could be written like this:

sass-example-mixin-1.sass

=plain_a
  color: black
  text-decoration: none
 
#header
  +plain_a
#footer
  +plain_a

Mixins can also take parameters, and have the option for defaults. Expanding on our previous example, we could change the color of our a tags to "green" in the content area by passing a variable:

sass-example-mixin-2.sass

=plain_a(!color = black)
  color= !color
  text-decoration: none
 
#header
  +plain_a
#content
  +plain_a(green)
#footer
  +plain_a

Note that we are setting a default value of color to be "black", and hence the a tag in the header and footer has a color of black whereas passing a value of "green" makes the a tag color for content to be green.

Conclusion

There you have it, everything you need to get started with an awesome set of templating languages. But in reality, I've only scratched the surface of what you can accomplish with Haml and Sass. I urge all of you to go out and read the fantastic documentation and start converting your current projects.

Just remember, with time and effort, app by app, we can get rid of the ERB plague once and for all!