First of all, let’s get Sass up and running. To get it, get Ruby and run

gem install haml

To compile a Sass file into CSS, run

sass style.sass style.css

If you’re using a Ruby framework like Rails or Merb, you can set Sass up to compile automatically. If you aren’t - Sass works great with non-Ruby stuff - you can use the Compass Sass framework to do the same thing.

You can convert your existing CSS files to Sass using the css2sass utility:

css2sass style.css style.sass

css2sass knows about several neat Sass tricks, so it’s usually a good idea to start off your conversion using it. For this tutorial, though, open up a brand new Sass file so you can follow along.

Syntax

The first thing to learn about Sass is the syntax. Sass uses indentation and line breaks rather than curly braces and semicolons to define rules and divide up properties. This may take a little getting used to at first, but give it a try; you might find that you end up liking it (if you really hate it, Sass 2.4 will support braces and semicolons as well).

// Sass

.important
  color: red
  font-weight: bold
/* CSS */

.important {
  color: red;
  font-weight: bold; }

Nesting

Often when writing CSS, you’ll have several selectors that all begin with the same thing. For example, you might have “#main p”, “#main .notice”, and “#main .notice h2”. It’s a pain to repeat the beginning over and over again, especially when it’s long.

Sass allows you to avoid this by nesting the child selectors within the parent selector.

// Sass

#main
  p
    padding-left: 1.5em
    margin: 0.5em 0

  .notice
    font-size: 1.2em
    background-color: #fff

    h2
      font-size: 0.8em    
/* CSS */

#main p {
  padding-left: 1.5em;
  margin: 0.5em 0; }
#main .notice {
  font-size: 1.2em;
  background-color: #fff;
  #main .notice h2 {
    font-size: 0.8em; }

Notice how the output is formatted as much like the Sass code as possible. This is the default output mode, but there are other modes for all sorts of CSS formatting styles. There’s even one for compressing the CSS as much as possible!

You can also nest properties, so you don’t have to repeat stuff like border-left- all the time.

// Sass

.fakeshadow
  border:
    style: solid
    left:
      width: 4px
      color: #888
    right:
      width: 2px
      color: #ccc
/* CSS */

.fakeshadow {
  border-style: solid;
  border-left-width: 4px;
  border-left-color: #888;
  border-right-width: 2px;
  border-right-color: #ccc; }

Parent Reference

What about pseudo-selectors, like :hover? There isn’t a space between them and their parent selector, but it’s still possible to nest them. You just need to use the Sass special character &. In a selector, & will be replaced verbatim with the parent selector.

// Sass

#main
  width: 97%

  a
    color: #0000ff

    &:hover
      color: #ff0000

    &:visited
      color: #00ff00
/* CSS */

#main {
  width: 97%; }
  #main a {
    color: #0000ff; }
    #main a:hover {
      color: #ff0000; }
    #main a:visited {
      color: #00ff00; }

Variables

Sass allows you to declare variables that can be used throughout your stylesheet. Variables begin with ! and can be colors, strings of text, true/false, or numbers (with units!). To use a variable in a property, just use = instead of :.

// Sass

!main_color = #ce4dd6
!style = "solid"

#header
  border-bottom:
    color = !main_color
    style = !style

a
  color = !main_color
/* CSS */

#header {
  border-bottom-color: #ce4dd6;
  border-bottom-style: solid; }

a {
  color: #ce4dd6; }

Variables allow you to re-use colors, sizes, and other values without repeating yourself. This means that changes that should be small - tweaking the coloring or the sizing - can be done in one place, not all over the stylesheet.

Operations

In addition to just using variable values as they’re defined, you can also use operations on them. This allows you to compute element sizing and even coloration dynamically.

The standard math operations (+, -, *, /, and %) are supported for numbers, and units will be converted if possible. The same operations work for colors, where the operation is performed on the red, green, and blue components in turn.

// Sass

!sidebar_width = 150px
!main_color = #ce4dd6

#main
  border:
    color = !main_color
    style: solid
  background-color = !main_color * 1.1
  padding-right = !sidebar_width + 20px

  #sidebar
    width = !sidebar_width
/* CSS */

#main {
  border-color: #ce4dd6;
  border-style: solid;
  background-color: #e254eb;
  padding-right: 170px; }
  #sidebar {
    width: 150px; }

Interpolation

Variables can be used for more than just property values. You can use #{} to insert them into property names or even selectors.

// Sass

!border_side = "left"
!border_class = "nifty"

.#{!border_class}
  border-#{!border_side}:
    style: solid
    color: blue
/* CSS */

.nifty {
  border-left-style: solid;
  border-left-color: blue; }

Mixins

Mixins are one of the most powerful aspects of Sass. They allow re-use of styling - properties or even entire rules - without having to re-write them or move them into a non-semantic class.

To define a mixin, just write =mixin-name with some Sass nested underneath. To use it, write +mixin-name where you want it to be expanded.

// Sass

=float-left
  display: inline
  float: left

#sidebar
  +float-left

#content img
  +float-left
/* CSS */

#sidebar {
  display: inline;
  float: left; }

#content img {
  display: inline;
  float: left; }

Arguments

The real power of mixins comes when you pass them arguments. Arguments are declared as a parenthesized, comma-separated list of variables. Each of those variables is assigned a value each time the mixin is used.

Mixin arguments can also be given default values by following them with = and the value. Then the user of the mixin can choose not to pass that argument and it will be assigned the default value.

// Sass

=replace-text(!img, !x = 50%, !y = 50%)
  text-indent: -9999em
  overflow: hidden
  background:
    image = image_url(!img)
    repeat: no-repeat
    position = !x !y

#header h1
  +replace-text("/images/logo.png")

li#overview
  +replace-text("/images/overview.png", 20%)
/* CSS */

#header h1 {
  text-indent: -9999em;
  overflow: hidden;
  background-image: image_url(/images/logo.png);
  background-repeat: no-repeat;
  background-position: 50% 50%; }

li#overview {
  text-indent: -9999em;
  overflow: hidden;
  background-image: image_url(/images/overview.png);
  background-repeat: no-repeat;
  background-position: 20% 50%; }

@import

Stylesheets can get pretty big. CSS has an @import statement that allows you to break your styles up into multiple stylesheets, but each stylesheet is a separate (expensive) HTTP request. That’s why Sass’s @import statement pulls in Sass files directly. Not only that, but any variables or mixins defined in @imported files are available in the file that imported them.

Sass has a naming convention for Sass files that are meant to be imported: they begin with an underscore. You can import these files without using the underscore, and they won’t get rendered as CSS.

// Sass: _defines.sass

!main_color = #ce4dd6

=float(!side)
  display: inline
  float = !side

// style.sass

@import defines.sass

#main
  color = !main_color

  #sidebar
    +float("right")
/* CSS : style.css */

#main {
  color: #ce4dd6; }
  #sidebar {
    display: inline;
    float: right; }