A study of Sass (meta-language on top of CSS)

June 14, 2009 by Noel Gomez · 8 Comments
Filed under: Programming 

I’ve been experimenting with different technologies as I learn Ruby on Rails and recently I have been looking into using HAML instead of ERB and Sass instead of CSS.  I started using HAML a while back and I really enjoy how clean the template files look. This article has an argument for HAML and I agree with the author. I found an article on A List Apart on sprites and I thought it was a good way to showcase the awesomeness of Sass.  The article covers creating an image map with a hover effect.

The Links

The HTML for the map is below: (I modified the IDs from the original article to make it simpler to follow)

1
2
3
4
5
6
<ul id="menu" >
  <li id="menu_item1"><a href="#1"></a></li>
  <li id="menu_item2"><a href="#2"></a></li>
  <li id="menu_item3"><a href="#3"></a></li>
  <li id="menu_item4"><a href="#4"></a></li>
</ul>

Here is the same map in HAML: (WP-Syntax does not support HAML/Sass, but this is not too bad)

1
2
3
4
5
6
7
8
9
%ul#menu
  %li#menu_item1
    %a{:href => "#1"}
  %li#menu_item2
    %a{:href => "#2"}
  %li#menu_item3
    %a{:href => "#3"}
  %li#menu_item4
    %a{:href => "#4"}

The Style

In the article they discuss how the hover effect is achieved using this image. (dimensions added for clarity)
test-1
The CSS used for the hover effect is below with some minor modifications to the original:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 
#menu {
  width: 400px; 
  height: 200px;
  background: url(../../images/test-1.jpg);
  margin: 2em 2em; 
  padding: 0;
  position: relative;
}
 
#menu li, #menu a {
  height: 200px; 
  display: block;
}
 
#menu li {
  position: absolute;
  list-style: none;
}
 
#menu_item1 {margin-left: 0; width: 96px;}
#menu_item2 {margin-left: 96px; width: 76px;}
#menu_item3 {margin-left: 172px; width: 111px;}
#menu_item4 {margin-left: 283px; width: 118px;}
 
#menu_item1 a:hover {
  background: transparent url(../../images/test-1.jpg) 0 -200px no-repeat;
}
 
#menu_item2 a:hover {
  background: transparent url(../../images/test-1.jpg) -96px -200px no-repeat;
}
 
#menu_item3 a:hover {
  background: transparent url(../../images/test-1.jpg) -172px -200px no-repeat;
}
 
#menu_item4 a:hover {
  background: transparent url(../../images/test-1.jpg) -283px -200px no-repeat;
}

In the article they discuss the math that must be performed to make this work, but it’s hard to go from all the values presented to the logic that was used to arrive at those values. In addition, there are a lot of constants and repetition in the CSS. Not very DRY in my mind. Now here is the Sass that will produce the same CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
!menu_image = "url(../../images/test-1.jpg)"
!width_item_1 = 96
!width_item_2 = 76
!width_item_3 = 111
!width_item_4 = 118
!height_menu = 200
!unit = px
 
#menu
  :margin 2em 2em
  :position relative
  :width 400px
  :height = !height_menu + !unit
  :padding 0
  :background = !menu_image
  a
    :height = !height_menu + !unit
    :display block
  li
    :display block
    :position absolute
    :list-style none
    :height = !height_menu + !unit
    &#menu_item1
      :margin-left 0
      :width = !width_item_1 + !unit
      a:hover
        :background = "transparent " !menu_image "0 " -(!height_menu) + !unit no-repeat
    &#menu_item2
      :margin-left = !width_item_1 + !unit
      :width = !width_item_2 + !unit
      a:hover
        :background = "transparent " !menu_image " " -(!width_item_1) + !unit " " -(!height_menu) + !unit no-repeat
    &#menu_item3
      :margin-left = (!width_item_1 + !width_item_2) + !unit
      :width = !width_item_3 + !unit
      a:hover
        :background = "transparent " !menu_image " " -(!width_item_1 + !width_item_2) + !unit " " -(!height_menu) + !unit no-repeat
    &#menu_item4
      :margin-left = (!width_item_1 + !width_item_2 + !width_item_3) + !unit
      :width = !width_item_4 + !unit
      a:hover
        :background = "transparent " !menu_image " " -(!width_item_1 + !width_item_2 + !width_item_3) + !unit " " -(!height_menu) + !unit no-repeat

The top of the Sass file defines the constants that will be used for all the calculations in the CSS. This may seem like a little extra work, but it has a few advantages. First, this makes it simpler to calculate the position of the background image for the :hover and it allows the developer to quickly change the CSS if the image changes. For example, imagine that the image changed to this:


test-2
Not only did the dimensions for each item change, but the overall height changed from 200 to 50. If CSS was written directly, not only would the url to the new image need to be changed, but the positions would have to be recalculated and the height and negative y-position would need to change. Using Sass, we can just change the constants defined at the top of the Sass file:

1
2
3
4
5
6
7
!menu_image = "url(../../images/test-2.png)"
!width_item_1 = 75
!width_item_2 = 100
!width_item_3 = 50
!width_item_4 = 175
!height_menu = 50
!unit = px

I like the simplicity and flexibility that Sass enables when defining CSS. When I learned about CSS, the selling point was to allow the design of a site to be flexible, but having to statically define colors, positions, heights and widths for items like the one discussed here, make maintenance more complex and error prone. Imagine the potential for errors when the image was changed above. Finally, I believe maintenance and readability increases as it is simple to follow how the values are arrived at instead of simply stating the value.
 
Sass has a lot more to offer than what I present above. If you would like to learn more check out the HAML/Sass site.