20

This is a question that several people have asked before, but none of the questions were quite asked or answered in a way that I found helpful, so I'm writing the question and answer that I would have found helpful.


I have a Rails 3.1+ app using the asset pipeline. There's one specific action that I want to have use different CSS. (In my specific case, I have a page that is intended to be printed, so it truly needs completely different CSS and does not need any Javascript.) Currently, I have only one application-specific CSS file. How do I add the new CSS file and direct the asset pipeline to use my file?

For example, right now, my app/assets looks like

app/assets
    /javascript
        application.js
        custom.js.coffee
    /css
        application.css
        custom.css.scss

I want to add a print.css file that is used by the view of a specific action. This view will not use the application.css file. How do I add print.css?

Kevin
  • 14,655
  • 24
  • 74
  • 124

5 Answers5

30

I found this blog post to be very helpful: http://blog.seancarpenter.net/2012/11/05/page-specific-javascript-with-the-asset-pipeline/. My answer paraphrases what this blogger already wrote and fills in a few missing details.


First, it's important that you've read and understood the Rails Guide to the Asset Pipeline. Unfortunately, this guide doesn't clearly explain how to add action-specific assets, but it does cover some concepts you need to know. Made sure you understand these ideas:

  • That the asset pipeline compiles Javascript, CSS, and other assets so that Rails servers can cache assets for better performance.
  • That manifest files use commands like require, require_tree, and require_self to indicate which files are compiled together.
  • That in order for the asset pipeline to work properly in production, you need to manually run rake assets:precompile to produce the compiled, minified assets in the public directory.

These ideas are the minimum "need-to-know" pieces of information about the asset pipeline. If you don't already understand these ideas, you don't have an "expert or enthusiast" level of knowledge about the pipeline, and unfortunately, SO isn't the right place to learn this stuff. Fortunately, the the Rails Guide to the Asset Pipeline is a short 15-minute read and can get you up to speed quickly if you need it.


Second, these are the changes you need to make in order to ensure that the asset pipeline correctly sees and handles your new print.css file.

Follow these steps:

  1. Add your print.css file to app/assets/css.
  2. You'll need to create a manifest file that will show Rails where to find print.css. You need to do this, even though you only have a single CSS file you're adding. This is an easy step:
    • Add a file called print.js to app/assets/javascript.
    • Add this line to print.js:
//= require print

This will be the only line in the entire print.js file. If I understand correctly, Rails expects manifest files to have the file extension .js, which is why we aren't using print.css as the manifest file.

  1. We now need to instruct Rails to find and use the print.js manifest. Add the following line in your config/application.rb file:
config.assets.precompile += %w( print.js )
  1. We're almost finished! However, the already-present application.js manifest includes the line //= require_tree . which means that it will include your print.css file. This will cause your print.css styling to affect your entire site, not just the single view. There are two ways of dealing with this:
    • If application.js and print.js do not share any assets, you can use the stub command in your application.js to exclude the assets used in print.js. What this does is instruct application.js to remove any of the assets that print.js references from its own list of referenced files. Our modified application.js looks like:
(snip...)
require_tree .
stub print

See this answer for more information.

  • If your print.js and application.js files share some assets, you'll need to move all of the assets used by application.js into subdirectories. I didn't do this myself, so I'm not the most help in this area. Look at this answer for instructions.

Now we have included print.css in the asset pipeline. We now need to direct Rails to use print.css in your specific view.

Let's say your action is in the reports controller, and that the action is named print_reports. This means we have a reports_controller.rb file and a print_reports.html.erb (or .haml) file. We need to make several changes to these files.

  1. To start, add a new layout in app/views/layouts. Perhaps call it print.html.erb. We'll use this new layout for your print_reports.html.erb file. Set it up as you desire. For a page intended to be printed, this will likely be very simple, such as
<html>
    <head>
        <title="Print">
    </head>
    <body>
        <%= yield %>
    </body>
</html>

Using a separate layout the disadvantage that it's difficult to keep this layout and the layout used by the rest of the application in sync, but if you are using separate CSS files for the action, it's unlikely that you want the layout to be the same anyway.

  1. Add a stylesheet_link_tag in the layout's header pointing to your print.css:
<html>
    <head>
        <title="Print"/>
        <%= stylesheet_link_tag "print" %>
    </head>
    <body>
        <%= yield %>
    </body>
</html>
  1. In the controller, we'll tell Rails to use our new layout for the action. Add the line layout 'print', only: [:print_reports] to your controller:
class reports_controller < ApplicationController
    layout 'print', only: [:print_reports]

    #snip

See this question for more information and a few different approaches.

At this point, when you run the app, your print_reports action should be using print.css correctly!


Remember to run rake assets:precompile before deploying on the server.

Community
  • 1
  • 1
Kevin
  • 14,655
  • 24
  • 74
  • 124
  • 7
    You don't really need to add an extra JavaScript file. Just put `config.assets.precompile += %w( print.css )` in your config/application.rb and then precompile your assets as you would usually do. – Markus Herzog Dec 04 '15 at 16:13
5

Official Solution

It is documented in the official Rails Guides here: http://guides.rubyonrails.org/asset_pipeline.html#controller-specific-assets

Actually you can leave out the require_tree directive (located in application.css and application.js) then use this in your template:

For controller specific JavaScript:

<%= javascript_include_tag params[:controller] %>

For controller specific CSS:

<%= stylesheet_link_tag params[:controller] %>
Community
  • 1
  • 1
Bijan
  • 25,559
  • 8
  • 79
  • 71
3

All of you are putting very complicated answers.

1 Go to app/assets/stylesheets
2.Make a file with the extension .css
3.Go to config/initializers/assets.rb
4.Put this line of of code Rails.application.config.assets.precompile += %w( file.css )
5.Replace file.css with the file you created
6.Go to your html.erb file
7.Type this in the <head>, <%= stylesheet_link_tag "file" %>
8.Replace file with the filename(no extension in the name)

Good Job you linked the file!

Rishi Ryan
  • 67
  • 1
  • 5
  • 1
    Thank you for the concise answer. If you're trying to use sass, replace file.css in step 4 with file.css.scss or file.scss depending on how you have the file saved in apps/assets/stylesheets. And then it's properly sent thru the asset pipeline. No headache! – Joe Jul 21 '18 at 09:07
  • 2
    That works great, but it's not valid. individual erb files don't have a section. That's typically in the layout. – baash05 Nov 24 '18 at 22:34
  • Separately to the main section in my appliciation.html.erb file, I added another section to a specific html.erb file and it works ok. Is there a reason not be doing this? – John Hanlon Apr 02 '21 at 11:49
  • that's just malformed HTML. Please cross-check.. A single document can't have multiple tags – Rajan Verma - Aarvy Apr 29 '21 at 11:28
  • This still makes it available everywhere. However, if you put in the application.html.erb something like `<% if @fileCSS %><%= stylesheet_link_tag 'file' %><% end %>` and then put `@fileCSS = true` in the controller methods you want to "see" it, it works perfectly. – Liz Nov 26 '21 at 22:53
3

There's one specific action that I want to have use different CSS.

Here's an alternative way to accomplish what you're looking for:

Add the controller name and action name to the app body in your /views/layouts/application.html.rb:

<body class="<%= controller_name %>-<%= action_name %>">
  <%= yield %>
</body>

Then in your .scss file:

.controller_name-action_name {
  // your css goes here
}

So if your controller was static_pages and your action was home:

.static_pages-home {
  // your css goes here
}

Tada! Your css only appears for that specific action.

Kyle Krzeski
  • 6,183
  • 6
  • 41
  • 52
  • 1
    I like this idea, but I have the feeling that giving the user this information, i.e. the controller name and the action used in a specific page, is not a good practice. – Rorrim Jun 01 '22 at 15:19
1

In your layout

<head>
  // ...
  <%= yield :stylesheets %>
</head>

In your view

<%= provide :stylesheets do %>
  // your page-specific css
<% end %>
gbertl
  • 321
  • 2
  • 16
  • 2
    While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, as this reduces the readability of both the code and the explanations! – Blue Feb 01 '18 at 04:30
  • 1
    This should be the answer! It is so simple. Include explanation -> "Here's how you add things to the head." Done – baash05 Nov 24 '18 at 22:41