Applying SMACSS to Rails Projects

How to organize your stylesheets in the asset pipeline.

SMACSS is a great set of principles for organizing CSS rules into reusable modules; it makes a lot of sense for teams that are collaborating on large web applications. How do you apply these principles using SCSS in a Rails project?

If you’re already familiar with SMACSS, skip ahead to the Rails styleguide.

SMACSS principles

SMACSS (Scalable and Modular Architecture for CSS) is a set of CSS guidelines created and promoted by Jonathan Snook, who also offers an e-book of the same name. A generous portion of the book’s contents is available for free at smacss.com. If you aren’t already familiar with SMACSS, I highly recommend the book; it’s a quick read.

Let’s recap some core principles of SMACSS before we dive into the Rails implementation.

SMACSS categorizes styles as follows:

  • Base styles are the default styles for HTML elements (h1, p, pre, etc.).
  • Layout styles are grid and other reusable layout classes. All of these classes are prefixed with l- (e.g. .l-sidebar).
  • All remaining CSS is classified into modules, with classes namespaced by module. For example, an alert module might have the following rules: .alert, .alert-message.alert-icon.

These practices are strongly recommended:

  • Avoid IDs in CSS
  • Minimize the depth of CSS selectors
  • Use classes to define visual presentation patterns1

SMACSS versus Rails conventions

As an experienced Rails developer, you may notice that many of the SMACSS principles contradict what are typically considered best practices in Rails.

  • Rails scaffold creates a stylesheet per controller. Since controllers map to models, this means styles are organized according to your application’s domain model. By contrast, SMACSS rules are organized into modules that implement visual patterns, not domain-specific ones.
  • Rails developers will often nest CSS rules (a popular Sass feature) to create stylesheets that mimic the HTML hierarchies of their views. A pleasing syntactic symmetry between SASS and HAML makes this approach especially tempting. In SMACSS, nesting is heavily discouraged, because it leads to styles that are over-specific and hard to reuse.
  • Rails view helpers like content_tag_for automatically emit HTML with model-based class names. For example, content_tag_for(:tr, @user) { … } produces <tr class="user">…</tr>. These helpers assume your CSS follows domain-based naming conventions, whereas SMACSS uses presentational naming conventions.

In short, should you choose to apply SMACSS principles to your Rails project, keep in mind that you will be breaking some Rails conventions. Proceed with caution!

A SMACSS styleguide for Rails projects

With all the SMACSS background out of the way, let’s get to the actual application of SMACSS to a Rails project, shall we?

File organization

Structure your asset pipeline directories as follows. This layout borrows some conventions from GitHub’s CSS styleguide.

app/assets/stylesheets/
├── application.css.scss
├── base.css.scss
├── layout.css.scss
|
├── globals
│   ├── _all.scss
│   ├── _functions.scss
│   ├── _mixins.scss
│   └── _variables.scss
|
└── modules
    ├── alert.css.scss
    └── …

vendor/assets/stylesheets/
├── normalize.css
└── …

Filenames

In Sass, a leading underscore indicates a partial, meaning the file should not be compiled into a standalone CSS file. SMACSS advocates designing modules that can be used independently, so use partials only for pure Sass constructs, like variable, function, and mixin definitions.

Files that can stand alone (e.g. modules) should be named with the double-extension .css.scss. Partials, on the other hand, should start with an underscore and end with just .scss. Starting with Rails 4.2, partials named with .css.scss will trigger a deprecation warning.

Application manifest

Use the Sprockets //=require syntax to specify how your styles should be merged into a single stylesheet in production. Here’s an example application.css.scss:

//= require normalize
//= require ./base
//= require ./layout
//= require_tree ./modules

Globals

The globals directory is for Sass functions, variables, and mixins that are truly global: that is, things that are shared across multiple files. If you have variables or mixins that are only used by a single module, define those in the module file itself. Don’t clutter your globals directory with single-use code.

It is a good practice to use global variables for typography, like these:

$serif: Georgia, serif;
$sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif;

$title-font-size: 42px;
$headline-font-size: 26px;
$sub-headline-font-size: 20px;
$base-font-size: 16px;
$small-font-size: 13px;

$baseline: 25px;
$base-line-height: ($baseline/$base-font-size);

Consider combining all of your global imports with a globals/_all.css.scss partial that looks like this:

@import "./variables";
@import "./functions";
@import "./mixins";

Then in your base, layout, and module stylesheets, start off each stylesheet by importing all your globals:

@import "globals/all";

Sass import versus Sprockets require

When using Sass within the Rails asset pipeline, there are two ways to combine stylesheets: the Sass way (@import) and the Sprockets way (//=require). Both come in handy.

@import

By using the import syntax, you are instructing the Sass compiler to combine the Sass source code first, and then perform compilation. If your intention is to reuse global variables, functions, and mixins (or you want to use a third-party library like Bourbon) you must use @import. Likewise, if you want to use the Sass @extend keyword to combine CSS selectors across files, you’ll need to use @import for those files as well.

//= require

The require syntax is not to aid in compilation, but to define manifests used to merge files for production deployment. When require is used, Sprockets compiles your Sass source code first as independent stylesheets, and then merges the resulting CSS into a single file.

Benefits of require in development mode

The reason this styleguide suggests using require for application.css.scss is due to how Sprockets helpfully changes its behavior depending on whether Rails is running in development or production mode.

  • In production, Sprockets merges required files together, minifies them, and compresses them. The result is efficient, but hard to read.
  • In development, Sprockets doesn’t merge required files. It serves each stylesheet individually to the browser.

By using //=require, in development mode you’ll see this in your browser:

<link rel="stylesheet" href="/assets/normalize.css">
<link rel="stylesheet" href="/assets/base.css">
<link rel="stylesheet" href="/assets/layout.css">
<link rel="stylesheet" href="/assets/modules/alert.css">
…

Separate stylesheets make browser-based troubleshooting much easier. When using the WebKit inspector to trace style rules, now you can clearly see the cascade: which styles come from normalize, which are from base, your layout, and your modules.

Bootstrap your next project

If you like the suggestions that I’ve outlined in this post and are eager to apply SMACSS to your next Rails project, consider using my rails-template project on GitHub. Download the project tarball and use it as the foundation of your Rails app, or simply browse through the code and take what you need.

Here’s what you’ll find in my rails-template:

  • An asset pipeline preconfigured in the SMACSS style
  • Gemfile, Guardfile, and application configuration for a lightning-fast livereload workflow
  • An base.html.erb layout with some useful HTML5 boilerplate
  • Capistrano recipes, rbenv setup instructions, and much more

What’s been your experience integrating SMACSS and Rails? Let me know.


  1. Using presentational class names as advocated by SMACSS may feel wrong, especially since we were taught that well-written HTML and CSS should always be “semantic”. By following SMACSS, does that mean abandoning a clean separation between content and presentation? Isn’t that a bad thing? Not necessarily. Read Philip Walton’s excellent article on the subject: Defending Presentational Class Names

You just read

How to organize your stylesheets in the asset pipeline.

January 2013

Share this post?  Copy link

About the author

Hi! I’m a Ruby and CSS enthusiast, regular open source contributor, software engineer, and occasional blogger writing from the San Francisco Bay Area. Thanks for stopping by! —Matt

GitHub Email LinkedIn