Sorry this page looks weird. It was automatically migrated from my old blog, which had a different layout and different CSS.

css_dryer: DRY Up Your CSS

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.

So I am pleased to give you css_dryer, a plugin that converts DRY stylesheets into CSS.

You can always find the most up to date information in the README.

Nested Declarations

John Nunemaker brought this into the open recently over at RailsTips.org. He gave the following CSS declarations as an example of annoying repetition:

div#content    { /* some styles for div#content    ... */ }
div#content h2 { /* some styles for div#content h2 ... */ }
div#content a  { /* some styles for div#content a  ... */ }

And then these as his dreamy alternative:

div#content {
  /* some styles for div#content ... */
  h2 { /* some styles for div#content h2 ... */ }
  a  { /* some styles for div#content a  ... */ }
}

(Note to self: get syntax highlighting going on this blog.) Done!

I thought this was a terrific idea so I sat down and wrote the code to do it, using John’s example as a test case. It took a little longer than I thought it would, mainly due to the recursion. (I always fumble around with recursion until suddenly it falls into place — at which point it is so obvious that I wonder why I was being so thick.)

So you can now nest declarations as deep you like. The inline/multiline nature of each declaration will be preserved too. For example, the DRY stylesheet:

div {
  /* comment for div */
  color: green;   
  p {
    /* comment for div p */
    color: red;
    b { color: blue; 
  }
}

Becomes this CSS:

div {
  /* comment for div */
  color: green;
}    
div p {
  /* comment for div p */
  color: red;
}
div p b { color: blue; }

Any selector can be nested: descendant, child, adjacent, class, pseudo-class, attribute and id selectors are all handled.

Note also that @media blocks are preserved. So this, for example, is left untouched:

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

Nice.

Please be aware that nested, multiple, comma separated selectors are not handled correctly. Comma separated selectors, nested or otherwise, are now supported.

Variables

Another source of repetition is the lack of variables in CSS, leading to lots of search-and-replace shenanigans whenever you decide to change a value.

For example, websites often use the same colour in more than one place. If, say, the sidebar’s border is the same colour as the footer’s, CSS requires you to write:

#sidebar { border: 1px solid  #fefefe; }
#footer  { footer: 1px dashed #fefefe; }

Later on you might receive the traditional feedback on months of development work: “Make the border lighter.” So now you have to hunt through your CSS looking for all occurrences of #fefefe and replace them with a lighter shade. Of course any decent text editor makes this trivial, but it’s still annoying.

A variable would solve this neatly:

<% sleek_grey = '#fefefe' %>
#sidebar { border: 1px solid  <%= sleek_grey %> }
#footer  { footer: 1px dashed <%= sleek_grey %> }

Enter CssDryer

I took the code I wrote to convert DRY stylesheets into CSS à la Nunemaker, added in an ERB pre-processing step and structured it as a new templating system (c.f. rhtml). I then turned it into a plugin: css_dryer.

$ script/plugin install git://github.com/airblade/css_dryer.git

So, how does it all fit together?

First, generate a controller to serve up your DRY stylesheets:

$ script/generate controller stylesheets

Edit the controller to look like this (thanks John and Topfunky!):

class StylesheetsController < ApplicationController
  before_filter :set_headers
  after_filter  { |c| c.cache_page }
  session :off
  layout nil

  private
  def set_headers
    headers['Content-Type'] = 'text/css; charset=utf-8'
  end
end

Add this 1.2-style route to config/routes.rb:

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

You can then put your stylesheets in app/views/stylesheets/. They will be cached in public/stylesheets/.

The stylesheets themselves should have an ncss extension, e.g. site.ncss.

You can reference the stylesheet in your views like this:

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

or with the stylesheet_link_tag helper:

<%= stylesheet_link_tag 'site' %>

To Do

Here’s a simple list:

Anything else? Let me know what you think!

Comments

I think it’s awesome! I was hoping someone would make my dreams come true. That’s why I mentioned it. I’m downloading it now and trying it out.

John Nunemaker • 12 December 2006

Wow. If this works, you’re my new best friend. This looks very promising. Damnit, guess I have to add yet another blog to my feed reader, haha.

William H. Harle Jr. • 12 December 2006

Awesome. This is the way CSS should be written! You rule.

Olly • 13 December 2006

Hi Andrew! Great work, we’ve been wishing for this kind of functionality in CSS for a long time now!

I started converting the style-sheets to .dcss format, and it works almost perfectly. I found one problem though, and it occurs when working with classes. If I have one HTML element with multiple classes to it, when I try to group all of them in one code block, a space appears between the element name and the class name in the generated CSS. Example:

main-menu {

td {

color: #999;
cursor: default;

.hover{
  color: #444;
  cursor: pointer;
}

.pressed {
  background: url('/images/menu-active-bg.png');
  color: #444;
  cursor: default;

  .hover {
    background: url('/images/menu-active-bg.png');
    color: #444;
    cursor: pointer;
  }
}

} }

What this does for the <#main_menu td> is output a line looking like (for the second class)

        #main_menu td .pressed

when for it to work it should look like

        #main_menu td.pressed

, without the space before the class name.

Another nice thing would be to not output the empty definitions that occur when the nesting involves something like .class1 .class2 { /some_attr: some_value;/ } , without class1 having any properties of its own (I used classes just as an example, the same is true in other cases)

Thank you very much for this plug-in, we’re looking forward to any further developments :-)

Adi • 13 December 2006

I’m sorry about being lazy before and not formatting the comment, it’s really difficult to read now..

Adi • 13 December 2006

Hi Adi!

Thanks for the positive and useful feedback. I’ll certainly address the two problems you found and post an update here when the new code’s ready.

Andrew Stewart • 13 December 2006

very cool i’ve done variables for a while but the nestable thing is really cool thanks!

shaggy • 13 December 2006

Great work!

I always felt the need of such DRY thing for CSS and you just released it. Thanks a lot!

However, I see some features missing. Consider following example:

I have some links here and there on my page and I want them look like buttons. I am a fan of semantic markup, so instead of applying “button” class to all those links, I write a separate bunch of CSS rules to make each link look like button. In such case a technique like yours to DRY up CSS would be perfect. But your technique doesn’t allow writing nested rules that behave the way other than “include” (e.g. “E > F”, “E + F”, “E:pseudo-class”, etc.). With your plugin you can only write “E F” rules.

It would be nice if rules as described above would be possible in your plugin.

I guess, Adi just described it. I just tried to make it clear. +1 for this feature.

Maxim Kulkin • 14 December 2006

Maxim, you’re right: the plugin doesn’t handle adjacent, child or pseudo-class selectors at the moment. I’ll get on it and let you know when it does. Thanks for the feedback.

Andrew Stewart • 14 December 2006

RCSS DCSS

Any chance you could work to combine efforts here?

tim • 14 December 2006

Tim, it sounds sensible. On the other hand a little competition is no bad thing. I’ll consider it and perhaps get in touch with the authors of those others.

Andrew Stewart • 14 December 2006

Awesome, I can’t wait to try it out!

Jewls • 17 December 2006

Hi,

I’m the author of DCSS. Firstly let me say it’s nice to see other people tackling this issue. You obviously have a different take on how the syntax should look and that definitely warrants setting up a different project with a different codebase.

I have one small issue with your project: you’ve chosen the same extension (.dcss) and this may lead to some user confusion. I deliberately called my project DCSS so users would know it was distinct from RCSS and that the syntax was not compatible, however .rcss and .dcss files can peacefully exist side-by-side in the same rails project. This is not the case currently with css_dryer. Would you consider changing the extension to say .ncss (nested css)?

Also I noticed your code doesn’t handle multiple comma separated selectors, e.g.:

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

produces:

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

when it should produce:

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

… you should probably mention this in your documentation as comma separated selectors are part of the CSS spec.

Myles Byrne • 18 December 2006

Hi Miles, that’s a good point about the projects having the same extension. I didn’t mean to step on your toes (perhaps great minds think alike? ;-) and I’ll be glad to change the extension here to .ncss.

And you are spot on with multiple comma separated selectors. John Nunemaker also pointed that out to me the other day and I added it to the to-do section of the plugin’s README — but I will now also add it to the main documentation to make it clear early on. Thanks for the recommendation.

Andrew Stewart • 18 December 2006

This is awesome. I worked on a plugin for myself that solves some of the problems you are running into.

You can see the code here http://pastie.caboo.se/28347

Basically, if you include it in your environment.rb it dynamically creates a stylesheet controller and registers a template handler for extension .style (You could change it to .ncss to work with your example)

You then place css code in views/stylesheets which are cached after the first load. Since it uses rails caching it will not auto-cache in development mode which is a bonus when you are debugging.

The nice part about this code is you don’t have to create the controller and the caching is automatic. The only thing you have to do is create view/stylesheets/your_stylesheet.style.

probablyCorey • 18 December 2006

Dynamic CSS is the last big missing piece from Rails. I’m using my own Markaby-ish CSS generatior and I love being able to have more dynamic control over CSS.

I agree with Corey that caching should be the default, and you could do the controller with metaprogramming instead of a generator…it’s much cleaner that way.

topfunky • 18 December 2006

Corey, I like the way your mind works. That’s elegant code. As topfunky says, it’s the way it should be. I’ll rework mine to look more like yours, if that’s okay with you. Thanks for the contribution!

Andrew Stewart • 18 December 2006

Hey, I havn’t played with this yet (and i’m still learning rails) would it be easy to use this as a stand-alone for static stuff??

anyway good work again..

shaggy • 18 December 2006

Shaggy, do you mean converting nested stylesheets into CSS offline, on the command line? If so, you can if you write your own little script. Look at the test class for tips. You need to include CssDryer to mix in the process method. And away you go.

Andrew Stewart • 19 December 2006

The gray text is really hard to read on the black background of your site.

John Syrinek • 20 December 2006

John, I’m sorry about that. This grey-on-black is the default styling; I’ve had “improve blog’s appearance” on my to-do list for some time but haven’t got round to it yet. I hope to before too long….

Andrew Stewart • 21 December 2006

Hi Andrew! Thanks for this helpful plugin. I wrote a post in Italian on my Blog about Ruby on Rails regarding your plugin! How to install it, how to use it and the todo list! You can find it on Rails on the Road.

Thanks again

Matteo Alessani • 24 December 2006

Hi Matteo, mille grazie for translating this! I’m delighted to see the plugin in Italian — everybody knows the Italians lead the world in style, so I’m pleased you like it.

Andrew Stewart • 27 December 2006

Late to the party, but I’ll chime in… I like the nesting syntax, but I can live without it. Variables on the other hand, gotta have them. If you want to see how I handle dynamically generating CSS, I’ve had this article up for a good long while…

http://blog.hasmanythrough.com/2006/3/23/dirt-simple-rcss-templates

josh susser • 4 January 2007

Nice. But I notice that I can’t seem to put characters to the right of the opening brace. The whole line just disappears. I’m just wanting to put comments there.

#footer { /* nope */ 
}
#footer { <% # nope%> 
}

I guess that there is a Rexexp that is barfing on this. Good work though…

Rifraf • 4 January 2007

Josh, thanks for that link. I hadn’t seen your article before but it triggered some thoughts in my mind which I’ve credited you with in the README.

Rifraf, you’re right — that won’t work at the moment. I assume in the code that one only puts characters to the right of an opening brace if it’s an inline style, i.e. there’s also a closing brace on the same line. It’s a drawback of my home-made suboptimal parser. The quickest fix is to move the characters to the next line.

Andrew Stewart • 4 January 2007

Hi Andrew. I’m using the latest version, and so far it’s working great. But another thing just dawned on me.. Is there any way you can make a small app that does the exact opposite? That is takes the .css and condenses it to a .ncss format? because a lot of people, me included of course :D, have already got some good hundreds of lines of CSS in the standard ( or should I say wet ) format. Again, 10x for all the great work and god bless!

Adi • 5 January 2007

Hey Andrew, looks like some useful software!

I notice that you mention a “1.2RC1-only route” to add to routes.rb. Do I take that to mean that your plugin only works with Rails 1.2RC1 onwards? I’m having trouble getting your plugin to kick in and wondered if that might be the issue. If I browse to the address I’m expecting my rendered css file to be available at, I just get “Unknown action No action responded to site.css”. Cheers in advance.

Mark • 18 January 2007

Andrew, with the new release of 1.2 I’ve upgraded and answered my own question - the css file displays as expected now. Time to get stuck in!

Mark • 19 January 2007

Adi, that shouldn’t be an insurmountable problem. I’ll add it to the list but no guarantees about when I’ll get around to it!

Andrew Stewart • 25 January 2007

Hi, the plug in looks great. However, I’m not sure if it supports nested classes, or if it does I seem to be going about it wrong!

.panel { .label { … } }

becomes

.panel.label { … }

which doesn’t seem to work (at least in firefox). It should compile to this shouldn’t it (note the space):

.panel .label { … }

Robin Ward • 7 February 2007

Robin, it does support nested classes but not quite in the way you were trying.

This is how it works: if you give the element name in your nested selector, it will preserve a space when flattening the hierarchy. If you only give the class (or pseudo-class, ID, attribute, etc) name, it will remove the space.

I could have arranged it the other way round but more people seem to work this way than the other.

So your stylesheet should look something like this:

.panel {
  ...
  span.label { ... }
}

If you prefer the other way round, modify the def combo_key method around line 222 in lib/css_dryer.rb: in the ‘a ? b : c’ line, swap b and c.

Andrew Stewart • 12 February 2007

Thanks a lot, that approach worked a lot better and solved my problems.

Robin • 12 February 2007

Hi, I’m currently working on a project using rails 1.1.6 where upgrading to 1.2 is out of question. Unfortunatly css_dryer doesn’t support 1.1.6 any longer. Does any one know how to hack css_dryer to work with 1.1.6? Thanks, MyKey_

MyKey_ • 17 February 2007

Hi MyKey_, the easiest way is to get the pre-1.2 version out of Subversion.

Andrew Stewart • 19 February 2007

Oh you saved tons of my time. Thanks a lot.

Claus • 25 March 2007

Variables in CSS?! We did not know that in France ;)

Fabien • 31 March 2007

A very good and sophisticated article.

Justin • 6 May 2007

Hello,

This looks like a great plugin, but I am having some difficulties getting it to work. I have followed all the instructions and as far as I can tell everything is set up properly. My problem seems to be that the .css file is not being created. I am in development mode on a windows XP based system. Somebody mentioned that stylesheets shouldn’t cache on development mode, is this something you’ve implemented? Any ideas on what could be causing my problem?

supaspoida • 8 May 2007

Nevermind! Everytime I post to one of these I end up solving the problem two minutes later. It was a simple typo in the routes. Thanks for this!

supaspoida • 8 May 2007

Andrew,

I really like your ideas, however I have a few suggestions. As you’ve already pointed out, you can’t really handle:

.container {

...

}

.container .section1 {

...

}

.container.alternate {

...

}

And so on. you could use a div.section1 but that assumes that you know what element you are going to attach the classname to. Let me suggest that you do some special handling of *. The universal selector, in all but very specific cases doesn’t add anything to a selector. It has no weight when calculating specificity and it doesn’t exclude anything from your collection of candidate elements. The only time it is meaningful is if you have:

.container * .bar {

...

}

This makes for a useful approach to the problem. I would suggest that coding the example mark-up above should be written as:

.container {

...
*.section1 {
    ...
}
.alternate {
    ...
}
* .bar{
    ...
}

}

For those who really want to keep the * in the final selector, you can have a setting to called strip_extra_universal_selector or something similar.

There is another DRY situation that you haven’t considered. Often you are going to treat all the links of a particular type in the same way, although there might be minor variations depending on where its used. Or perhaps you are building a family of online products and you want to have consistent handling of certain elements. Take navigation links you might decide to always do something like:

a:link, a:visited {

display:block;
color: {{PrimaryColor}}
background-color: {{SecondaryColor}}
border: 3px solid {{PrimaryColor}}

} a:hover {

color: {{SecondaryColor}}
background-color: {{PrimaryColor}}
border: 3px solid {{SecondaryColor}}

}

It would be useful if you could define that somewhere separate and do something like:

.container {

...
StandardLinks :PrimaryColor=>"#f0f", :SecondaryColor=>"#ff0";
*.bar {
    ...
    StandardLinks :PrimaryColor=>"#f00", :SecondaryColor=>"#0f0";
}

}

which would generate:

.container { …
} .container a:link, .container a:visited {

display:block;
color: #f0f
background-color: #ff0
border: 3px solid #f0f

} .container a:hover {

color: #ff0
background-color: #f0f
border: 3px solid #ff0

} .container .bar {

...

} .container .bar a:link, .container .bar a:visited {

display:block;
color: #f00
background-color: #0f0
border: 3px solid #f00

} .container .bar a:hover {

color: #0f0
background-color: #f0f
border: 3px solid #0f0

}

It occurs to me that this is not unlike a helper or a partial (depending on how you want to look at it)

Adam • 23 May 2007

Andrew, this regular stylesheet crashes the plugin:

/*
 * multilined comment. This is fine.
 */
html{
/* Multi-lined comment in a selector.
 * this crashes css_dryer
 */
}

Additionally, the plugin incorrectly handles comments with blank lines:

/*
* comment
[blank newline]
*
*/

gets transformed to:

/*
* comment

(Not closing the comment). Please add these to your test cases. You could just strip all CSS comments as a preprocessing step, using a gsub and this regex: /\/\(.|[\n])?\*\//

but then you lose all the comments, which are sometimes needed even in the generated CSS, especially if I’m debugging some CSS in development mode.

Phil Crosby • 4 June 2007

Ack, as a side note, you should turn on replacing a newlines your blog’s comment box with some br tags or a paragraph tag.

Otherwise every sentence seems like it’s part of one huge paragraph, when that’s not what the user intended (like Adam’s post).

Phil Crosby • 4 June 2007

I’m not sure caching the file in /public/stylesheets is a good idea. If you ever change the original .ncss file and restart your production server, the new file will not get used. Rails will instead serve up the static .css file from /public/stylesheets, and will not hit the Stylesheets controller.

Phil Crosby • 7 June 2007

Phil, thanks for the useful feedback. I’ll take care of those broken comments.

You’re right too about newlines in the comments people kindly leave here. I’ll sort this out too.

On the caching front: yes, the cached stylesheets have to be expired manually. This will happen wherever the file is cached. You could always write some filter code to expire the cache based on timestamps.

Andy Stewart • 18 June 2007

Hi Andy, great work!

I’m using your plugin for two days, and just made a little addition I found useful sharing: in NcssHandler#render under:

# Create an anonymous object and get its binding
env = Object.new
bind = env.send :binding

I added:

env.extend(StylesheetsHelper)

so one can use the self-defined helper-methods within the .ncss templates, e.g.:

def method_missing(method, *args, &block)
  unless args.blank?
    m = method.to_s.dasherize.gsub(/^\\-/, '_')
    suffix = m.match(/[width][height]/) ? 'px' : ''
    "#{m}: #{args * ' '}#{suffix};"
  end
end

So I can do things like:

<%= border_width(100) %>
René • 6 July 2007

I’ve been playing with this plugin, and have an issue that I’m not sure is a bug or by design or just not considered. When I do multiple comma selected values I end up with css files filled with duplicate settings for each group of selectors rather than no modified output at all. While the .ncss file is dry, the resulting css files are not, which is bad for file sizes and debugging.

This is what I am trying to do, which I expect to yield a file with no changes:

body { 
\tbackground-color: #fff;
\tcolor: #333;
\t
}

p, ol, ul, td, {
\tfont-family: Georgia, arial, helvetica, sans-serif;
  font-size:1.1em;
  line-height:1.5em;
}

yields this:


Am I doing something wrong or is this syntax just not supported? Thanks!

supaspoida • 28 September 2007

Supaspoida, you’re not doing anything wrong — it’s the way css_dryer works. I found that the way I had implemented the parsing of nested stylesheets made handling nested, comma separated selectors difficult. So I took the simplest approach that generated valid CSS and that results in the outcome above.

I realise it’s suboptimal and I have in mind a way to rewrite the parsing to make it more robust; this would leave your example stylesheet unchanged. It’s on my to-do list and I hope to get to it within a month or two (lots of other deadlines looming in the meantime!).

Andy Stewart • 28 September 2007

Andy, thanks for the response. I love css dryer and will continue to use it on my personal projects, but don’t think I will be able to use it on any production projects until this gets worked out. I have many sets of multiple comma separated selectors, and with the way this works now that will increase the file size quite a bit.

If you wouldn’t mind dropping me an e-mail or even posting here, I’m interested to know what you were thinking to solve this? Maybe I could take a crack at it to hold me over till you implement your solution.

supaspoida • 1 October 2007

Supaspoida, I’d be delighted if you were to have a crack at it.

If you look at the code you’ll see that I process nested stylesheets line by line. I think rewriting this to process character by character would simplify the code to such an extent that I would then be able to see how to determine when comma separated selectors should be moved around and when they should be left alone (as well as making most other things easier).

There’s no intrinsic reason why the current implementation couldn’t be modified to do what you want. But it’s already hard enough to follow that I know I’d reach the solution quicker by first simplifying the code and then making any modifications still needed.

Andy Stewart • 1 October 2007

This looks very cool, definitely something I need to check out for my next ROR project. I have a feeling it’ll be a huge time saver and that the nesting will help me avoid some CSS “bugs.”

Eric • 12 November 2007

Hi,

I have interested of your work and where can i get the sample application using your plugin.

thanks, ravi.

Ravi • 18 December 2007

Ravi, there’s no sample application per se. You can install the plugin into your own application like this:

$ script/plugin install http://opensource.airbladesoftware.com/trunk/plugins/css_dryer
Andy Stewart • 18 December 2007

Hi my requirement is something like each user should be able to customize the style…does this plugin fit into this?

Venkat • 19 December 2007

Venkat, yes it does. The nested stylesheets are ERB templates in which you can use instance variables set in your action – just as with a normal .html.erb template.

So you could have a form where your users set colours, widths, etc; you store those values in the database and then set them in a before_filter in, say, your stylesheet controller. Your (nested) stylesheet can then use those values when generating the final stylesheet.

You can read some more about dynamic CSS here; see particularly the part about PNN.

Andy Stewart • 20 December 2007

Thanks a lot, that approach worked a lot better and solved my problems.

josh • 22 December 2007

Andy, do you have any plans for getting CSS Dryer to play nice with Rails 2.0 assets caching (combining stylesheets)?

Morgan Roderick • 26 December 2007

Morgan, I see what you’re getting at. However the nested stylesheets are templates that need rendering so it would only be sensible if the nested stylesheets didn’t use any variables.

Even then it’s not entirely trivial. The stylesheet_link_tag helper would have to render the ncss templates and (maybe) write them to disk before combining them with any other CSS into a single file.

Please feel free to send in a patch!

Andy Stewart • 4 January 2008

Howdy,

CSS_DRYER is a fantastic plugin, but you guys need a way of submitting bugs to your project (any chance of adding it to ruby forge?) I have noticed that updating to any of the 2.+ versions of CSS_DRYER gives me the following error in my logs:

ActionView::TemplateError (undefined method `erb_time_mode' for #<#:0x31a7414>) in app/views/stylesheets/screen.ncss:

    vendor/plugins/css_dryer/lib/css_dryer.rb:362:in `render'
    vendor/rails/actionpack/lib/action_view/base.rb:416:in `delegate_render'
    vendor/rails/actionpack/lib/action_view/base.rb:299:in `render_template'
    vendor/rails/actionpack/lib/action_view/base.rb:260:in `render_file'
    vendor/rails/actionpack/lib/action_controller/base.rb:806:in `render_file'
    vendor/rails/actionpack/lib/action_controller/base.rb:711:in `render_with_no_layout'
    vendor/rails/actionpack/lib/action_controller/layout.rb:256:in `render_without_benchmark'
    vendor/rails/actionpack/lib/action_controller/benchmarking.rb:50:in `render'
    /usr/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
    vendor/rails/actionpack/lib/action_controller/benchmarking.rb:50:in `render'
    vendor/rails/actionpack/lib/action_controller/base.rb:1101:in `perform_action_without_filters'
    vendor/rails/actionpack/lib/action_controller/filters.rb:632:in `call_filter'
    vendor/rails/actionpack/lib/action_controller/filters.rb:638:in `call_filter'
    vendor/rails/actionpack/lib/action_controller/filters.rb:438:in `call'
    vendor/rails/actionpack/lib/action_controller/filters.rb:637:in `call_filter'
    vendor/rails/actionpack/lib/action_controller/filters.rb:638:in `call_filter'
    vendor/rails/actionpack/lib/action_controller/filters.rb:449:in `call'
    vendor/rails/actionpack/lib/action_controller/filters.rb:637:in `call_filter'
    vendor/rails/actionpack/lib/action_controller/filters.rb:619:in `perform_action_without_benchmark'
    vendor/rails/actionpack/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
    /usr/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
    vendor/rails/actionpack/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
    vendor/rails/actionpack/lib/action_controller/rescue.rb:83:in `perform_action'
    vendor/rails/actionpack/lib/action_controller/base.rb:430:in `send'
    vendor/rails/actionpack/lib/action_controller/base.rb:430:in `process_without_filters'
    vendor/rails/actionpack/lib/action_controller/filters.rb:624:in `process_without_session_management_support'
    vendor/rails/actionpack/lib/action_controller/session_management.rb:114:in `process_without_test'
    vendor/rails/actionpack/lib/action_controller/test_process.rb:15:in `process'
    vendor/rails/actionpack/lib/action_controller/base.rb:330:in `process'
    vendor/rails/railties/lib/dispatcher.rb:41:in `dispatch'
    vendor/rails/actionpack/lib/action_controller/integration.rb:257:in `process'
    vendor/rails/actionpack/lib/action_controller/integration.rb:152:in `get'
    (irb):1:in `irb_binding'
    /usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'
    /usr/local/lib/ruby/1.8/irb/workspace.rb:52

What is unclear is where erb_time_mode is being defined…is it being defined in Rails 2.0, and is CSS_DRYER 2.+ only compatible with Rails 2.0?

Also, when I do a google search for erb_time_mode, the only page that comes up in Google is:

http://opensource.airbladesoftware.com/trunk/plugins/css_dryer/lib/css_dryer.rb

Any ideas? Thanks again for creating a better way of writing CSS!

Ryan Kuykendall • 8 January 2008

After taking a look at the ERB class, it looks like the following line in NcssHander#render:

      dry_css = ::ERB.new(template, nil, @view.erb_time_mode).result(@view.send(:binding))

…has a typo in it (it should be erb_trim_mode):

      dry_css = ::ERB.new(template, nil, @view.erb_trim_mode).result(@view.send(:binding))
Ryan Kuykendall • 8 January 2008

Hi Ryan, thanks for reminding me of the need for somewhere to report bugs. I’ll get css_dryer up on RubyForge in the not too distant future.

And thanks for the bug report with fix! I’ve corrected the code and released a new version (0.2.3).

Andy Stewart • 8 January 2008

Andy … I thought that writing them to disk first, would be the only real option. Unfortunately, my schedule doesn’t currently allow me to do much work outside direct client work.

But, I’ll keep it in mind, and will hopefully find the time to get my head around it.

My team is really enjoying css_dryer … it’s really hard to write “regular” css these days ;)

Morgan Roderick • 11 February 2008

I cant seem to get this to work for the life of me. I followed all the instructions and did everything just like u said. There is no typos cuz i copied and pasted everything. I’m using version 0.2.3 on rails2. I also tried to run in production environment… no luck

Mike • 28 February 2008

It seems that the install messed for some reason. I started a new project and everything works just fine.

Mike • 29 February 2008

Fantastic work Andy! I will be trying this out on my next project. I just started writing CSS in an erb template so I could use variables and this alone has made a huge difference. But this is sure to improve things even further. I’ll let you know how I get on with it.

James Crossett • 27 March 2008

I added in support for the rails 2.0 asset packaging (e.g., all.css & all.js). The modified version can be found on github here:

http://github.com/dwalters/css-dryer/

Dan Walters • 20 April 2008

Dan, excellent work. I see you also took care of Rails 2.1-compatibility.

I’ve been intending to move css_dryer from Subversion to git sooner or later. This is a useful push to get on with it. In the meantime I’ll merge your changes into the Subversion repository.

Andy Stewart • 22 April 2008

Dan, your patch doesn’t seem to work for me.

<%= stylesheet_link_tag ‘base’, :media => ‘screen’, :cache => ‘screen’ %> <%= stylesheet_link_tag ‘test’, :media => ‘screen’, :cache => true %> <%= stylesheet_link_tag ‘test2’, :media => ‘screen’, :cache => true %>

Adds 1 screen.css and 2 all.css to output, and the all.css only contains the content of the first file.

Have you experienced this?

Morgan Roderick • 27 April 2008

Morgan,

You need one stylesheet_link_tag per cached stylesheet - something more like this:

<%= stylesheet_link_tag 'base', 'test', 'test2', :media => 'screen', :cache => true %>
Dan Walters • 28 April 2008

Added this to my StylesheetsHelper:

  def ie6(&block)
    surround_with("* html", &block)
  end
  
  def ie7(&block)
    surround_with("*+html", &block)
  end
  
  def ie(&block)
    ie6(&block) + ie7(&block)
  end
  
  def surround_with(with, &block)
    concat """
    #{with} {
      #{capture(&block)}
    }
    """, block.binding
  end

so i con do:

<% ie6 do %>
  body {
    padding: 4px;
  }
<% end %>

and it generates valid css for ie6 only:

  * html body {
    padding: 4px;
  }

My NCSS templates look nice and my IE hacks are less of a … hack ^^

René • 10 May 2008

René, I like that a lot. Nice idea.

I’m going to work that into all my stylesheets immediately. Thanks!

Andy Stewart • 12 May 2008

I am sure this is already mentioned here. I am converting my existing css to ncss and I had a problem with nested classes. I changed line 222 of lib/css_dryer.rb to include a space between branch/leaf.

  def combo_key(branch, leaf)  #:nodoc:
    (leaf =~ /\\A[.:#\\[]/) ? "#{branch} #{leaf}" : "#{branch} #{leaf}"
  end

The above could have read

  def combo_key(branch, leaf)  #:nodoc:
"#{branch} #{leaf}"
  end

In my case all the css conversions are now working as expected. I am sure this is not the correct way, but I use it as a quick fix for nested classes like .class1 .class2 { … }.

Anyways a great plugin.

WN • 28 August 2008

Hi WN,

The idea behind that line is that you might want to nest pseudo-classes, or other kinds of selectors, attaching them to the parent selector like this:

a {
  :link, :visited {
    color: red;
  }
  :hover, :active {
    color: blue;
  }
}

I do see your point though, and in fact I’m planning to make this behaviour configurable.

In the meantime, another way to achieve what you want is to remove the full stop and hash characters from the regular expression.

Andy Stewart • 29 August 2008

Hi Andy,

Great plugin! I’ve just converted our biggest project to use it, and it’s made the CSS much more manageable.

I do, however, agree with others that the special treatment of nested selectors starting with [.:#\[] is confusing. Nesting should have a single meaning.

Another way to achieve the same thing would be to introduce some extra syntax for the case where you want to extend the parent selector. For example:

a {
  color: blue;
  +.standout { color: red; }
  +:hover { color: green; }
}
Pete Yandell • 9 September 2008

Hi Pete,

It’s good to hear your CSS is more manageable now!

Thanks for your thoughts about nested selectors. When you put it like that, I can’t help but agree that the special treatment is surprising. Given that’s the consensus, I’ll change the default behaviour and add a configuration option so people can revert it stylesheet-wide if they like.

I think the only way to revert the behaviour on a case by case basis would be to introduce some new syntax, as you suggest. I’ve made a point of not introducing any new syntax so far, but I think this would be worthwhile.

Andy Stewart • 10 September 2008

I don’t really see any need for special syntax for signaling that you’re extending the parent selector.

It already works fine. Indentation goes a long way :-)

Oh, and if you do decide to go with it, “+” has significance in css :-)

Morgan Roderick • 2 October 2008
It is nice to know about this  plugin, It is very useful
sureshV R • 12 January 2009

Hi,\n\nI am using Rails 2.3.2 and css_dryer 0.4.2. In development mode my stylesheets are processed\nbut in production mode I am getting an exception\nNo such file or directory ‘application.css’ in asset_tag_helper.rb: 656\n\nIn my layout I use\n

\n\n<%= stylesheet_link_tag 'application', 'ids','classes', :media => 'screen', :cache => true %>\n\n
\nI don’t know how to fix this. \n

WN • 22 July 2009

Hi WN,\n\nThe problem is that css_dryer isn’t yet compatible with the :cache => true option in the stylesheet_link_tag helper.\n\nThere’s a fork of css_dryer which does support it. I intend to roll this into my version of css_dryer but haven’t got round to it yet.\n\nThe best workaround in the meantime is to drop the :cache => true. The stylesheets will still be cached – they just won’t be delivered in a single bundle, but rather as separate resources.\n

Andy Stewart • 23 July 2009

Andrew Stewart • 11 December 2006 • RailsCSS
You can reach me by email or on Twitter.