Home » Ruby Sass to Libsass: 1. Introduction

Ruby Sass to Libsass: 1. Introduction

In September and October of 2014, Dealer.com transitioned its CMS platform from Ruby Sass to Libsass. This is Part 1 of the story.

Introduction to Sass

Sass is a superset of CSS that compiles down to ordinary CSS. It offers handy things like variables and control flow, as well as more advanced functionality like ruleset nesting, mixins, and extends.

Sass was implemented in Ruby, as suggested by its borrowed use of the terms “mixin” and “extend” for its own concepts. As Sass became popular, a supporting plugin, also written in Ruby, named Compass was widely used to handle the boilerplate of browser prefixing properties, shortening verbose styling, and even spriting images.

As more people adopted Sass and Compass for larger projects, they began to notice that its speed was not great. Sometimes it was terrible. And in 2012, the creator of Sass launched a new project to reimplement Sass in C++, calling it Libsass. It wasn’t until 2013 that the flowering of JavaScript build tools like Grunt and Yeoman brought the attention of Sass users to Libsass through its bindings in node-sass.

Those users soon realized that the new Sass implementation didn’t exactly match up to the one they were accustomed to.

That brings us to September 2014

Libsass issue #146, “Extended selectors won’t compile correctly”, had been open since August 2013. The issue had 76 comments, but was only making fitful progress toward resolution. At Dealer.com, and especially in the CMS product, we’re using @extend a lot. We started looking into whether a simple fix, much like those already attempted, would get us to the level of support we need.

During the Dealer.com Dallas Hackathon, Josh Towers, Adam Moore, and David Holt switched Clickmotive sites from Ruby Sass to Libsass, taking the Sass compilation for a palette from 50 seconds to 2 seconds. They were able to accomplish those wins without any modifications to libsass – in part because they weren’t using @extend.

While use of Libsass was increasing, it seemed there were a large number of Ruby Sass users who couldn’t switch to the much faster C++ implementation specifically because the @extend implementation was incomplete. If we could implement @extend, our Sass would compile faster, but we’d also unleash a flurry of interest in further development of Libsass that would benefit us down the road.

How hard could it be?

What @extend does

First, we should get a handle on what the expected behavior is.

The @extend directive in Sass allows you to tell the compiler that you want to be able to use the selector for the current ruleset to also be applied to another ruleset (or rulesets) within the current Sass context. It sounds a little confusing when you say it like that – I often find an example is clearer.

At it’s simplest, @extend does something like this:

SCSS: a { x:y; } b { @extend a; }
CSS:  a, b { x:y; }

That looks pretty nice. We could see how there are some cases where that might improve the size of the compiled CSS.

Here’s a slightly more complex example:

SCSS: a b { x:y; } c { @extend b; }
CSS:  a b, a c { x:y; }

Great! So it seems like @extend gets us a new selector, replacing what we’re extending with what we’re extending from.

But then let’s take a look at this:

SCSS: a b c { x:y; } d e { @extend b; }
CSS:  a b c, a d e c, d a e c { x:y; }

What’s the deal with the third selector?! (The “d a e c” one.) At first, we had a hard time believing it was intentional. It turns out that it’s documented. Not only that, but it became clear from a conversation with Natalie and Chris that the Sass maintainers are philosophically committed to that behavior.

But finally: our existing CSS contains selectors generated by that algorithm, and there’s risk associated with just leaving them out, even if they seem a little bonkers. So with the simple solutions ruled out, we started really digging into how to make @extend do the right thing.

To Be Continued

Continued in Implementing Extend.