Scope and precedence of CSS definitions?

I (a Rails newbie) am trying to understand how CSS works in Rails, relying on guidance from http://guides.rubyonrails.org/configuring.html. I’m getting some surprising, perplexing, and unwelcome results, regarding how the CSS settings cascade to different views. To follow the story, see numbered screen shots at https://drive.google.com/file/d/0B-thboqjuKZTeVI3R0tPbGhPVjQ/view?usp=sharing:

  1. Here’s the legend for all the screen shots: This app has two models, users and toys. The views and other Rails objects for both models have been generated using scaffold. On each screen, the top window shows scaffolds.scss; below that on the left is users.scss; at the right of that is toys.scss; at lower left is the users object list; and at lower right is the toys object list. This screen shows the unmodified versions of all this. The screen backgrounds are both white, following the CSS in scaffolds.scss. Thru all these screens, the variable to watch is the background color.

  2. I modified (and saved) users.scss to make a yellow background, and I refreshed the pages at the bottom. Here came the first surprise: I expected the users background to turn yellow and the toys background to remain white. In other words, I assumed that the scope of the users.scss setting would be just the users pages, and that the toys pages would continue to inherit the (unmodified) white background from scaffolds.scss.

  3. I modified (and saved) toys.scss to make a yellow background, and I refreshed the pages at the bottom. I was hoping that the toys screen would turn green and the users screen would stay yellow. Nothing changed. So it seems that the users CSS overrides the toys CSS, tho I have no idea how this precedence got established.

  4. THis is further confirmed when I change the users CSS, and it applies to both.

  5. When I remove the users CSS color specification, both screens now follow the toys CSS. This too is weird; why doesn’t one or both of these screens inherit the scaffold.scss setting?

6-7. A couple more experiments confirm that (a) If the CSS attribute for any model is set, that setting applies to all models in the application (rather than just to that model); and (b) among the models in an app, there is some kind of precedence ordering that determines which model’s setting of a given CSS attribute applies to all models.

Clearly, there is a bug here. Whether the bug is in in the machinery of Rails or in my understanding of it, I can’t tell. What I would like to know is, how (or where) to set CSS attributes so they apply to the displays of just one model.

~ Thanks, Ken

The default setup is that all of your CSS is loaded all the time. While it might seem wasteful to load CSS that isn’t needed on a given page, the rationale is that this allows the browser to load a single CSS file on first visit that is then cached for subsequent pages.

In development the CSS files are fetched individually but the result is the same. There is nothing that says that the styles from users.scss should only apply to pages rendered by the users controller, nor is there an inheritance chain. If you want these things, you must set them up yourself, for example by setting a class on some appropriate element of the DOM based on the current controller and then changing the selectors in your CSS files

In the presence of conflicting CSS directives the browser picks the most specific one (eg #foo .title is more selective than just .title) - see https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity for example.

Fred

Thanks Fred ~

My follow-up questions are inserted below your comments (which I have indented).

~ Ken

There is nothing that says that the styles from users.scss should only apply to pages rendered by the users controller, nor is there an inheritance chain.

There does seem to be a de facto inheritance chain (or precedence ordering) among my three .scss files (in descending order of precedence, the chain is users.scss, toys,scss, scaffold.scss) - that is, if I add a style to any of these, it trumps those later in the chain; if I remove a style, it exposes the one following it in the chain. What I don’t understand is how this particular chain got established, and why. In other words, why are there separate scaffold.scss, users.scss, toys.scss, and other .scss files in the standard generated Rails setup? Why not just one .scss file for all application-wide styles?

And if I want a particular style sheet to be scoped to something less than the whole app, where do I put it or references to it?

I just noticed this in the application.css file. It seems to speak to these questions, but I don’t understand it:

  • You’re free to add application-wide styles to this file and they’ll appear at the bottom of the
  • compiled file so the styles you add here take precedence over styles defined in any styles
  • defined in the other CSS/SCSS files in this directory. It is generally better to create a new
  • file per style scope.

``

FYI, that is a perfectly legitimate, and common, way to do things. But it's also perfectly legitimate, and common, to have some styles specific to certain kinds of data. Really just depends on what's a better fit for your application design...

~ Ken

There is nothing that says that the styles from users.scss should only apply to pages rendered by the users controller, nor is there an inheritance chain.

There does seem to be a de facto inheritance chain (or precedence ordering) among my three .scss files (in descending order of precedence, the chain is users.scss, toys,scss, scaffold.scss) - that is, if I add a style to any of these, it trumps those later in the chain; if I remove a style, it exposes the one following it in the chain.

What I don’t understand is how this particular chain got established, and why. In other words, why are there separate scaffold.scss, users.scss, toys.scss, and other .scss files in the standard generated Rails setup? Why not just one .scss file for all application-wide styles?

Inheritance is the wrong way to think about it: there is a single flat namespace, conflicts are resolved by specificity as described in the document linked earlier. The behaviour you observe simply means that (for example) the styles you’ve added/removed to users.css had higher specificity to the ones in the other stylesheets. If there is a tie on specificity of a selector then the last selector ones. Which files selectors are in doesn’t matter, other than it having some influence on which occurrence of a selector is last.

And if I want a particular style sheet to be scoped to something less than the whole app, where do I put it or references to it?

I just noticed this in the application.css file. It seems to speak to these questions, but I don’t understand it:

  • You’re free to add application-wide styles to this file and they’ll appear at the bottom of the
  • compiled file so the styles you add here take precedence over styles defined in any styles
  • defined in the other CSS/SCSS files in this directory. It is generally better to create a new
  • file per style scope.

``

It’s trying to discourage you from adding straight to application.css - it creates clutter. You can setup an entirely separate manifest (I’ve commonly see applications have a separate admin.css) and then change stylesheet_link_tag to load that css file instead of application.css for some pages. Most of the time you just want to make the selectors specific enough that they only apply to content on the relevant pages.

For example if the body elements on all the pages rendered by the users controlled have class “users” then a selector of the form

body.users h1 {

}

would only affect h1 tags on pages from that controller (the application layout is a good place to enforce this sort of thing)

Fred

Thanks Fred ~

The stylesheet_link_tag is the new news to me (remember, I’m a newbie!), and it sounds like that’s the way to apply different stylesheets to different pages. Also, I just discovered the http://guides.rubyonrails.org/layouts_and_rendering.html guide, which I should have read before I even raised this question.

~ Ken