Get Inside Unbounce


Handling Code Duplication With Sass @imports in The Asset Pipeline

I have used the asset pipeline heavily since its release in Rails 3.1.0 over two years ago. Only recently did I run in to a handful of gotchas when it came to dealing with stylesheet compilations using Sass.

The asset pipeline had been used mostly for compilation and minification of coffeescript. Not much time was spent on the stylesheet-side development. Depending on the service we may have used Sass or Less but in either case the only feature we really took advantage of from either was nesting rule definitions.

This made our CSS compilations very simple. We used the pipeline-provided require directives and it compiled the stylesheets without a problem. Occasionally we would see a load-order or dependency issue that was simply resolved by requiring one file before the other.

A simple example:

 *= require 'base'
 *= require 'fonts'
 *= require 'styles'

This would result in a compiled css file as such:

# rules from base.css …
# rules from fonts.css …
# rules from styles.css …

The first time I ran into an issue with this pattern was when we began using more of Sass’s features, including mixins, functions & variables. We wanted to define sets of commonly-used mixins and variables and have access to them in all of our stylesheets. The plan was a very common use-case and one of the reasons Sass comes highly recommended.

We started by defining a common.css.scss file with some variables and functions:

@mixin content-area() {
      background: $light-blue;
      box-shadow: 0 5px 0 -2px rgba(0, 170, 255, 0.10);
      padding: 42px 20px;

$burgundy:   #68332f;
$light-blue: #f5fbfc;
$red:        #dc1730;

Then utilized the new mixins in the styles.css.scss file:

.callout-bucket {
    @include content-area;
    height: 252px;

Now require both files in application.css:

 *= require 'common'
 *= require 'styles'
 *= require 'other'

And tada! We get an exception.

Error compiling asset styles.css:
Sass::SyntaxError: Undefined mixin 'content-areas'

What is going on?

I thought Sass was supposed to help me with code reuse, what gives?

When the asset pipeline parses files it does so file by file, pre-processor by pre-processor (from right to left). This means the common.css.scss file is where it starts. As the scss extension is the rightmost extension Sass begins by running the file through the Sass pre-processor. It makes the mixins and variables available for use within the current Sass environment scope, parses the file, and ends. The pipeline then moves on to the next file styles.css.scss. Sass starts up a new environment scope again. It attempts to use the mixin defined in common.css.scss but it is not defined. This is because common.css.scss and styles.css.scss are not being processed within the same scope.

Fixing It: The Wrong Way

My first thought to fix the problem was: “oh I need to include those functions into each and every file I want to utilize them in.”

So I adjusted my styles.css.scss and other.css.scss files and included the common.css.scss file at the top of both.


@import 'common'

.callout-bucket {
    @include content-areas;
    height: 252px;


@import 'common'

// code for other.css.scss …

This fixes the exception raised by the pre-processor and compiles my css as I expected. My page styles load and everything seems great. Upon a visual inspection of the compiled css it reveals the file is structured as such:

# rules from base.css …
# rules from styles.css …
# rules from base.css …
# rules from other.css …

This shows us all the common rules were being brought in twice. I also had many other files I imported common in to. This result is thousands of lines worth of repeated rules. This increases the compilation time and overall compiled file size.

This is not a suitable “solution.”

Fixing It: The Right Way

So it turns out that the fix is actually quite simple, and is recommended by both the Rails Guides and the Sass-rails gem readme.

If you want to use multiple Sass files, you should generally use the Sass @import rule instead of these Sprockets directives. Using Sprockets directives all Sass files exist within their own scope, making variables or mixins only available within the document they were defined in.

The Rails Guide

Sprockets provides some directives … They are very primitive and do not work well with Sass files. Instead, use Sass’s native @import directive which sass-rails has customized to integrate with the conventions of your Rails projects.

sass-rails github

Now that we understand the scope problem we can change our application.css file to be a sass processed file application.css.scss and make all our imports using the Sass @import method:

@import 'bourbon';
@import 'neat';
@import 'common';
@import 'styles';
@import 'other';

This creates a single scope while pre-processing the application.css.scss file and imports all our other files in to that scope. This allows them to have access to any of the variables, functions, and mixins defined before them.

Another common problem: Redundant Includes

Another issue often experienced as an application grows, and with it the size and amount of stylesheets, is that files will often end up importing or mixing in the same rules multiple times.

The sprocket directives, as well as Sass’s @import, do not validate, nor care, if the file being imported has or has not been previously imported.

We’ve experienced this issue here at Unbounce. Here is a single example:


@import 'compass/css3/border-radius';       
@import 'compass/css3/images'
@import 'compass/css3/box-shadow';      
@import 'compass/css3/user-interface';


@import 'base';

@import 'compass/css3/border-radius';
@import 'compass/css3/images';
@import 'compass/css3/box-shadow';
@import 'compass/css3/user-interface';

@import 'base';
@import 'base/colours';
@import 'base/mixins/buttons';

@import 'reset.css.scss';
@import 'main.css.scss';
@import 'global.css.scss';
@import 'header.css.scss';

The result is a file with all the css from base.css.scss is included three times over in the final compiled file.

# rules from base (including compass imports) …
# rules from compass import …
# rules from base again (including compass imports) …
# rules from all other imports …

Sass and the Pipeline will both allow you to find yourself in this corner. It is up to the developers to make sure our stylesheets are structured and organized.

Sass is an awesome tool and using it with the asset pipeline can make for a really great development environment. It is however another combination that makes it easy to shoot yourself in the foot when done improperly. Having guidelines for your team to follow will help keep your stylesheets well structured and organized. Knowing the gotchas during setup will save yourself from headache and rule redundencies.

9 CommentsLeave a Comment

  • Reply

    Chang Huang

    4 years ago

    How do you solve the last redundant problem?

    • Reply

      Trevor Wistaff

      3 years ago

      Ensure you don’t import in individual stylesheets but rather import your helpers/mixins in application.css.scss

    • Reply


      3 years ago

      We focus on being aware of what the import does and making sure when any particular person uses one we know it’s the first occurrence.

      It’s more about knowing that the problem will occur and doing your best not to let it happen.

      Once you understand the problem, recognizing it and finding/removing the redundancies becomes easier.

  • Reply

    Trevor Wistaff

    3 years ago

    Thanks a lot for this! It was bothering me and I completely overlooked that highlighted quote in the Rails guide.

    • Reply


      3 years ago

      Glad it helped!

      It’s easy to miss the suggestions in the guides and they can make all the difference.

  • Reply

    Stephen Deere

    3 years ago

    whats the best way to import google fonts within sass. eg bootstrap sass.
    if i add the @import in variables it end up being included multiple times… should i be creating a mixin instead

    • Reply


      3 years ago

      A mixin is a great way to go about including fonts.

      Adding the @import in many places would result in the fonts inclusion many times which is not the desired effect.

      A good way to handle it is to:
      – Include the google font url once near the top of the sass file
      – Put the font style in a variable
      – Include those fonts in mixins
      – Use your new mixins

      // Define the import
      @import url(',700,900|Noticia+Text:400,700')

      // Define the font style
      $ruda-black: 'Ruda Black', sans-serif;

      // Define a mixin
      @mixin ruda-black-font() {
      font-family: $ruda-black;
      font-weight: 600;

      // Use the mixin elsewhere
      .price {
      @include ruda-black-font();

      Hope the example helps!

  • Reply

    Victor Polko

    3 years ago

    Hello, Brian!

    I’ve run into this post while wondering how I can make my development faster.
    Here’s the problem:
    while using sass @import rules with Asset Pipeline, every small change — even a comment line! — in any of .sass files makes SASS recompile all files, even those that are in gems, and it takes it about 6-7 seconds on an average project.
    Whereas using Sprockets’ require* directives makes it recompile only the file that was changed, and it is of course much faster.
    Yes, as you mentioned, it is very uncomfortable to include all mixins and variables in every file where I might need them, but the development is really faster compared to sass @import.

    Maybe you have any recipe for me?

    By the way, for those who are afraid of worse debugging (all css in inspector is taken from application.css, while on @import), there is a gem for source maps:


  • Reply


    3 years ago

    I don’t understand the solution though. If I have stylesheets that are being created separately using css groups. How does this make it so that I don’t have to keep importing base/mixins/variables styles into the individual sass files?

    How do I link base.scss and helpers.scss to all of my other style sheets without the need to continually re-import the files? :( I read the article twice and I’m still very confused.

Leave a CommentPlease be polite. We appreciate that.

Your Comment