Controller Specific Stylesheets in Rails 3: Inheritence

Controller specific stylesheets in rails 3: Inheritence

The Rails guide on how to use the asset pipeline doesn't quite tell the whole truth here. It says:

You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as <%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag params[:controller] %>.

Now, you could do as they suggest and load specific stylesheets for each controller, but it does not work as they suggest out of the box. The neglect to mention a few things you must do.

  1. You need to remove the //= require_tree . directive from application.css, which, left in place, will load every other asset in the folder. This means that every page would load users.css, and if you added the controller-specific stylesheet line as in their example, it would load the controller stylesheet twice.

  2. You would need to tell Rails to precompile the individual files. By default, all *.css files besides application.css are ignored by the precompiler. To fix this you'd have to do edit your config to do something like this:

    # in environments/production.rb
    # either render all individual css files:
    config.assets.precompile << "*.css"
    # or include them individually
    config.assets.precompile += %w( users.css static_pages.css )
  3. Finally, as instructed by the Rails guide, you'd need to change your stylesheet includes to look something like:

    <%# this would now only load application.css, not the whole tree %>
    <%= stylesheet_link_tag :application, :media => "all" %>

    <%# and this would load the controller specific file %>
    <%= stylesheet_link_tag params[:controller] %>

However, the above may not be truly the best practice. Sure, sometimes you might want individual stylesheets, but most the time you probably just want to serve your style bundle so the client can cache one file. This is how the asset pipeline works out of the box, after all.

Besides that, if you were to just add override rules in your controller specific stylesheets, then you're creating a load-order-specific tangle of styles right out of the gate. This... is probably not good.

A better approach might be to namespace the styles in the controller sheets, something like this:

// in application.css (or some other commonly loaded file)
background-color: $color1;

// in users.css.scss
body.controller-users {
background-color: $color2;
}

// and so on...

Then in your layout, add the controller name to the body class, like:

<body class="controller-<%= params[:controller] %>">

In this way, your styles are resolved by namespace, not just load order. Furthermore with this solution you could still go ahead and load separate controller-specific stylesheets if you desire, or you could forget about that and just let everything be compiled into application.css as it would be by default. All the styles would be loaded for each page, but only the controller-specific styles would apply.

How do I use Controller specific stylesheets in Rails 3.2.1?

I don't think it works that way (Home.css being applied only to Home controller actions). The different files are just for separation, to make it clearer what are the CSS rules describing. You can read this guide about the asset pipeline. I'm guessing you altered the default application.css.scss and removed the line importing all CSS files from app/assets/stylesheets.

rendering only controller-specific stylesheets not working on first request

If you are not using Turbolinks, remove it from your project. It includes:

  • removing turbolinks gem
  • removing requires from application.js and application.css
  • removing data-turbolinks-track attribute from your layout

And consider using rails new --skip-turbolinks for your next project.

My stylesheets in Rails seem to be overlapping

This is due to Sprockets & bad CSS structuring.



#app/assets/stylesheets/application.css
*= require_all

It's not CSS which will be the problem, but the way you're calling it - both in your asset pipeline and your application.

The above will be concatenating all your css into the main application.css file. Whilst this might seem harmless, if you have conflicting CSS definitions, they'll override themselves.

Sample Image

The way around this is to firstly ensure you're not conflicting your CSS and you are calling only the appropriate CSS for your application.


Sprockets

To better understand a fix, you need to know about sprockets.

Sprockets compiles your assets to make them as efficient as possible.

Your assets reside in the asset pipeline, which has been created to be such that it will load the appropriate CSS for the different parts of your app -- both efficient and versatile.

The "conventional" way for Rails to do this is to encourage you to use controller centric assets and application level assets:

#app/assets/stylesheets/controller.css
#app/assets/stylesheets/application.css

This is okay if your CSS works in situ, but if it doesn't, you've got a problem (like what you're experiencing).

As such, the way to fix is to firstly ensure that you're splitting your CSS as you need, and you're coding the CSS properly (more in a second).



Controller-Centric Assets

1. Remove all references in "application.css"

Sprockets gives you the ability to "include" other files in your main application.css file:

#app/assets/stylesheets/application.css
*= require_all <- remove this line

This will prevent your "application.css" file having all the others compiled into it. The others will be compiled on their own, allowing you to call them separately:

2. Layout

#app/layouts/application.html.erb
<%= stylesheet_link_tag "application", controller_name %>

This will allow you to call the controller-centric assets you have with base-level application CSS.

The drawbacks to this are that you'll have to repeat a lot of your CSS, and will end up having completely different CSS for different parts of your app -- not the best.



CSS Structure

The alternative is to make your CSS structured properly. This is much harder to achieve, but will give you much clearer results...

I have a main div with different width for my Sessions controller (login page) and my Records controller (search function.)

#app/assets/stylesheets/application.sass
.main
/* main styling */
&.search
/* search styling here */
&.results
/* results styling here */

This will allow you to call:

#app/views/layouts/application.haml
.main.search search stuff here
.main.results results stuff here

Rails - Stylesheet require - choose specific stylesheets for each page

You can use different layout and can load different assets on every layout.

<%= stylesheet_link_tag "css_file_name" .... %>
<%= javascript_include_tag "js_file_name" ... %>

Then you can define your controller actions which layout should render, wish you can get an idea from here.

respond_to do |format|
format.html {render :layout => 'your_layout_name'}
end

Rails CSS stylesheets overriding each other

From the documentation:

Sprockets concatenates all JavaScript files into one master .js file
and all CSS files into one master .css file.

What this means, of course, is that when you make a change to jobs.css, you'll see the same css being applied to every matching element throughout your application. All of those separate .css files are there to help you keep things organized from a human perspective, rather than from the perspective of your application.

You might want to just come up with different IDs and classes depending on your page (like #body_client and #body_job) to differentiate them, but you can see how this naming convention could get unwieldy as your app grows.

Having separate assets is possible, but not without some pain.

In application.js, remove:

//= require_tree

In application.css, remove:

*= require_tree

In application.html.erb, add the following:

<%= stylesheet_link_tag "application", params[:controller], :media => "all" %>
<%= javascript_include_tag "application", params[:controller] %>

Create a new initializer file at config/initializers/assets.rb and add the following code:

%w( clients_controller jobs_controller ).each do |controller|
Rails.application.config.assets.precompile += ["#{controller}.js.coffee", "#{controller}.css"]
end

That should get you set up with separate per page assets. Check the original blog post for more details.

rails 3 css specific for controller is not loading in production (heroku)

It may be that you need to precompile your assets.

bundle exec rake assets:precompile:all RAILS_GROUPS=assets

Read up on the Rails Guides Asset Pipeline.

That would explain why it worked in development, but the production environment is set to expect to use precompiled assets.

Rendering a specific stylesheet for a view or set of views

In the section of your template file (often 'application.html.erb'), you can use a conditional to set the stylesheets to use based on something...

<Head>
<% if ... %>
<%= stylesheet_link_tag "main" %>
<% else %>
<%= stylesheet_link_tag "off-brand-main" %>

The conditional could be a controller (controller_name == 'something') and/or action (action_name == 'something').

Can't access indivdual controller stylesheets in Rails 4

I'm not sure what the params['clients'] part is about, do you mean params[:controller] (which would be better accomplished through controller.controller_name I think)? It should just be <%= stylesheet_link_tag 'name_of_stylesheet' %> that you want.



Related Topics



Leave a reply



Submit