A List Apart

Menu

Illustration by

A Vision for Our Sass

At a recent CSS meetup, I asked, “Who uses Sass in their daily workflow?” The response was overwhelmingly positive; no longer reserved for pet projects and experiments, Sass is fast becoming the standard way for writing CSS.

Article Continues Below

This is great news! Sass gives us a lot more power over complex, ever-growing stylesheets, including new features like variables, control directives, and mixins that the original CSS spec (intentionally) lacked. Sass is a stylesheet language that’s robust yet flexible enough to keep pace with us.

Yet alongside the wide-scale adoption of Sass (which I applaud), I’ve observed a steady decline in the quality of outputted CSS (which I bemoan). It makes sense: Sass introduces a layer of abstraction between the author and the stylesheets. But we need a way to translate the web standards—that we fought so hard for—into this new environment. The problem is, the Sass specification is expanding so much that any set of standards would require constant revision. Instead, what we need is a charter—one that sits outside Sass, yet informs the way we code.

To see a way forward, let’s first examine some trouble spots.

The symptoms

One well-documented abuse of Sass’s feature-set is the tendency to heavily nest our CSS selectors. Now don’t get me wrong, nesting is beneficial; it groups code together to make style management easier. However, deep nesting can be problematic.

For one, it creates long selector strings, which are a performance hit:

body #main .content .left-col .box .heading { font-size: 2em; }

It can muck with specificity, forcing you to create subsequent selectors with greater specificity to override styles further up in the cascade—or, God forbid, resort to using !important:

body #main .content .left-col .box .heading  [0,1,4,1]
.box .heading  [0,0,2,0]

Comparative specificity between two selectors.

Last, nesting can reduce the portability and maintainability of styles, since selectors are tied to the HTML structure. If we wanted to repeat the style heading for a box that wasn’t in the leftcol, we would need to write a separate rule to accomplish that.

Complicated nesting is probably the biggest culprit in churning out CSS soup. Others include code duplication and tight coupling—and again, these are the results of poorly formed Sass. So, how can we learn to use Sass more judiciously?

Working toward a cure

One option is to create rules that act as limits and reign in some of that power. For example, Mario Ricalde uses an Inception-inspired guideline for nesting: “Don’t go more than four levels deep.”

Rules like this are especially helpful for newcomers, because they provide clear boundaries to work within. But few universal rules exist; the Sass spec is sprawling and growing (as I write this, Sass is at version 3.4.5). With each new release, more features are introduced, and with them more rope with which to hang ourselves. A rule set alone would be ineffective.

We need a proactive, higher-level stance toward developing best practices rather than an emphasis on amassing individual rules. This could take the form of a:

  • Code standard, or guidelines for a specific programming language that recommend programming style, practices, and methods.
  • Framework, or a system of files and folders of standardized code, which can be used as the foundation of a website.
  • Style guide, or a living document of code, which details all the various elements and coded modules of your site or application.

Each approach has distinct advantages:

  • Code standards provide a great way of unifying a team and improving maintainability across a large codebase (see Chris Coyier’s Sass guidelines).
  • Frameworks are both practical and flexible, offering the lowest barrier to entry and removing the burden of decision. As every seasoned front-end developer knows, even deciding on a CSS class name can become debilitating.
  • Style guides make the relationship between the code and the output explicit by illustrating each of the components within the system.

Each also has its difficulties:

  • Code standards are unwieldy. They must be kept up-to-date and can become a barrier to entry for new or inexperienced users.
  • Frameworks tend to become bloated. Their flexibility comes at a cost.
  • Style guides suffer from being context-specific; they are unique to the brand they represent.

Unfortunately, while these methods address the technical side of Sass, they don’t get to our real problem. Our difficulties with Sass don’t stem from the specification itself but from the way we choose to use it. Sass is, after all, a CSS preprocessor; our Sass problem, therefore, is one of process.

So, what are we left with?

Re-examining the patient

Every job has its artifacts, but problems arise if we elevate these by-products above the final work. We must remember that Sass helps us construct our CSS, but it isn’t the end game. In fact, if the introduction of CSS variables is anything to go by, the CSS and Sass specs are beginning to converge, which means one day we may do away with Sass entirely.

What we need, then, is a solution directed not at the code itself but at us as practitioners—something that provides technical guidelines as we write our Sass, but simultaneously lifts our gaze toward the future. We need a public declaration of intentions and objectives, or, in other words, a manifesto.

Sass manifesto

When I first discovered Sass, I developed some personal guidelines. Over time, they formalized into a manifesto that I could then use to evaluate new features and techniques—and whether they’d make sense for my workflow. This became particularly important as Sass grew and became more widely used within my team.

My Sass manifesto is composed of six tenets, or articles, outlined below:

  1. Output over input
  2. Proximity over abstraction
  3. Understanding over brevity
  4. Consolidation over repetition
  5. Function over presentation
  6. Consistency over novelty

It’s worth noting that while the particular application of each article may evolve as the specification advances, the articles themselves should remain unchanged. Let’s cover each in a little more depth.

1. Output over input

The quality and integrity of the generated CSS is of greater importance than the precompiled code.

This is the tenet from which all the others hang. Remember that Sass is one step in the process toward our goal, delivering CSS files to the browser. This doesn’t mean the CSS has to be beautifully formatted or readable (this will never be the case if you’re following best practices and minimizing CSS), but you must keep performance at the forefront of your mind.

When you adopt new features in the Sass spec, you should ask yourself, “What is the CSS output?” If in doubt, take a look under the hood—open the processed CSS. Developing a deeper understanding of the relationship between Sass and CSS will help you identify potential performance issues and structure your Sass accordingly.

For example, using @extend targets every instance of the selector. The following Sass

.box {
	background: #eee;
	border: 1px solid #ccc;

	.heading {
	  font-size: 2em;
	}
}

.box2 {
	@extend .box;
	padding: 10px;
}


compiles to

.box, .box2 {
  background: #eee;
  border: 1px solid #ccc;
}
.box .heading, .box2 .heading {
  font-size: 2em;
}

.box2 {
  padding: 10px;
}

As you can see, not only has .box2 inherited from .box, but .box2 has also inherited from the instances where .box is used in an ancestor selector. It’s a small example, but it shows how you can arrive at some unexpected results if you don’t understand the output of your Sass.

2. Proximity over abstraction

Projects should be portable without over-reliance on external dependencies.

Anytime you use Sass, you’re introducing a dependency—the simplest installation of Sass depends on Ruby and the Sass gem to compile. But keep in mind that the more dependencies you introduce, the more you risk compromising one of Sass’s greatest benefits: the way it enables a large team to work on the same project without stepping on one another’s toes.

For instance, along with the Sass gem you can install a host of extra packages to accomplish almost any task you can imagine. The most common library is Compass (maintained by Chris Epstein, one of Sass’s original contributors), but you can also install gems for grid systems, and frameworks such as Bootstrap, right down to gems that help with much smaller tasks like creating a color palette and adding shadows.

These gems create a set of pre-built mixins that you can draw upon in your Sass files. Unlike the mixins you write inside your project files, a gem is written to your computer’s installation directory. Gems are used out-of-the-box, like Sass’s core functions, and the only reference to them is via an @include method.

Here’s where gems get tricky. Let’s return to the scenario where a team is contributing to the same project: one team member, whom we’ll call John, decides to install a gem to facilitate managing grids. He installs the gem, includes it in the project, and uses it in his files; meanwhile another team member—say, Mary—pulls down the latest version of the repository to change the fonts on the website. She downloads the files, runs the compiler, but suddenly gets an error. Since Mary last worked on the project, John has introduced an external dependency; before Mary can do her work, she must debug the error and download the correct gem.

You see how this problem can be multiplied across a larger team. Add in the complexity of versioning and inter-gem-dependency, and things can get very hairy. Best practices exist to maintain consistent environments for Ruby projects by tracking and installing the exact necessary gems and versions, but the simplest approach is to avoid using additional gems altogether.

Disclaimer: I currently use the Compass library as I find its benefits outweigh the disadvantages. However, as the core Sass specification advances, I’m considering when to say goodbye to Compass.

3. Understanding over brevity

Write Sass code that is clearly structured. Always consider the developer who comes after you.

Sass is capable of outputting super-compressed CSS, so you don’t need to be heavy-handed in optimizing your precompiled code. Further, unlike regular CSS comments, inline comments in Sass aren’t outputted to the final CSS.

This is particularly helpful when documenting mixins, where the output isn’t always transparent:

// Force overly long spans of text to truncate, e.g.:
// @include truncate(100%);
// Where $truncation-boundary is a united measurement.

@mixin truncate($truncation-boundary){
    max-width:$truncation-boundary;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
}

However, do consider which parts of the your Sass will make it to the final CSS file.

4. Consolidation over repetition

Don’t Repeat Yourself. Recognize and codify repeating patterns.

Before you start any project, it’s sensible to sit down and try to identify all the different modules in a design. This is the first step in writing object-oriented CSS. Inevitably some patterns won’t become apparent until you’ve written the same (or similar) line of CSS three or four times.

As soon as you recognize these patterns, codify them in your Sass.

Add variables for recurring values:

$base-font-size: 16px;
$gutter: 1.5em;

Use placeholders for repeating visual styles:

%dotted-border { border: 1px dotted #eee; }

Write mixins where the pattern takes variables:

//transparency for image features
@mixin transparent($color, $alpha) {
  $rgba: rgba($color, $alpha);
  $ie-hex-str: ie-hex-str($rgba);
  background-color: transparent;
  background-color: $rgba;
  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$ie-hex-str},endColorstr=#{$ie-hex-str});
  zoom: 1;
}

If you adopt this approach, you’ll notice that both your Sass files and resulting CSS will become smaller and more manageable.

5. Function over presentation

Choose naming conventions that focus on your HTML’s function and not its visual presentation.

Sass variables make it incredibly easy to theme a website. However, too often I see code that looks like this:

$red-color: #cc3939; //red
$green-color: #2f6b49; //green

Connecting your variables to their appearance might make sense in the moment. But if the design changes, and the red is replaced with another color, you end up with a mismatch between the variable name and its value.

$red-color: #b32293; //magenta
$green-color: #2f6b49; //green

A better approach is to name these color variables based on their function on the site:

$primary-color: #b32293; //magenta
$secondary-color: #2f6b49; //green

Presentational classes with placeholder selectors

What happens when we can’t map a visual style to a functional class name? Say we have a website with two call-out boxes, “Contact” and “References.” The designer has styled both with a blue border and background. We want to maximize the flexibility of these boxes but minimize any redundant code.

We could choose to chain the classes in our HTML, but this can become quite restrictive:

<div class="contact-box blue-box">
<div class="references-box blue-box">

Remember, we want to focus on function over presentation. Fortunately, using the Sass @extend method together with a placeholder class makes this a cinch:

%blue-box {
	background: #bac3d6;
	border: 1px solid #3f2adf;
}

.contact-box {
	@extend %blue-box;
	...
}
.references-box {
@extend %blue-box;
	...
}

This generates the following CSS, with no visible references to %blue-box anywhere, except in the styles that carry forward.

.contact-box,
.references-box {
	background: #bac3d6;
	border: 1px solid #3f2adf;
}

This approach cuts references in our HTML to presentational class names, but it still lets us use them in our Sass files in a descriptive way. Trying to devise functional names for common styles can have us reaching for terms like base-box, which is far less meaningful here.

6. Consistency over novelty

Avoid introducing unnecessary changes to the processed CSS.

If you’re keen to introduce Sass into your workflow but don’t have any new projects, you might wonder how best to use Sass inside a legacy codebase. Sass fully supports CSS, so initially it’s as simple as changing the extension from .css to .scss.

Once you’ve made this move, it may be tempting to dive straight in and refactor all your files, separating them into partials, nesting your selectors, and introducing variables and mixins. But this can cause trouble down the line for anyone who is picking up your processed CSS. The refactoring may not have affected the display of anything on your website, but it has generated a completely different CSS file. And any changes can be extremely hard to isolate.

Instead, the best way to switch to a Sass workflow is to update files as you go. If you need to change the navigation, separate that portion into its own partial before working on it. This will preserve the cascade and make it much easier to pinpoint any changes later.

The prognosis

I like to think of our current difficulties with Sass as growing pains. They’re symptoms of the adjustments we must make as we move to a new way of working. And an eventual cure does exist, as we mature in our understanding of Sass.

It’s my vision that this manifesto will help us get our bearings as we travel along this path: use it, change it, or write your own—but start by focusing on how you write your code, not just what you write.

About the Author

29 Reader Comments

Load Comments