From my talk on learning HAML and SASS for Magma Rails 2011.
Slides and an HTML reference version below. A sample application with all code examples is at https://github.com/jonathandean/SASS-and-HAML-FTW.
The slide content is posted below the embedded slides for easier reading on the web. Sorry for all of the bullets, it just didn’t seem worth the time to rewrite as paragraphs.
An introduction to HAML
What is HAML?
- A more concise syntax for coding HTML in your Rails app
- Uses indentation for nesting markup
- Reduces code (no closing tags)
- Fixes the messy markup problem that often clutters views
- Uses a CSS-like syntax that is easier to read and compare to your style rules
- Makes coding your View layer faster
- Note: doesn’t affect render time or client-side performance, simply reduces coding efforts and makes the developer more efficient
- Easy to learn!
Using HAML in Rails
- Use it as a replacement for ERB files
- If you use Bundler, add this to your Gemfile:
gem 'haml'
- Files named filename.html.haml (instead of filename.html.erb) will be interpreted by HAML instead of ERB
A comparison
HTML
<body>
<div id='wrapper'>
<div class='stuff'>
<a href='#'>Top</a>
</div>
</div>
</body>
7 lines, 78 characters
HAML
%body
#wrapper
.stuff
%a{:href => '#'}
4 lines, 36 characters
Tag names
Use % and then the name of the tag
HAML
%body %div
HTML
<body></body> <div></div>
ID’s
ID’s are just like they are in CSS. They begin with # and are followed by the ID name
HAML
%div#mine %p#yours
HTML
<div id='mine'></div> <p id='yours'></p>
Classes
Classes are also just like they are in CSS. They being with a . and are followed by the class name
HAML
%div.mine %p.yours
HTML
<div class='mine'></div> <p class='yours'></p>
Here’s one way to have multiple classes. We’ll go over more options later.
HAML
%div.mine.yours
HTML
<div class='mine yours'></div>
div is the default tag name
If you want to leave out the tag name and just put classes and/or ID’s, %div is assumed
HAML
%div#mine
or just
#mine
HTML
<div id='mine'></div>
ID’s and classes together
You can put ID’s and classes together as well
HAML
#mine.yours #mine.yours.his
HTML
<div class='yours' id='mine'> </div> <div class='yours his' id='mine'> </div>
Nesting
Tags are nested by indentation only. There are no closing tags!
HAML
#mine %p.yours hi!
HTML
<div id='mine'> <p class='yours'>hi!</p> </div>
Text content
Text can go at the end of the line if there’s no other content to nest. It can also be nested itself, which is required with other nested content.
HAML
#mine %p.yours hi!
or
#mine
%p.yours
hi!
HTML
<div id='mine'> <p class='yours'>hi!</p> </div>
Another example with text content and a tag
#mine
%p.yours
hi!
%span dude
HTML
<div id='mine'>
<p class='yours'>
hi! <span>dude</span>
</p>
</div>
This will not work and will produce an error. You cannot have text content on the same line as its parent tag and also nest other tags within it.
#mine
%p.yours hi!
%span dude
Attributes
Attributes can be added using a Ruby Hash syntax:
HAML
%a{:href => 'http://hi.com'} hi
%input{:type => 'submit'}
HTML
<a href='http://hi.com'>hi</a> <input type='submit'>
Or a more HTML-like syntax:
HAML
%a(href='http://hi.com') hi %input(type='submit')
HTML
<a href='http://hi.com'>hi</a> <input type='submit'>
Boolean attributes
Use boolean values for attributes such as ‘selected’. To have a value selected, use:
HAML
%input{:selected => true}
or
%input(selected=true)
or
%input(selected)
HTML
<input selected>
To have a value deselected it’s the same but with false instead of true. This isn’t necessary in these examples of course (because you can just leave out the selected attributes) but is useful if you are getting the value from a variable instead of typing in the boolean value directly.
HAML
%input{:selected => false}
or
%input(selected=false)
HTML
<input>
Using an array for ID’s and Classes
For the ID attribute it will separate the values by an _ (underscore.) For the Class attribute it will add them as separate classes.
HAML
%p{:class => ['one','two']} hi
%p{:id=> ['one','two']} hi
HTML
<p class='one two'>hi</p> <p id='one_two'>hi</p>
HTML??
You can also use regular HTML. This is handy when converting a file over time, using copy and paste with something like a tracking code, or anything else complicated and hard to write as HAML.
HAML
#myParagraph <p>Hello there!</p>
HTML
<div id='myParagraph'> <p>Hello there!</p> </div>
Adding Ruby code
Start the line with an = (equals sign) for Ruby code that will output something into the view. Start the line with a – (dash) for Ruby code that doesn’t output anything (which should be rare considering this code usually belongs in the controller.)
HAML
%p= 'hi '*5
HTML
<p>hi hi hi hi hi</p>
Note in the following example how the ‘end’ isn’t necessary to close the block. This is done for you based on indentation just like everything else in HAML.
HAML
- 5.times do %p= 'hi'
HTML
<p>hi</p> <p>hi</p> <p>hi</p> <p>hi</p> <p>hi</p>
Ruby interpolation
Use #{} to interpolate Ruby code
HAML
- awesome_guy = 'Jon'
%p Hi #{awesome_guy}
is the same as
- awesome_guy = 'Jon'
%p= "Hi #{awesome_guy}"
HTML
<p>Hi Jon</p>
Filters
Filters start with a : (colon) and let you put in indented content to be interpreted in a special way. For example, the :javascript filter wraps some JavaScript in <script> and CDATA tags for inline JavaScript.
HAML
:javascript
alert('Hello there!');
HTML
<script>
//<![CDATA[
alert('Hello there!');
//]]>
</script>
The :css filter is similar and wraps some CSS in <style> and CDATA tags for inline CSS.
HAML
:css
.mine{
color: red;
}
HTML
<style>
/*<![CDATA[*/
.mine{
color: red;
}
/*]]>*/
</style>
There are quite a few other filters:
-
:plain
does not parse the content
-
:escaped
same as plain but HTML-escapes the text
-
:ruby
pass the content to the normal Ruby interpreter
-
:sass
parse the content with SASS to produce CSS output
- and more! (see the docs)
You can also make custom filters if you have special needs. See the documentation for HAML::Filters
An introduction to SASS
What is SASS?
- Syntactically Awesome Stylesheets
- Smarter CSS
- Gives you variables and methods (mixins) for CSS
- Lets you nest declarations
- Provides selector inheritance
- Lets you do math with your variable values
- Works by compiling .sass or .scss files into normal valid .css
- Commonly used in Ruby on Rails applications but can be used in any web project
Two available syntaxes
Both syntaxes have the same features and produce the same CSS output.
SASS
- HAML-style indentation
- No brackets or semi-colons, based on indentation
- Less characters to type
- Enforced conventions/neatness
SCSS
- Semi-colon and bracket syntax
- Superset of normal CSS
- Normal CSS is also valid SCSS
- Newer and recommended
A comparison
SASS
$txt-size: 12px
$txt-color: #333
$link-color: #999
#main
font-size: $txt-size
color: $txt-color
a
color: $link-color
SCSS
$txt-size: 12px;
$txt-color: #333;
$link-color: #999;
#main{
font-size: $txt-size;
color: $txt-color;
a{
color: $link-color;
}
}
Both of these compile to:
CSS
#main{
font-size: 12px;
color: #333333;
}
#main a{
color: #999999;
}
In the examples we’ve already demonstrated how you can assign values to a variable and how basic nesting works. Variables are pretty self-explanatory when you look at the example. They start with a $, are assigned using a : (colon) just like CSS properties (not using an = like in other languages) and output the set value when referenced again without the : following it.
Nesting in SASS/SCSS not only makes you code more readable it saves you a lot of typing. You can see from the example how putting an anchor (a) tag inside of #main causes it to make a rule for ‘#main a’ for you without making you type #main again. It’s also much easier at a glance to see what’s going on and provides a more direct correlation to the structure of the markup.
Referencing the parent when nesting
You can reference the parent selector with & (ampersand)
SCSS
#content{
font-size: 12px;
&, a{
color: #333;
}
}
CSS
#content{
font-size: 12px;
}
#content, #content a{
color: #333;
}
Selector inheritance
You can also extend other CSS declarations with @extend
SCSS
.error{
color: red;
}
.seriousError{
@extend .error;
font-weight: bold;
}
CSS
.error, .seriousError{
color: red;
}
.seriousError{
font-weight: bold;
}
Mixins
Mixins are sets of reusable styles, almost like methods in other languages.
SCSS
@mixin awesome-text{
font-size: 24px;
font-weight: bold;
color: blue;
}
p{
@include awesome-text;
}
CSS
p{
font-size: 24px;
font-weight: bold;
color: blue;
}
Mixins can also take parameters!
SCSS
@mixin awesome-text($size){
font-size: $size;
font-weight: bold;
color: blue;
}
p{
@include awesome-text(24px);
}
li{
@include awesome-text(18px);
}
CSS
p{
font-size: 24px;
font-weight: bold;
color: blue;
}
li{
font-size: 18px;
font-weight: bold;
color: blue;
}
Here’s a more advanced mixin example that has the common styles necessary for image-replacing a link inside of another element. Assume this is your markup:
<h1><a href="/">Home page</a></h1>
SCSS
@mixin image-replace($image-url){
&, a{
display: block;
background: url($image-url) no-repeat;
}
a{
text-indent: -99999px;
text-decoration: none;
}
}
h1{
@include image-replace('images/header.gif');
&, a{
width: 200px;
height: 30px;
background-position: 0px -100px;
}
}
CSS
h1, h1 a{
display: block;
background: url(“images/header.gif”) no-repeat;
}
h1 a{
text-indent: -99999px;
text-decoration: none;
}
h1, h1 a {
width: 200px;
height: 30px;
background-position: 0px -100px;
}
Mathematic operations
You can do simple math operations with your variable values, even if they have units
SCSS
$page-width: 500px;
$sidebar-width: 100px;
$content-width: $page-width - $sidebar-width;
#main{
width: $page-width;
#sidebar{
width: $sidebar-width;
}
#content{
width: $content-width;
}
}
CSS
#main{
width: 500px;
}
#main #sidebar{
width: 100px;
}
#main #content{
width: 400px;
}
The supported mathematic operators are +, -, *, / and %
Because the division operator (/) is also valid in normal CSS, the following is not changed:
SCSS
font: 10px/8px;
CSS
font: 10px/8px;
Therefore, there are only three cases where / is used as division.
1. When one of the values is stored in a variable
SCSS
$content-width: 500px; width: $content-width/2;
CSS
width: 250px;
2. When surrounded by parenthesis
SCSS
width: (500px/2);
CSS
width: 250px;
3. When part of another math expression
SCSS
width: 10px + 500px/2;
CSS
width: 260px;
To use variables in the CSS version without doing math operations, use #{} interpolation (described in more detail in the next section.)
SCSS
$some-val: 10px;
$another-val: 8px;
font: #{$some-val}/#{$another-val};
CSS
font: 10px/8px;
Interpolation
You can use variables in selectors and property declarations using interpolation with #{}
SCSS
$class-name: wrapper;
$attribute-name: font;
div.#{$class-name}{
#{$attribute-name}-size: 12px;
}
CSS
div.wrapper{
font-size: 12px;
}
Note: If you use RubyMine, it may falsely mark this syntax as invalid.
Control directives
@if
SCSS
$type: big;
p{
@if $type == big{
font-size: 24px;
}
}
CSS
p{
font-size: 24px;
}
@if / @eles
SCSS
$type: small;
p{
@if $type == big {
font-size: 24px;
} @else if $type == medium{
font-size: 18px;
} @else {
font-size: 16px;
}
}
CSS
p{
font-size: 16px;
}
@for
SCSS
@for $i from 1 through 3 {
.item-#{$i} {
width: 10px * $i;
}
}
CSS
.item-1{
width: 10px; }
.item-2{
width: 20px; }
.item-3{
width: 30px; }
@each
SCSS
@each $item in item1, item2{
.#{$item}{
width: 500px;
}
}
CSS
.item1{
width: 500px; }
.item2{
width: 500px; }
@while
SCSS
$i: 6;
@while $i > 0 {
.item-#{$i} {
width: 10px * $i;
}
$i: $i - 2;
}
CSS
.item-6{
width: 60px; }
.item-4{
width: 40px; }
.item-2{
width: 20px; }
Importing other SASS files
Import other .sass or .scss files using @import
SCSS
@import "reset";
or with the file extension if you really want
@import "reset.css.scss";
You can also create partials that will only be imported to other files and not compiled to .css files themselves. Just name the partial with an underscore in the front, such as _sample.css.scss. Now import the same way:
@import "sample";
Partials are handy for organizing styles into multiple files but compiling to only one file for use on the web.
Note: when using the Rails 3.1 asset pipeline, name your files with .css.scss or .css.sass extentions instead of just .scss or .sass (otherwise the .css part isn’t necessary.)
Nested properties
SASS can simplify the declaration of name-spaced CSS properties:
SCSS
.sassy{
font:{
size: 12px;
weight: bold;
}
}
CSS
.sassy{
font-size: 12px;
font-weight: bold;
}
Color operations
You can also do mathematic operations on color values:
SCSS
color: #010203 + #040506;
CSS
color: #050709;
How this is computed:
#010203 + #040506 = #050709
01 + 04 = 05
02 + 05 = 07
03 + 06 = 09
Variable defaults
Variable defaults will only assign the variable if it hasn’t been defined yet. This is handy for when you import a partial in some files but not in all of them and want the value from the partial to take precedence if it has already been defined.
In this example, $page-color will have the value of #333 because it was already defined and so the second assignment doesn’t happen:
$page-color: #333; $page-color: #666 !default;
In this example, $section-color is defined as #999 because it hasn’t already been defined previously:
$section-color: #999 !default;
Using SASS without Rails
SASS can be used for any project you have CSS by installing the gem. Ruby is required to run for SASS to be installed and run, but this can be installed locally and isn’t required on the server. Once you have the compiled CSS output, just upload those files to the server any way you normally would.
To install the gem (after Ruby is already installed):
gem install sass
Then the easiest way is to have SASS watch for changes to the file and auto-compile on each save. This will cause input.scss to compile to output.css automatically:
sass --watch path/to/input.scss:path/to/output.css
Using SASS with Rails 3.1
The good news is that SASS is included by default with Rails 3.1 for use with the Asset Pipeline! Just put your filename.css.scss files in app/assets/stylesheets/ and the Asset Pipeline will deal with compiling them for you. See the sample application for an example.
Using SASS with older versions of Rails
First, add the following to your Gemfile if you’re using Bundler:
gem "sass"
(If you aren’t using Bundler then install and configure the gem as you would any other gem.)
You can really put your sass files anywhere, but I would recommend using the new convention introduced by Rails 3.1 for if you upgrade some day. You also don’t need the .css part of the filename but using filename.css.scss will once again make an upgrade easier later.
Resources and next steps
The documentation for HAML and SASS are the best places to learn more. They are short, easy reads and have plenty of examples to help you out. Also check out and modify the examples in the sample application to understand more of how they work. Finally, start making your own applications using HAML and SASS. It’s also really easy to convert your existing applications since CSS is also valid SCSS and HAML also allows plain HTML. So you can really just change your file extensions now and convert as you go!






