Ruby on Rails CSS File Structure

css ruby on rails software development

Ruby on Rails is an amazing framework. At BiteSite, we started using it back in 2011 and haven’t stopped since. It’s combination of technical ability and software philosophy has kept it our framework of choice when it comes to web development and it only gets better with time.

One of the things we appreciate most about Ruby on Rails is its convention over configuration philosophy. In short, it basically says, “If you follow our conventions, you don’t have to configure your code”. This allows for less boilerplate code and is, in my opinion, the best way to enforce coding conventions.

With convention over configuration, one of the best benefits you get is a well designed architecture for your code. Every Rails app puts all of its controllers in the same folder, all of its models in the same folder and so on. It makes developing Rails so easy to jump into.

That being said - it’s not perfect. While convention over configuration is in so many places in Ruby on Rails - there are areas it doesn’t exist and you’re left to come up with your own design or structure.

One of these places is CSS.

While Rails does suggest how to structure your CSS when you use scaffolding, it doesn’t really have a strict convention over configuration policy about how to split up your CSS into multiple files and where to put them (aside from the ‘stylesheets’ folder).

When apps get big, this can become a problem.

Over the years, we have come up with a system that has served us pretty well that we thought we’d share. It’s not perfect, but it does work.

Definition

To be clear, this system only dictates how to split up your files and where to put them. It does not cover how to actually structure your CSS selectors and CSS code within those files.

Driving Forces

When we set out our to improve our CSS File structure, there were really two driving forces. One, as with most coding, we wanted our code to be DRY (Don’t Repeat Yourself) where possible. Now, notice I said “where possible” vs. “as much as possible”. We found that overtime, it was hard to keep track of CSS code and so many times you would change some CSS code to fix one problem, but not realize it was being used in 5 other places. We were consistently breaking pages without realizing it and so we implemented our second driving principle: make our file structure easy to follow and easy to figure out what pages it was affecting.

We realized this was breaking DRY code, but it kept our codebase sane and predictable.

(Incidentally, we found out later on, that this true separation of concerns vs separation of technologies was being embraced in future frameworks like React and Vue where you can scope CSS to individual components).

SCSS vs CSS

Note, at BiteSite, we use SCSS to help with a lot of this, but a lot of the learnings can be applied to vanilla CSS.

CSS File Structure

With those two driving forces in places, we came up with the following CSS file structure:

app
↳ assets
  ↳ stylesheets
    ↳ base
    ↳ common
    ↳ components
    ↳ views

The 4 folders we’ve introduced are base, common, components, views. Let’s explore each of these.

base

The base folder is where we put our UI Kit CSS. So all your standard buttons, links, typography, tables and other CSS that would apply to most or all web apps we put in here. To complement this, we also include things like globals and colorswatches which we can import into the rest of the files. We’ll usually split each of these into its own file. Examples include:

app
↳ assets
  ↳ stylesheets
    ↳ base
      ↳ globals.scss
      ↳ colorswatches.scss
      ↳ buttons.scss
      ↳ links.scss
      ↳ typography.scss
      ↳ forms.scss
      ↳ tables.scss

common

The common folder is how we achieve ‘DRY’ code outside of UI Kit elements. While we generally try to scope our CSS to specific pages (or components if using React), there are definitely some cases where it’s better to re-use CSS within HTML/JS code. We have a dedicated folder for these to help remind developers. It tells the developer, “Hey, if you’re editing something in the common folder, be careful, because it most likely affects more than just the part of the web app that you’re working on.” Additional to some reusable CSS that’s specific to your app, we also include things like layout CSS here. Examples include:

app
↳ assets
  ↳ stylesheets
    ↳ common
      ↳ layout.scss
      ↳ shopping_cart_line_item.scss
      ↳ pricing_badge.scss

components

This is specific to web applications that use Webpacker + React. If you don’t use React, you can skip this part. When we create components and want to scope some CSS specifically to the component. Now I’m sure there are better JS ways to do this, but to be honest we haven’t quite figured them out yet, so we do this instead.

To make this work, we add a CSS ID/class selector to the outer most element of the component that starts with “component-”. Here is an example:

/* product_card.jsx */
…
function ProductCard() {
  return (
    <div className=’component-product-card’>
      …
    </div>
  );
}

Then we place it’s corresponding CSS in this folder:

/* app/assets/stylesheets/components/product_card.scss */
.component-product-card {
  …
}

views

This is the last folder and we use this for page specific CSS. To do this scoping, we surround every view template in a <div> or <span> that has an ID in the form of “controller-view”. Here is an example:

<!-- app/views/products/show.html.erb -->
<div id=”products-show”>
  ...
</div>

With the page scoped, we then place the CSS files in a file structure that’s similar to the regular app/views/ folders. We create a folder for each controller and a file for each view.

/* app/assets/stylesheets/views/products/show.scss */
#products-show {
  ...
}

Conclusion

We’ve been using this file structure for over 5 years now and while we know of some shortfalls (e.g. React components named the same but within different folders), it has worked out very well for us. Hopefully this will help others as well!

Casey Li
CEO & Founder, BiteSite