GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Description: DRY Up Your CSS
Homepage: http://blog.airbladesoftware.com/2006/12/11/cssdryer-dry-up-your-css
Clone URL: git://github.com/dwalters/css-dryer.git
name age message
file .autotest Thu Jul 26 01:36:30 -0700 2007 Makes Autotest pick up test files. [deploy]
file CHANGELOG Fri Aug 15 02:45:23 -0700 2008 The self_clear helper accepts a :clear option. [deploy]
file MIT-LICENCE Wed Dec 20 02:25:52 -0800 2006 Adding css_dryer for first time [deploy]
file README Fri Aug 22 00:49:13 -0700 2008 Added to do. [deploy]
file Rakefile Thu Mar 20 11:24:57 -0700 2008 Added a generator. [deploy]
directory generators/ Fri Aug 15 02:45:23 -0700 2008 The self_clear helper accepts a :clear option. [deploy]
file init.rb Sat Sep 27 08:31:36 -0700 2008 Remove the treetop parser (didn't mean to push ... [dwalters]
file install.rb Thu Mar 20 11:42:46 -0700 2008 Prints installation section of readme. [deploy]
directory lib/ Tue Sep 30 07:51:23 -0700 2008 Update asset packaging to work with upstream ch... [dwalters]
directory tasks/ Wed Dec 20 02:25:52 -0800 2006 Adding css_dryer for first time [deploy]
directory test/ Wed May 14 02:06:24 -0700 2008 Version 0.2.6. See CHANGELOG. [deploy]
README
# CssDryer -- Eliminate Repetition In Your Stylesheets


## Introduction

Cascading style sheets (CSS) are wonderful but repetitive.  Rails strongly discourages repetition, the don't repeat 
yourself (DRY) principle, so writing CSS feels ungainly in Rails.

There are two sources of repetition in CSS:

* nested selectors
* lack of variables

Nested selectors lead to CSS like this:

  div           { font-family: Verdana; }
  div#content   { background-color: green; }
  div#content p { color: red; }

Note the triple repetition of <code>div</code> and the double repetition of <code>#content</code>.

The lack of variables leads to CSS like this:

  .sidebar { border: 1px solid #fefefe; }
  .content { color: #fefefe; }

Note the repeated colour <code>#fefefe</code>.

The CssDryer eliminates both of these sources allowing you to write DRY, pleasing stylesheets.  The examples above 
become:

  <% dark_grey = '#fefefe' %>

  div {
    font-family: Verdana;
    #content {
      background-color: green;
      p { color: red; }
    }
  }

  .sidebar { border: 1 px solid <%= dark_grey %>; }
  .content { color: <%= dark_grey %>; }

Note, though, that <code>@media</code> blocks are preserved.  For example:

  @media screen, projection {
    div {font-size:100%;}
  }

is left unchanged.

The original whitespace is preserved as much as possible.

See CssDryer for more details.


## Which Selectors Are Supported?

The CssDryer handles nested descendant, child, adjacent, class, pseudo-class, attribute and id selectors.

Multiple comma separated selectors are now supported.


## Comments

Comments on nested selectors do not get 'flattened' or de-nested with their selector.  For example comment B will be 
left inside the html selector below:

Before:

  /* Comment A */
  html {
    /* Comment B */
    p {
      color: blue;
    }
  }

After:

  /* Comment A */
  html {
    /* Comment B */
  }
  html p {
      color: blue;
    }
  }

This is suboptimal but I hope not too inconvenient.

Please also note that commas in comments will sometimes be replaced with a space.  This is due to a shameful hack in the 
code that handles comma-separated selectors.


## Partials

You may use partial nested stylesheets as you would with normal templates.  For example, assuming your controller(s) set 
the @user variable and a User has a background colour (red):

app/views/stylesheets/site.ncss:

  body {
    color: blue;
    <%= render :partial => 'content', :locals => {:background => @user.background} %>
  }

app/views/stylesheets/_content.ncss:

  div#content {
    background: <%= background %>;
    margin: 10px;
  }

And all this would render to site.css:

  body {
    color: blue;
  }
  body div#content {
    background: red;
    margin: 10px;
  }


## Remember the helper

Browser hacks are an ugly necessity in any non-trivial stylesheet.  They clutter up your stylesheet without actually 
adding anything.  They make you sad.

So encapsulate them in the StylesheetsHelper instead.  Separate your lovely CSS from the decidely unlovely hacks.  For 
example:

app/views/stylesheets/site.ncss:

  <% ie7 do %>
    #sidebar {
      padding: 4px;
    }
  <% end %>

This renders to site.css:

  *+html #sidebar {
    padding: 4px;
  }

In this example the hacky selector, "*+html", isn't too bad.  However some hacks are pretty long-winded, and soon you'll 
thank yourself for moving them out of your nested stylesheet.

You don't have to limit yourself to browser hacks.  Consider self-clearing: to make an element clear itself requires 13 
lines of CSS, in 3 selector blocks, by my count.  To make a second element clear itself, you need to add the element's 
selector to each of those three blocks.  It's fiddly.  And your stylesheet gets harder and harder to understand.

We can do better:

app/views/stylesheets/site.ncss:

  <%= self_clear 'div.foo', 'div.bar', 'baz' %>

Self-clear as many elements as you like in one easy line.


## Installation

Pre-requisite: Rails 2.1 (use v0.2.6 for Rails 2.0).

First, install in the usual Rails way.  From your application's directory:

  $ script/plugin install http://opensource.airbladesoftware.com/trunk/plugins/css_dryer

Second, generate the stylesheets controller and helper, and a test nested stylesheet:

  $ script/generate css_dryer

Third, add a named route to your config/routes.rb:

  map.stylesheets 'stylesheets/:action.:format', :controller => 'stylesheets'

Verify that everything is working by visiting this URL:

  http://0.0.0.0:3000/stylesheets/test.css

You should see this output:

  body {
    color: blue;
  }
  body p {
    color: red;
  }
    

## Usage

You put your stylesheets, DRY or otherwise, in <code>app/views/stylesheets/</code>.  Once rendered they will be cached 
in <code>public/stylesheets/</code>.

DRY stylesheet files should have a <code>ncss</code> extension -- think 'n' for nested.  For example, 
<code>site.ncss</code>.

Get them in your views with a <code>css</code> extension like this:

    <link href='/stylesheets/site.css' rel='Stylesheet' type='text/css'>

or with Rails' <code>stylesheet_link_tag</code> helper:

    <%= stylesheet_link_tag 'site' %>


## To Do

* Make CssDryer work with Rails' asset packaging: incorporate Dan Walters' code on GitHub.
* Rake task to generate and write .css files from .ncss ones.
* Use .css.ncss naming convention.
* Package as a gem as well as a plugin.
* Configuration, e.g. #implicit_nested_divs = true
* Replace regexp-based nested-stylesheet parser with a Treetop parser.
* Merb compatibility.
* Split out a separate EXAMPLES document.


## Alternatives

* [RCSS][1]: ERB, server-side constants, server-side classes and command line execution.  No nesting as such, though 
server-side classes offer a form of inheritance.

* [DCSS][2] (written up [here][3]): server-side constants, different syntax.  Descendant selectors only.

* [Styleaby][4] creates CSS with Ruby syntax.  "An experimental, unauthorized mashup of Scott Barron's stillborn 
Builder::CSS templates and Why The Lucky Stiff's Markaby templates."

* [Dirt Simple .rcss Templates][5] by Josh Susser.  No nesting, just variables.

[1]: http://rubyforge.org/projects/rcss
[2]: http://rubyforge.org/projects/dcss
[3]: http://myles.id.au/2006/11/20/introducing-dcss/
[4]: http://topfunky.net/svn/plugins/styleaby/README
[5]: http://blog.hasmanythrough.com/2006/3/23/dirt-simple-rcss-templates


## Credits

The idea came from John Nunemaker on [Rails Tips][6].  John beta-tested the code, provided a test case for @media blocks 
and suggested the controller's body.  Thanks John!

The caching code is based on [Topfunky's][7].

Changing the controller's name to <code>stylesheets</code>, thus allowing one to use Rails' 
<code>stylesheet_link_tag</code> helper, occurred to me while reading Josh Susser's [Dirt Simple .rcss Templates][5].  
Once I noticed it, I realised everybody was using a <code>StylesheetsController</code>.  Doh!

[6]: http://railstips.org/2006/12/7/styleaby-css-plugin/
[7]: http://topfunky.net/svn/plugins/styleaby/lib/stylesheets_controller.rb


## Author

[Andrew Stewart][8], [AirBlade Software][9].

[8]: mailto:boss@airbladesoftware.com
[9]: http://airbladesoftware.com


## Licence

CssDryer is available under the MIT licence.  See MIT-LICENCE for the details.

Copyright (c) 2006-2008 Andrew Stewart