Sass, Compass, and Assetic in 10 minutes

CSS are boring for most of the developers. They don't like it. They prefer development, because they hate supporting Internet Explorer, they find no pleasure in writing CSS and they are right : CSS is boring. Writing CSS means most of the time repeating code, supporting browsers and creating static code.

There are also the frontend required optimizations, who prevent the developer from generating beautiful and DRY CSS : sprite generation, put all CSS in one file, compress assets, and so on.

Most of the time, they prefer to have a "ready-to-use" HTML production. This approach is not the most optimal, because you need to adapt CSS, to add new rules, to refactor some parts. CSS are part of the application, as the PHP, as the HTML. When a separation like this occurs, lot of time is spent fixing CSS issues, and going through the CSS sounds like a headache.

Once upon a time, some people created some concepts for the development : Don't Repeat Yourself, Keep It Simple Stupid. Those concepts looks like forgotten when developers create CSS.

Let's see how to bring them back to the scene, let's power the CSS production.

1. Small files, please

First, whatever the context is, when you produce some code : create small and separated files.

For example, the CSS on this website, the files are separated by purpose, as isolated as possible :

  • reset.css
  • main.css
  • banner.css
  • header.css
  • footer.css
  • etc.

When I search something, I know the file to open, The feature is isolated. The refactoring of a feature would be fast, easy and isolated enough.

2. Sass : it's f#~king awesome

Sass is an extension of CSS adding new features to the language : mixins, variables, inheritance and more.

A Sass file looks like this :

@mixin message-color($color, $bg)
    color:            $color
    border:           1px solid $color
    background-color: $bg

#messages

    .error, .success
        margin:           1em
        padding:          1em

    .error
        @include message-color(red, #ffaaaa)

    .success
        @include message-color(green, #aaffaa)

Sass allows to produce meaningful stylesheet code. Here we see different concepts in Sass. But to be fully impressed by Sass, you should visit the website and see examples.

The main idea is avoid repeating or computing values. Each time you are about to repeat something (a selector, a CSS ruleset), there is a method for it in Sass.

For example, to avoid computing :

$headerHeight: 50px
$menuHeight:   20px

#header
    position: relative
    height:   $headerHeight
#menu
    position: absolute
    top:      $headerHeight - $menuHeight
    right:    0
    height:   $menuHeight

Here you can modify later the $menuHeight or $headerHeight, there is no need to modify 20 different lines, no need to search where the value is used, and no meaningless value in your CSS (height: 42px, what's this value ?).

Another useful command, when you already have existing CSS is sass-convert. This command will convert to CSS to Sass files :

sass-convert my-file.css my-file.sass

This command has no magic inside, and cannot guess computed values, but it's a good start for Sass.

I already had many small CSS files for this website, converting them to Sass was a 30-seconds task.

3. Compass, a framework over Sass

Sass provides the language. Compass provides the tools ! Compass is a CSS authoring framework. It provides you helpers and utilities like image-width, and sprite-* methods. A reference guide is available with every methods.

A quick-example of features available with it :

@include "compass/reset" // Reset CSS
$logoUrl: "/logo.png"
$bgColor: blue

#header
    color: $bgColor
    background-color: lighten($bgColor, 40%)
#logo
    width:      image-width($logoUrl)
    height:     image-height($logoUrl)
    background: url($logoUrl)
    span
        display: none

Sprite-generation also become very easy with Compass. Here's a small sample of sprite-generation used for the menu of this website :

$menu: sprite-map("sprites/menu-*.png");
#menu
    li
        background-image: sprite-url($menu)
    li.blog
        background-position: sprite-position($menu, "blog")
    li.cv
        background-position: sprite-position($menu, "cv")
    li.contact
        background-position: sprite-position($menu, "contact")

Awesome, heh ?

The sprite-image is generated automatically, so no need to refactor the CSS and the sprite-image when you modify an image.

4. Assetic : the magic key

Kris Wallsmith created an awesome tool for asset management : Assetic. For a brief introduction on "how it works" and "what's in it", please see his presentation on Symfony Live (slides here).

First of all, separate two things : Assetic and AsseticBundle.

Assetic is the library providing the filters, the asset manager, the formula loader, and so on. It comes with many pre-configured filters for Sass, Compass and others. It also provides the tools for combining assets.

AsseticBundle is the integration in Symfony2 providing iteration of templates to find formula, automatic generation of them in development, CLI tools, and so on.

Sample with Symfony2, Sass & Compass

Let's see how to create a Symfony2 project with some Sass files.

First, install Sass & Compass (cf Sass website and Compass install guide).

Then, enable filters in your application in app/config/config.yml. Here, I indicate to Compass where all my images are located. So when I say url('/images/logo.png'), he knows it means /my/project/web/images/logo.png.

parameters:
    # Assetic
    assetic.filter.compass.images_dir: %kernel.root_dir%/../web/images
    assetic.filter.compass.http_path:  /images

assetic:
    debug:          %kernel.debug%
    use_controller: false
    filters:
        sass:    ~
        compass: ~

Then, create a stylesheets.html.twig containing your assets definition :

{% stylesheets filters="compass"
    "@AlomMainBundle/Resources/assets/css/main.sass"
    "@AlomMainBundle/Resources/assets/css/header.sass"
    "@AlomMainBundle/Resources/assets/css/footer.sass"
%}
    <link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}

And I include it in my layout :

<!- ... -->
<head>
    {% include "::stylesheets.html.twig" %}
</head>

And that's all.

For the development environment, stylesheets are generated automatically. For the production environment, you need to generate assets before, by running :

./app/console assetic:dump --env=prod --no-debug

And that's all.

Conclusion

Mastering CSS is important for a project. Develop them the same way you develop the rest of the application is very important if you want to make the code evolve and want something reusable. This article shows you how to take benefit from modern technologies : Sass, Compass, Assetic, and Symfony2.

Even if you are not using Assetic and Symfony2, Sass & Compass are too awesome to be unused : you can use them for generating HTML templates by using Compass CLI tools.

29 November 2012 - Update

You may have problems when launching assetic command, getting exceptions. If it is so, I recommend you to complete the reading with this excellent article from Luis Cordova.

Paths should not be expressed using @AcmeDemoBundle/Resources/... but should be expressed with bundles/acmedemo/....

Palleas August 1, 2011

I see a lot of myself in this article, I used to be really bored by CSS (mostly because of the cross browser compatibility crap, the vendor-prefixed properties...). Compass is awesome, and it's nice to see it integrated in assectic.

Great article! :)

Darren Slatten September 13, 2011

Thank you for this excellent article! I have a couple of questions for you, if you don't mind. I am completely new to Symfony and Sass/Compass, so forgive me if these are silly questions.

1. In your config.yml file, what do the tildes (~) mean? For example, what does this mean:

sass: ~
compass: ~



2. Why did you use Twig to combine your .sass files instead of using Sass native functionality (e.g. partials)? In other words, can you put this in your layout template:

<head>
{% stylesheets '@AlomMainBundle/Resources/assets/css/main.sass' filter='compass' %}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
</head>

And then put this in main.sass:

@import "header";
@import "footer";



Thanks again. :)
--
Darren

Julien DIDIER October 3, 2011

For information, to use sprites, you must have Compass 0.11.5 ;-)

Julien DIDIER October 9, 2011

I have a strange behaviour with the sprites generator. To generate sprites files, I have to fill in http_path parameter with "/images". But assetic want to find the background-image in "/images/images" directory. So I have to fill in http_path with "/" to view images from the sprite in my browser.

cordoval November 10, 2011

when running php app/console assetic:dump i am getting this error on your alom project:

<code>
[RuntimeException]
/home/cordoval/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:247:in `to_specs': Could not find compass (>= 0) amongst [minitest-1.6.0, rake-0.8.7, rdoc-2.5.8] (Gem::LoadError)
from /home/cordoval/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:256:in `to_spec'
from /home/cordoval/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems.rb:1210:in `gem'
from /home/cordoval/.rvm/gems/ruby-1.9.2-p290/bin/compass:18:in `<main>'
</code>

Alexandre Salomé November 12, 2011

@cordoval: Here the problem is probably about compass installation. Before you try integrating it in Symfony2, you should make it work as standalone.

Joe November 15, 2011

I'm getting the following error: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:827:in `report_activate_error&#039;: Could not find RubyGem sass (&gt;= 0) (Gem::LoadError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:261...

I am able to run sass and compass from the command line, however?

Alexandre Salomé November 16, 2011

@Joe: Make sure the binary used in Assetic and the binary used in CLI are the same.

You can explicitly set the used binary by setting "bin" under "filter: compass:" in config.yml.

eBuildy December 21, 2011

Very nice article, clear & simple !

I am using Sf2, you must write "filter" instead of "filters"

Cheers

Guiyomh July 2, 2012

Hi Alexandre,

you say :
For the development environment, stylesheets are generated automatically.

But this don't work for me. How do i to do for generate automatically my stylesheets, on refresh my browser ?

PS: I use SF 2.0.14

chk August 12, 2012

A lot of thanks! Very good and userful post!

k0pernikus October 10, 2012

@Darren Slatten: As to why one should not use @import for your sass inclusion: There is still an open bug that hinders invalidation of the cache if those imported sass file changes. Meaning you would have to clear your cache everytime when making a change. If imported via twig, all changes are seen instantly. See https://github.com/kriswallsmith/assetic/issues/79 for details.

Luis cordova October 18, 2012

now that i come back to it, my question is how you set your sf2 project to generate sprites automatically, is it over compass? Please let me know thanks!

alex November 6, 2012

{% stylesheets filter="compass"

instead of:

{% stylesheets filters="compass"

cordoval November 9, 2012

rvm all do gem install 'sass'