Organizing CSS & Sass in Rails

Rails comes equipped with Sass by default. When you build a new app, an application.css stylesheet is created for you. This isn't just a normal stylesheet -- rather, it stands as a master stylesheet in the Rails Asset Pipeline. If you don't know how the Asset Pipeline works, I suggest you read a little bit about it. In this file, you'll find the following code.

app/assets/stylesheets/application.css
/*
    ...ommitted code...
    *= require_self
    *= require_tree .
*/

require_self puts the CSS contained within this file at the top of any other CSS being imported in the following lines. This means if you need to override some classes quickly, here's the stylesheet to do it in. But, I would advise against that as overriding anything from external stylesheets can become confusing very quickly for yourself and other devs working on the project.

require_tree . then imports every other file in your app/assets/stylesheets/ directory. When it's time to compile & compress all the Sass, all of your code gets put into the application.css file, which then gets a unqiue hash generated on the end of it to prevent caching issues. You can see this in action if you check the source of a live Rails app. It'll look something like /assets/application-743e264624884d9ac2219fcfbb648f54.css.

Another thing that Rails does, is generate stylesheets for you when you run the rails generate command in the terminal. On creation of a new controller, you'll see a stylesheet with the controller's name in your assets. It'll look something like app/assets/stylesheets/posts.css.scss. I'm not quite sure of the reasoning behind the naming scheme here, as you could also name this file posts.scss and it can be imported just the same. The problem with this auto-stylesheet generation, is that it leads to a rather unorganized set of styles. with each controller you create, you end up with a new sheet. Something like...

**BAD**
+--- assets/
|   +--- stylesheets/
|       +--- /application.css
|       +--- /posts.css.scss
|       +--- /users.css.scss
|       +--- /uploads.css.scss
|       +--- /home.css.scss

Another issue with this is the use of site-wide Sass resources such as variables, mixins, and other proprocessor features. If you declare a mixin and some variables in home.css.scss, you cannot use it in users.css.scss. This is because Rails does not compile all the stylesheets in the Sass way using @import, but rather it includes them all with the require_tree . snippet you saw earlier in application.css.

I much prefer to take full advantage of Sass, while still playing nice with Rails' asset pipeline. Here's the strutcure I follow:

app/assets/stylesheets/application.css
/*
    *= require_self
    // notice I'm only requiring main.scss
    *= require main
*/

Directory structure:
+--- assets/
|   +--- stylesheets/
|       +--- /application.css
|       +--- /main.scss
|           +--- base/
|               +--- /mixins.scss
|               +--- /globals.scss
|               +--- /normalize.scss
|           +--- styles/
|               +--- /posts.scss
|               +--- /home.scss

This allows you to do all of your organization in main.scss, leaves you in charge of which stylesheets come first, and provides all your Sass access to mixins living in your mixins.scss file. Lets take a look at what my main.scss file looks like.

app/assets/stylesheets/main.scss
@import "base/mixins.scss";
@import "base/normalize.scss";
@import "base/globals.scss";
@import "styles/home.scss";
@import "styles/posts.scss";

I usually avoid writing any regular Sass code in my main stylesheet, as well as in application.css. If I write styles that affect the website globally, they will go in globals.scss. If I edit how my posts are laid out, or how my user home page looks, I will put the styles in their respective stylesheets. This tackles CSS in a more object oriented way, as well as keeps your assets clean and very easy for a team to go in and edit.

Leave your thoughts below, or ping me on Twitter. I plan to make a quick gem to set this all up for you in the future, so be on the lookout!

Tweet