167

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!

Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
talon55
  • 2,023
  • 3
  • 15
  • 10

3 Answers3

223

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');
fny
  • 31,255
  • 16
  • 96
  • 127
gcastro
  • 6,266
  • 3
  • 18
  • 14
  • 1
    While this is a nice organization of the files, I believe it still solves the essential problem in the same way the question itself does. – semperos Sep 08 '11 at 11:49
  • @semperos, you're correct that the shape of the solution is essentially the same but my proposal still allows us to use the asset pipeline for the entirety of the stylesheets. I'm not sure if there's another way to selectively include part of the styles unless it's on a separate stylesheet. At least this way we compile to only a handful of CSS files. – gcastro Sep 08 '11 at 17:16
  • @gcastro, this helped me a lot, thanks. It was exactly the technique I needed to have separate layouts with their own assets but still get everything precompiled. – Keith Schacht Dec 18 '11 at 08:36
  • @gcastro, this is a much cleaner way to deal with stylesheets... thanks for the tip :) – Orlando Dec 25 '11 at 03:11
  • 5
    Something like this in the Rails Asset Pipeline guide would be good actually. thanks – Bashar Abdullah Apr 02 '12 at 19:23
  • This is actually really cool! I didn't know that the pipeline would work this way. Sorry for the late reply =) – talon55 Apr 08 '12 at 22:12
  • 3
    There is a gotcha though: if you follow this structure and use simple `.css` files then all your images will be broken. E.g. `background: url('image.png')` will be translated to the path `/assets/all/image.png` (mind the **all** in the path). Instead this works: `background: url('/assets/image.png)`. If there is an easier solution to this please post it. Other than using SASS which has helpers methods that probably resolve the path correctly. – Max Jun 25 '12 at 17:06
  • Good point @Max! I've updated the answer to include the 3 alternatives. – gcastro Jun 28 '12 at 18:36
  • I use same scheme for coffeescript. Should i include manifest js files in `config.assets.precompile`? – ExiRe Nov 21 '12 at 09:29
  • 1
    @ExiRe, yes. Any stylesheets or JS files/manifests that don't follow the standard pattern need to be added to the precompile array (see: http://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets) – gcastro Dec 06 '12 at 05:00
  • I think it's a bit misleading to tell people they have to re-organize their files. you don't have to require a tree, you can just keep your existing folder structure and explicitly include files in your new manifest. that way, you're taking advantage of the pipeline AND you don't break all your image paths, etc. – Ringo Mar 01 '13 at 00:54
  • Fair enough but then you have to remember to include new files. Speaking from experience, it's easy to forget. Plus, you don't break all the image paths, you just have to use the helper. It's a tradeoff. – gcastro Mar 01 '13 at 16:51
  • To simplify this a bit, you can put a manifest file in each sub-directory and use this `Dir.glob(Rails.root.join('app/assets/*/*/manifest.*').to_s).map{|p| File.join(p.split(File::SEPARATOR)[-2..-1])}` to precompile all manifests. – spyle Jul 15 '13 at 15:47
  • I just want to add that a missing backslash at the end of the manifest breaks the precompilation. I wasted over half an hour on this until I spotted it. The last two characters of the manifest should be */ . – MSC Jul 03 '16 at 00:26
10

Came across this problem today.

Ended up putting all IE specific stylesheets into lib/assets/stylesheets and creating one manifest file per version of IE. Then in application.rb add them to the list of things to precompile :

config.assets.precompile += ["ie9.css", "ie7.css", "ie8.css", "ie.css"]

And in your layouts, conditionally include those manifest files and you're good to go!

Anthony Alberto
  • 10,325
  • 3
  • 34
  • 38
2

Thats a pretty neat way to do it. I use conditional classes on html or modernizr. See this article for a good representation on what does what. modernizr-vs-conditional-classes-on-html

mrmonroe
  • 325
  • 1
  • 10