Dynamic CSS 27 comments

posted Saturday, September 29, 2007 by topfunky

Presented at RailsConf 2007 in Berlin

Rails provides many built-in mime types that you can generate in your application. Unfortunately, CSS was forgotten!

Default mime types in Rails 1.2
Figure A Default mime types in Rails 1.2

Why generate CSS dynamically?

Why would you want to generate CSS dynamically? As a programmer, it annoys me to repeat the same hex values all over my CSS documents. Even worse, I have to make notes to remind myself what colors I’m using.

Many values are repeated in CSS files.
Figure B Many values are repeated in CSS files.

Not only colors, but HTML tags themselves are repeated inside CSS documents when I need to specify a style for a child element. For example, if I want to set a style for a link inside a list item, I have to repeatedly mention the li tag.

li {
  color: blue;
}  
li a {
  color: green;
}
li a:hover {
  color: yellow;
}

Generating CSS dynamically also gives you the ability to make your web applications smarter. For example, here are two columns. Normally, your Rails application would have no way to find out how wide they are. But if you store the column widths in a database table and generate the CSS, then other parts of your application can query the database for that information.

Your application can be smarter if it knows even just a few things about your layout.
Figure C Your application can be smarter if it knows even just a few things about your layout.

I worked on a site where we used this technique. PNN gives you the option of dragging a photo between columns of a layout. When you do, the photo is dynamically resized to fit the column. You could do this by squishing the image with CSS, but instead, we sent back a photo that was perfectly sized for the column.

Smart Columns

→ Short PNN Columns Screencast

But I don’t do CSS! My designer does!

CSS controls many aspects of a page’s appearance. You may want to generate only a part of the CSS, while leaving the rest to a designer. In the example above, we generated a small snippet of CSS to control the layout. The rest was accomplished with static CSS.

Making even a few elements of your CSS dynamic can open up many possibilities.
Figure D Making even a few elements of your CSS dynamic can open up many possibilities.

How? ERb

There are several plugins for working with CSS in Rails. The easiest way is to just use the ERb templates that are built into Rails already.

→ Short CSS with ERb Screencast

The benefits of this approach are:

  • Database-driven
  • Familiar
  • No plugins needed

How? Sass

I use the HAML plugin for my templates and it comes with the Sass engine for generating CSS. It’s much more powerful than ERb because it was built specifically for CSS. Fortunately, most of the elements mirror standard CSS syntax, so it’s easy to learn.

SASS syntax matches the CSS you already know.
Figure E SASS syntax matches the CSS you already know.

Sass works outside of the normal Rails controller system. Put your .sass files into public/stylesheets/sass and they will be converted to CSS when your application starts up.

In production, SASS generates static CSS files once, so there's no performance hit.
Figure F In production, SASS generates static CSS files once, so there’s no performance hit.

Sass also solves the nesting problem. Just indent a declaration and it will generate the appropriate CSS for nested items (inside the menu element here).

The best thing about it is…variables! You can define a color once and reuse it. Variables start with an exclamation mark, and you can reuse variables by putting an equals sign in the attribute declaration.

Colors and measurements can be specified once and reused.
Figure G Colors and measurements can be specified once and reused.

And you can do math! Add or subtract colors to generate darker or lighter shades.

Math with colors and measurements is possible.
Figure H Math with colors and measurements is possible.

There are many other features in Sass. You can split your code into separate files and they will be combined into a single document for production. You can even put variable declarations in one file and reuse those variables in other files.

SASS syntax makes it easy to split variables and directives into separate files for organization.
Figure I SASS syntax makes it easy to split variables and directives into separate files for organization.

This very blog is using Sass with Merb. I wrote a Stylesheet controller that looks something like this:

London, Tuesday, October 2, 6:30pm

In other news, I’ll be in London this next week, attending the Future of Web Apps Conference.

I’m also going to be at the George Pub on Tuesday night, handing out PeepCode t-shirts! Several dozen Rubyists have already replied and will be there, too.

http://lrug.org/nights/2007/09/26/episode-4-deadly-vision/

I’ve also published the third part of the RSpec Screencast at PeepCode. The next is one on the git SCM and was edited by Git maintainer Junio C Hamano. I hope to publish it shortly after I return from London (week of October 10).

27 comments

Leave a response

  • I’m also a Rubist that will be there. But … where the George Pub?

  • Gravatar icon jason

    Cool yay!

    So I’m now fooling around with SASS, but I have two questions…

    1/ Is the CSS file meant to get updated automatically when the source SASS file is changed? Maybe I’ve set it up wrong.

    2/ Is there a textmate SASS bundle? I’ve looked but no luck so far from Mr.Google.

    thanks for the tip!

  • Gravatar icon jason

    Just to answer one of my own questions…

    Putting this in env will always check for updates when developing:

    Sass::Plugin.options[:always_update] = true

  • Gravatar icon jason

    Well that isn’t working as well as I’d hoped

    But I see someone else has a similar issue ;)

    http://groups.google.com/group/haml/browse_thread/thread/9ecb5117ce05d599/d959c19972745936?lnk=gst&q=always_update&rnum=1#d959c19972745936

  • Gravatar icon Jordan Glasner

    Great rundown. I’ve been using haml and sass for a few months now, and it’s the perfect blend of simplicity and power.

    Jason, there is a Textmate bundle for haml and sass, a quick G search turned up:

    http://bjhess.com/bjhessblog/2007/05/31/installing-haml-bundle-for-textmate/

  • Gravatar icon topfunky

    @Francesc: Directions to the pub are here: http://fancyapint.com/pubs/pub46.html

    @jason: Yes, Sass doesn’t reload unless you reload a controller. I use my on controller in Merb, which solves this problem, but could be done in Rails, too.

    @Jordan: I was using an older bundle that I found somewhere. I’ve added a few snippets which I hope to submit back to the bundle maintainer.

    The nice thing is that both Haml and Sass are so concise that you really don’t need any snippets.

  • Can’t wait for the Git screencast :)

  • Gravatar icon Garth

    I’m really getting into HAML now after playing with merb. Great timing on the article, I’m currently learning the ins and outs of CSS.

  • Similar thing for Django

    http://www.djangosnippets.org/snippets/432/

  • Gravatar icon Masud

    Are your screenshots of code from Xcode?

  • Gravatar icon Crescent Fresh

    This is a bad idea:

    • more DSLs involved. More to remember, more to teach other developers of the codebase
    • maintaining your stylesheets happens far away from your stylesheets (in the db)
    • remembering colors and/or css selectors is never the hardest part of an application’s development. Yes it may become cumbersome during development but developing an infrastructure and framework for generating css is way overkill. Inline comments are WAY more useful.
    • Designing a cross-browser interface to a web app using css IS hard; this helps none of that
    • wastes more time than it saves

  • @Crescent

    • I don’t know where this idea that multiple languages is a bad idea. I mean, I love using Ruby in as many places as possible but its a terrible idea to think that learning a new language that solves a problem better than another language is a bad thing. Especially when they are DSLs and can be learned in 5 minutes. I’ve taught Sass to many designers who were extremely thrilled afterwards. When people first heard about Ruby many of the reactions were “not ANOTHER language to learn?!”. When Rails was launched “not ANOTHER framework to learn?!?”. Personally, I just chalk it up to laziness and a fear of learning something new. The best developers I know learn new languages because they find it fun... not a chore.
    • Did you read this post? The database is not involved at all. Its entirely file driven and in your public/stylesheets folder.
    • Its not just the 15 colors. You can also do math with the constants to help with layouts. And, you can keep string constants. For instance, we have a project where we are doing english and french and have images for each language. We have 15 Sass stylesheets that represent each controller. Whenever a language-specific asset (e.g. ”/images/en/logo.gif”) is called we make sure the language constant is included… url(”/images/” + !lang + ”/logo.gif”). Now, the magic is that those 15 files are all compiled into english.css and french.css with compression turned on. Our designers can work with organized sass files that work for both languages… and they are all compiled into language-specific files that are compressed and single-downloads.
    • When combined with a browser_selector.js solution, the automatic scoping makes things really easy. See below
      #main
        ul
          :width 30px
          .ie6 &
            :padding 0px
    

    The last line there will output ”.ie6 #main ul { padding: 0px; }” For the designer, we’ve brought our hacks right next to where they are defined for the general purpose styling.It certainly doesn’t solve the problem, but it at least helps us cope with the complexity.

    • Finally, I can only speak from our experience… and at Unspace Interactive, we’ve found a huge increase in our CSS developers’ efficiency and the maintainability of their code.

  • Good post! I needed to generate dynamic CSS once, and I though I was going crazy!!! :D Now, it’s good to know this can be a good practice!

    Felipe Giotto

  • Gravatar icon Crescent Fresh

    Ruby is a general purpose language. Sass is a DSL. My comments were directly mainly at it.

    css is also a DSL. The problem domain css tries to address is separating presentation from document structure. css being a DSL, by definition, has omissions, irregularities, imperfections and maybe even some oversights (like variables for example) compared to a general purpose language. You know what? Layering Yet-another-DSL on top of it doesn’t help in the long run. You’re at risk of abstracting away a DSL with another DSL.

    I guess I’m realizing that maybe my issue isn’t with dynamic css per say; Sass (and dry-css) are elegant and look dead-easy to learn (and remember 6 months down the road!). It’s more that I don’t think the problem of repeating hex values and element selectors in my css is large enough of an issue to warrant the architecture of another DSL inside rails. There MUST be bigger fish to fry elsewhere in my application.

  • Sass++, I don’t know how many times I gave up with some css layout and handed it over to a real designer because I got frustrated changing umpteen values to try a different center column width or something like that.

    We definitely have bigger fish to fry on our app, but Haml & Sass are small tasty fish and frying them first leaves more time for the big stuff as we’re not writing any more html/css than we need too.

  • looking forward to the event, can’t wait!

    John.

  • You should really get on Edge Rails :-)

  • Gravatar icon topfunky

    Awesome! I didn’t know that was in the trunk.

  • Gravatar icon xin

    this page is completely broken in ie6

  • Really helpful stuff… Thanks!

  • Gravatar icon jonuts

    @xin

    uh every page is completely broken in ie6. use a real browser.

  • Gravatar icon Tom Potter

    I can only find a bundle for HAML, is there one for SASS also?

  • Gravatar icon R. Daneel

    “every page is completely broken in ie6. use a real browser.”

    Dude, IE6 hasn’t been a “real” browser for YEARS.

  • Gravatar icon RoR

    Cool Tip. Thanks for the information

  • Gravatar icon Datta

    Hi. I`m looking this font:

    http://nubyonrails.com/system/images/Berlin.035-001.png

    Can someone tell name, link ?

  • Gravatar icon Dtr

    Hi. I`m looking this font:

    http://nubyonrails.com/system/images/Berlin.035-001.png

    Can someone tell name, link ?

  • Lovely fonts, thanks for info :D

Your Comment

Nuby on Rails

Geoffrey Grosenbach / Ruby / Code / Graphics / Design / Rails / Merb / Javascript / CSS

Ads by The Lounge

Manufactured with

Subscribe

Subscribe (RSS)