Using Rails 3.1 assets pipeline to conditionally use certain css

talon55 picture talon55 · Aug 20, 2011 · Viewed 42.8k times · Source

I’m in the process of building my first solo Rails app using Rails 3.1.rc5. My problem is that I want to have my site render the various CSS files conditionally. I’m using Blueprint CSS and I’m trying to have sprockets/rails render screen.css most of the time, print.css only when printing, and ie.css only when the site is accessed from Internet Explorer.

Unfortunately, the default *= require_tree command in the application.css manifest includes everything in the assets/stylesheets directory and results in an unpleasant CSS jumble. My current workaround is a sort of brute-force method where I specify everything individually:

In application.css:

*= require_self
*= require home.css
...
*= require blueprint/screen.css

In my stylesheets partial (haml):

<!--[if lt IE 9]
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
![endif]-->
= stylesheet_link_tag "application"
= stylesheet_link_tag 'blueprint/print', media: 'print'
<!--[if lt IE8]]
= stylesheet_link_tag 'blueprint/ie'
![endif]-->
= javascript_include_tag "application"

This works but it’s not especially pretty. I’ve done a few hours of searching to even get this far but I’m hoping that there’s some easier way to do it that I’ve just missed. If I could even selectively render certain directories (without including subdirectories) it would make the whole process a lot less rigid.

Thanks!

Answer

gcastro picture gcastro · Sep 1, 2011

I've discovered a way to make it less rigid and future proof by still using the asset pipeline but having the stylesheets grouped. It's not much simpler than your solution, but this solution allows you to automatically add new stylesheets without having to re-edit the whole structure again.

What you want to do is use separate manifest files to break things up. First you have to re-organize your app/assets/stylesheets folder:

app/assets/stylesheets
+-- all
    +-- your_base_stylesheet.css
+-- print
    +-- blueprint
        +-- print.css
    +-- your_print_stylesheet.css
+-- ie
    +-- blueprint
        + ie.css
    +-- your_ie_hacks.css
+-- application-all.css
+-- application-print.css
+-- application-ie.css

Then you edit the three manifest files:

/**
 * application-all.css
 *
 *= require_self
 *= require_tree ./all
 */

/**
 * application-print.css
 *
 *= require_self
 *= require_tree ./print
 */

/**
 * application-ie.css
 *
 *= require_self
 *= require_tree ./ie
 */

Next you update your application layout file:

<%= stylesheet_link_tag "application-all", :media => "all" %>
<%= stylesheet_link_tag "application-print", :media => "print" %>

<!--[if lte IE 8]>
    <%= stylesheet_link_tag "application-ie", :media => "all" %>
<![endif]-->

Lastly, don't forget to include these new manifest files in your config/environments/production.rb:

config.assets.precompile += %w( application-all.css application-print.css application-ie.css )

Update:

As Max pointed out, if you follow this structure you have to be mindful of image references. You have a few choices:

  1. Move the images to follow the same pattern
    • Note that this only works if the images are not all shared
    • I expect this will be a non-starter for most since it complicates things too much
  2. Qualify the image path:
    • background: url('/assets/image.png');
  3. Use the SASS helper:
    • background: image-url('image.png');