Tutorial, part 2: An exhaustive guide to refactoring your CSS using SASS

A step-by-step guide to making those cool overlapping colored boxes with arrowed edges, all in CSS, plus some advice on using SASS to not repeat yourself in your code

Yesterday, in part 1 of this tutorial, I walked step-by-step through the process of taking a tag list from a blog entry and stylizing it into a “breadcrumb-style” look, including colored boxes with arrows that overlap each other. Yesterday’s tutorial was a standalone solution for people who code in strict CSS; today we’ll be looking at how to refactor the entire thing in the SASS pre-compiler for CSS, specifically to live up to the agile development adage of “don’t repeat yourself” (or DRY).

The simple fact is that, in our modern age of webapps, real-time database queries, and non-refresh onscreen updating, the walls between being a “designer” and a “programmer” are being rapidly knocked down, best encapsulated by the job requirements for what’s now called a “front-end developer:” for to hold such a job, you need to understand not only the things that a traditional graphic designer from 30 years ago might know (Photoshop, color theory, typography, etc); and not only such “psuedo-languages” like HTML and CSS that so-called “web designers” were required to learn during the Dot-Com era 20 years ago; but now also full-on object-oriented programming (OOP) languages like JavaScript, so that you can then use interaction-based JavaScript libraries like jQuery, and also navigate lightweight JavaScript-based frameworks like Angular, React, Ember, Bootstrap and more (the frameworks that are often used for so-called “single-page applications” like Twitter, which often don’t need the complexity and power of a full-sized framework like Rails or Django).

As more and more people with coding experience get involved with front-end design, they’re coming to realize the shortcomings of all the old front-end design tools, and are rightly horrified by how inefficient they are from a coding standpoint. Take Cascading Stylesheets (CSS), for example, invented in the late ’90s as an “add-on” to the existing Hypertext Markup Language (HTML) that’s been around since the birth of the web; it’s a way to add styling to a web page without having to abandon HTML altogether, invented very specifically for graphic designers with little to no traditional programming experience. (In fact, this was one of its biggest original selling points, that you didn’t have to know anything about traditional coding to use CSS, making it a friendly tool for old print-oriented graphic designers from the ’70s and ’80s, who suddenly found themselves forced to make “sites on the World Wide Web” as part of their jobs.) The result? Highly disorganized code, with no way to group together related items, tons of duplicate typing, and none of the powerful options found in OOP languages to automate much of the process for you, such as the ability to define variables or create functions.

That’s why a group of volunteer Ruby programmers invented SASS in the early 2000s, which like CSS itself lays on “top” of the existing technology so that you don’t have to get rid of it altogether. It’s essentially a means of writing CSS much more like you would code in an OOP language, then running that SASS file through a Ruby-based compiler on your local computer to convert it into the plain ol’ CSS that web browsers already understand. As such, then, SASS by definition has a bit of a learning curve, especially challenging if you come from a design-exclusive background and have done no coding before. (Just to begin with, you’ll need to know how to open a Terminal shell, fetch the SASS package, and install it through a command-line interface [CLI], before you can even begin to actually use it, so you’ve got some work ahead of you if you didn’t understand what that sentence means.) Today’s tutorial assumes that you already have SASS installed and understand the basics; if you don’t, you’ll want to start with one of the dozens of great tutorials and classes found online. (Hint: before anything else, begin with the organization’s official guide.)

So how exactly do we use SASS to eliminate duplicate code in our CSS stylesheets? Well, to begin with, let’s take a look at what we’re actually dealing with when it comes to this tutorial, where I am creating a series of colored boxes within the footer of my blog entries, full of footer-type information like when the entry was created, that entry’s categories, that entry’s tags, and how many comments that entry has, some boxes of which have arrows and some of which don’t. I myself am doing all this in WordPress, and am not creating it from scratch but rather am modifying the stripped-down, minimalist “starter” theme that WordPress provides to developers free of charge, called Underscores. That provides me with a series of pre-defined class names for all these things we’re looking at, and I’m choosing to just keep using those class names as they were originally defined, although in theory I could change them if I wanted to. In the case of a blog entry’s footer, that breaks down like this:

In traditional CSS, then, here is how you would typically write the code to make this footer look the way it does in the illustration:

.entry-footer {
padding: 2em;
}
.posted-on {
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
color: #ffffff; /* white */
background-color: #cc6633; /* red */
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.posted-on a:link, a:visited { color: #ffcc99; }   /* light red */
.posted-on a:hover { color: #993300; }              /* dark red */
.comments-link {
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
color: #ffffff; /* white */
background-color: #000000; /* black */
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.comments-link a:link, a:visited { color: #cccccc; }    /* gray */
.comments-link a:hover { color: #ffffff; }             /* white */
.edits-link {
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
color: #ffffff; /* white */
background-color: #cccccc; /* gray */
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.edits-link a:link, a:visited { color: #000000; }      /* black */
.edits-link a:hover { color: #ffffff; }                /* white */
.cat-links {
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
color: #ffffff; /* white */
background-color: #669999; /* blue */
position: relative;
white-space: nowrap;
padding: 0 0.25em 0 1.25em;
}
.cat-links a:link, a:visited { color: #ccffff; }  /* light blue */
.cat-links a:hover { color: #336666; }             /* dark blue */
.cat-links:after {                             /* blue triangle */
content: " "; /* see part 1 of tutorial */
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669999;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.cat-links:before {             /* white border around triangle */
content: " "; /* see part 1 of tutorial */
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}
.tags-links {
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
color: #ffffff; /* white */
background-color: #669966; /* green */
position: relative;
white-space: nowrap;
padding: 0 0.25em 0 1.25em;
}
.tags-links a:link, a:visited { color: #ccffcc; }/* light green */
.tags-links a:hover { color: #336633; }           /* dark green */
.tags-links:after {                           /* green triangle */
content: " "; /* see part 1 of tutorial */
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669966;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.tags-links:before {            /* white border around triangle */
content: " "; /* see part 1 of tutorial */
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}

Whoa, yeah, look at all that duplicated code. So how do we go about DRYing it up using SASS?

Beginning refactoring: Eliminating exact duplication

Well, for starters, let’s look at all the places where the code is being exactly duplicated character-for-character, without any deviations, over and over. For example, the typeface we’re using in the footer is the same from one box to the next, which means we can move any specs that are defining it to our all-enclosing .entry-footer class, and remove those specs from each individual colored-box class:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
.posted-on {
color: #ffffff;
background-color: #cc6633;
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.posted-on a:link, a:visited { color: #ffcc99; }
.posted-on a:hover { color: #993300; }
.comments-link {
color: #ffffff;
background-color: #000000;
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.comments-link a:link, a:visited { color: #cccccc; }
.comments-link a:hover { color: #ffffff; }
.edits-link {
color: #ffffff;
background-color: #cccccc;
position: relative;
white-space: nowrap;
padding: 0 1.25em 0 1.25em;
}
.edits-link a:link, a:visited { color: #000000; }
.edits-link a:hover { color: #ffffff; }                  
.cat-links {
color: #ffffff;
background-color: #669999;
position: relative;
white-space: nowrap;
padding: 0 0.25em 0 1.25em;
}
.cat-links a:link, a:visited { color: #ccffff; }    
.cat-links a:hover { color: #336666; }               
.cat-links:after {                               
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669999;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.cat-links:before {               
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}
.tags-links {
color: #ffffff;
background-color: #669966;
position: relative;
white-space: nowrap;
padding: 0 0.25em 0 1.25em;
}
.tags-links a:link, a:visited { color: #ccffcc; }  
.tags-links a:hover { color: #336633; }             
.tags-links:after {                             
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669966;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.tags-links:before {              
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}

And then there are some text specs in there that get defined the same way over and over in the colored boxes themselves, but that don’t get applied to the entire footer as a whole. (For one good example, general text in the footer is black, but text specifically within a colored box is white.) So we can move all of those to a new class just for defining a colored box, which we’ll call .footer-box (to remind us of what it’s defining), then apply those styles to each individual box with SASS’s @extend property:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
.footer-box {
color: #ffffff;
position: relative;
white-space: nowrap;
}
.posted-on {
@extend .footer-box;
background-color: #cc6633;
padding: 0 1.25em 0 1.25em;
}
.posted-on a:link, a:visited { color: #ffcc99; }     
.posted-on a:hover { color: #993300; }               
.comments-link {
@extend .footer-box;
background-color: #000000;
padding: 0 1.25em 0 1.25em;
}
.comments-link a:link, a:visited { color: #cccccc; }     
.comments-link a:hover { color: #ffffff; }             
.edits-link {
@extend .footer-box;
background-color: #cccccc;
padding: 0 1.25em 0 1.25em;
}
.edits-link a:link, a:visited { color: #000000; }       
.edits-link a:hover { color: #ffffff; }                 
.cat-links {
@extend .footer-box;
background-color: #669999;
padding: 0 0.25em 0 1.25em;
}
.cat-links a:link, a:visited { color: #ccffff; }   
.cat-links a:hover { color: #336666; }              
.cat-links:after {                             
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669999;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.cat-links:before {              
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}
.tags-links {
@extend .footer-box;
background-color: #669966;
padding: 0 0.25em 0 1.25em;
}
.tags-links a:link, a:visited { color: #ccffcc; } 
.tags-links a:hover { color: #336633; }            
.tags-links:after {                          
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669966;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.tags-links:before {            
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}

Next, let’s take advantage of SASS’s ability to “nest” related tags inside of each other; so in this case, for example, instead of the specs for all those a tags needing to be on their own separate lines, they can all be nested inside each individual class they’re a part of, consolidated even further by SASS’s ability to define pseudo-classes (like a:link, a:visited and a:hover) using the & character:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
.footer-box {
color: #ffffff;
position: relative;
white-space: nowrap;
}
.posted-on {
@extend .footer-box;
background-color: #cc6633;
padding: 0 1.25em 0 1.25em;
a {
&:link, &:visited { color: #ffcc99; }
&:hover { color: #993300; }
}

}
.comments-link {
@extend .footer-box;
background-color: #000000;
padding: 0 1.25em 0 1.25em;
a {
&:link, &:visited { color: #cccccc; }
&:hover { color: #ffffff; }
}

}
.edits-link {
@extend .footer-box;
background-color: #cccccc;
padding: 0 1.25em 0 1.25em;
a {
&:link, &:visited { color: #000000; }
&:hover { color: #ffffff; }
}

}
.cat-links {
@extend .footer-box;
background-color: #669999;
padding: 0 0.25em 0 1.25em;
a {
&:link, &:visited { color: #ccffff; }
&:hover { color: #336666; }
}

}
.cat-links:after {                              
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669999;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.cat-links:before {             
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}
.tags-links {
@extend .footer-box;
background-color: #669966;
padding: 0 0.25em 0 1.25em;
a {
&:link, &:visited { color: #ccffcc; }
&:hover { color: #336633; }
}

}
.tags-links:after {                           
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669966;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.tags-links:before {            
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}

Fantastic! This is already a tremendous amount of refactoring, and even if you stopped there you could be proud of yourself for all the wasteful duplicate code you’ve eliminated. If you look closely, though, you should notice something interesting about what’s left, which is that the lines remaining in each class definition are almost the same, but just slightly tweaked from one class to the next. For example, each has a background-color, but each background color is different, so that some of the boxes in the footer are red, some are blue, some are green, etc; and the padding-right value changes between boxes with arrows (0.25em) and boxes without arrows (1.25em), because otherwise half the boxes would look weird no matter which value you chose to apply to them all. This is a perfect opportunity to use SASS’s most powerful tool, @mixin, which is essentially the same thing as functions within OOP languages.

Intermediate refactoring: Mixins

A “function” in a programming language is simply a fancy term for “a set of instructions;” and what makes them powerful is that you can define them in a way so that you can input different specific values (called “arguments” in coding language) each time you call on that set of instructions. So for example, one of the most simple functions you can write is one that adds two numbers, which would look like the following in JavaScript:

function sum(a, b) {
return a + b;
}

Then when you call the function, adding whatever numbers you want, it might look something like this:

sum(2, 3);
==> 5
sum(6, 8);
==> 14

And so exactly it is with SASS mixins as well; you start by defining the name of the mixin, then define the arguments you’ll use in that mixin, then define the things that happen when that mixin is called. So if we wanted to redefine our .footer-box class as a mixin, so that we could enfold all the different background colors and padding values, we would take what’s currently there:

.footer-box {
color: #ffffff;
position: relative;
white-space: nowrap;
}

…and rewrite it thusly:

@mixin footer-box($color, $padding-right) {
color: #ffffff;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;

}

…and then change our @extend in the class definition to an @include, making sure to add the specific data for the arguments it’s expecting:

.posted-on {
@include footer-box(#cc6633, 1.25em);
a {
&:link, &:visited { color: #ffcc99; }
&:hover { color: #993300; }
}
}

And that’s not all — since each of our colored-box classes also have a-tag pseudo-classes (:link:visited and :hover) that are exactly alike, only with different colors in each, we can enfold that in our mixin as well, adding more arguments so that these too can be defined differently in each case:

@mixin footer-box($color, $link, $hover, $padding-right) {
color: #ffffff;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; }
}

}

…so that the entirety of the code within each colored-box class is now just a single line:

.posted-on {
@include footer-box(#cc6633, #ffcc99, #993300, 1.25em);
}

(And just so there’s no confusion, the variable names you use as arguments in the mixin can be anything — instead of $color, $link, $hover and $padding-right, I could’ve called them $a, $b, $c and $d, or even $mouse, $cat, $dog and $horse — but one of the other principles of agile development is to use names that give an inherent sense of what they’re defining, so that when you approach that code years later after you’ve forgotten everything you originally did, it will still make sense.)

At this point, then, here’s how our code is looking:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
@mixin footer-box($color, $link, $hover, $padding-right) {
color: #ffffff;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; }
}
}
.posted-on {
@include footer-box(#cc6633, #ffcc99, #993300, 1.25em);
}
.comments-link {
@include footer-box(#000000, #cccccc, #ffffff, 1.25em);
}
.edits-link {
@include footer-box(#cccccc, #000000, #ffffff, 1.25em);
}
.cat-links {
@include footer-box(#669999, #ccffff, #336666, 0.25em);
}
.cat-links:after {                            
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669999;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.cat-links:before {             
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}
.tags-links {
@include footer-box(#669966, #ccffcc, #336633, 1.25em);
}
.tags-links:after {                           
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #669966;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
z-index: 2;
}
.tags-links:before {           
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid #ffffff;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
margin-left: 1px;
z-index: 1;
}

Want to guess what’s next? That’s right, all that information in the triangle pseudo-classes can be moved into its own mixin as well, with three arguments needed (or that is, with only three values that change from one pseudo-class to the next) — the color, the z-index, and whether the margin-left is 0px or 1px. In the spirit of the Block-Element-Modifier (BEM) naming system (a whole can of worms we won’t get into today), we’ll call this mixin footer-box__arrow, since it is literally a modifier to the element footer-box (itself an element of the block entry-footer)*. Or in other words, footer-box__arrow will never be used in our code independent of footer-box, and the name helps us understand and remember that:

@mixin footer-box__arrow($color, $z-index, $margin-left) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
margin-left: $margin-left;
left: 100%;
z-index: $z-index;
}

[*Or if you prefer to think of it in terms of Brad Frost’s Atomic Design naming system: footer-box__arrow is an atom, footer-box is a molecule, and entry-footer is an organism. Potato, potahto.]

When adding the proper @include calls, that makes our code now look like this:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
@mixin footer-box($color, $link, $hover, $padding-right) {
color: #ffffff;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; }
}
}
@mixin footer-box__arrow($color, $z-index, $margin-left) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
margin-left: $margin-left;
left: 100%;
z-index: $z-index;
}
.posted-on {
@include footer-box(#cc6633, #ffcc99, #993300, 1.25em);
}
.comments-link {
@include footer-box(#000000, #cccccc, #ffffff, 1.25em);
}
.edits-link {
@include footer-box(#cccccc, #000000, #ffffff, 1.25em);
}
.cat-links {
@include footer-box(#669999, #ccffff, #336666, 0.25em);
}
.cat-links:after {
@include footer-box__arrow(#669999, 2, 0);
}
.cat-links:before {
@include footer-box__arrow(#ffffff, 1, 1);
}
.tags-links {
@include footer-box(#669966, #ccffcc, #336633, 1.25em);
}
.tags-links:after {
@include footer-box__arrow(#669966, 2, 0);
}
.tags-links:before {
@include footer-box__arrow(#ffffff, 1, 1);
}

And of course :after and :before are both pseudo-classes too, just like :link:hover and :visited, so in SASS can be nested within the original classes they modify:

.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase;
}
@mixin footer-box($color, $link, $hover, $padding-right) {
color: #ffffff;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; }
}
}
@mixin footer-box__arrow($color, $z-index, $margin-left) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
margin-left: $margin-left;
left: 100%;
z-index: $z-index;
}
.posted-on {
@include footer-box(#cc6633, #ffcc99, #993300, 1.25em);
}
.comments-link {
@include footer-box(#000000, #cccccc, #ffffff, 1.25em);
}
.edits-link {
@include footer-box(#cccccc, #000000, #ffffff, 1.25em);
}
.cat-links {
@include footer-box(#669999, #ccffff, #336666, 0.25em);
&:after {
@include footer-box__arrow(#669999, 2, 0);
}
&:before {
@include footer-box__arrow(#ffffff, 1, 1);
}

}
.tags-links {
@include footer-box(#669966, #ccffcc, #336633, 0.25em);
&:after {
@include footer-box__arrow(#669966, 2, 0);
}
&:before {
@include footer-box__arrow(#ffffff, 1, 1);
}

}

Wow, we did it! Not a single line of duplicated code left in our stylesheet! That’s as DRY as the Sahara desert, I’m tellin’ ya! So we’re done now, right?

No. We’re not done.

Advanced refactoring: Squeezing every last drop

One of the things I learned in DevBootcamp is that coders (or at least Ruby coders) love having little competitions to see who can write the most beautifully minimalist code possible; and even if you’re not into this for poetic reasons, minimalist code helps your app or website run faster, and it allows you to make changes after the fact with much less fuss and effort. For example, take all those hexadecimal color codes you’re seeing above, which is the specific color scheme I chose for the CCLaP website overhaul when first planning the project. But what if I have a change of heart next month and change the colors slightly? As currently written, I would have to go back into my project, track down every single instance of that hexadecimal code, then change them all by hand. Instead I could just assign those hexadecimal codes to variables using SASS:

$red:         #cc6633;
$red-light: #ffcc99;
$red-dark: #993300;
$blue: #669999;
$blue-light: #ccffff;
$blue-dark: #336666;
$green: #669966;
$green-light: #ccffcc;
$green-dark: #336633;

…and then use the variable names every time I want to call on that color, ensuring that those colors will get automatically updated every time I change just the variable definition itself (and providing the added bonus of making the code a lot more understandable in plain English):

.posted-on {
@include footer-box($red, $red-light, $red-dark, 1.25em);
}

(Me, a year later, when I’ve completely forgotten what I originally did, but have to go back into my code and change some stuff: “Okay, which colored box again does the .posted-on class affect? Oh, yeah, right, THE RED ONE!”)

Or even better, since SASS allows you to apply math operators to variables, I can define the light and dark shades of each color using a formula, thus automatically updating those too anytime I change the main color:

$red:         #cc6633;
$red-light: $red * 2;
$red-dark: $red / 2;
$blue: #669999;
$blue-light: $blue * 2;
$blue-dark: $blue / 2;
$green: #669966;
$green-light: $green * 2;
$green-dark: $green / 2;

It’s a small change but a very important one; because if, a month after your website goes live, you hear from a color-blind user complaining about how they can’t tell the difference between the blue and green you chose, this way you only have to change that value once in your entire project, versus possibly hundreds of times by hand if you hard-coded the value everywhere. That’s agile development in a nutshell: the constant effort to save yourself time and trouble as a programmer, thinking smartly ahead of time so that you don’t have to slog through a bunch of crap afterwards.

Or here’s another example: within your mixins, you can actually define a default value for your arguments if you want, which means that the argument automatically takes on that value if you don’t define it otherwise within an @include. So in our case, since all the colored boxes in our footer that have arrows use a padding-right value of 0.25em, we can define that as the default within the mixin:

@mixin footer-box($color, $link, $visited, $padding-right: 0.25em) {
...

…then in any of our classes that have arrows, we can leave that argument out altogether:

/* Before */
.cat-links {
@include footer-box($blue, $blue-light, $blue-dark, 0.25em);
...
/* After */
.cat-links {
@include footer-box($blue, $blue-light, $blue-dark);
...

Or for another example, since all the non-white triangles have a margin-left value of 0, we can make that the default value in our mixin too:

@mixin footer-box__arrow($color, $z-index, $margin-left: 0) {
...

…and then in all our :after pseudo-classes we can leave out defining the margin-left altogether:

/* Before */
.cat-links {
...
&:after { @include footer-box__arrow($blue, 2, 0); }
...
/* After */
.cat-links {
...
&:after { @include footer-box__arrow($blue, 2); }
...

Again, it might not seem like much; but believe me, after you’ve hard-coded that value hundreds of times in your project, then suddenly learn that your client wants that 0 changed to a 0.5, you’re going to kick yourself for not setting up these defaults in the first place.

So when all is said and done, then, here’s what the “final” version of our SASS code looks like, after all the refactoring:

$red:         #cc6633;
$red-light: $red * 2;
$red-dark: $red / 2;
$blue: #669999;
$blue-light: $blue * 2;
$blue-dark: $blue / 2;
$green: #669966;
$green-light: $green * 2;
$green-dark: $green / 2;
.entry-footer {
padding: 2em;
font: 400 1em/1.5em 'BenchNine', sans-serif;
text-transform: uppercase; }
@mixin footer-box($color, $link, $hover, $padding-right: 0.25em) {
color: white;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; } } }
@mixin footer-box__arrow($color, $z-index, $margin-left: 0) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
margin-left: $margin-left;
left: 100%;
z-index: $z-index; }
.posted-on { 
@include footer-box($red, $red-light, $red-dark, 1.25em); }
.comments-link { 
@include footer-box(black, gray, white, 1.25em); }
.edits-link { 
@include footer-box(gray, black, white, 1.25em); }
.cat-links {
@include footer-box($blue, $blue-light, $blue-dark);
&:after { @include footer-box__arrow($blue, 2); }
&:before { @include footer-box__arrow(white, 1, 1); } }
.tags-links {
@include footer-box($green, $green-light, $green-dark);
&:after { @include footer-box__arrow($green, 2); }
&:before { @include footer-box__arrow(white, 1, 1); } }

…or when compared side-by-side with the original version:

…with “final version” in quotes, of course, because refactoring is never a process that’s officially over; in fact, I’m willing to bet money that someone reading this right now can find even more ways to make this code tighter and more poetically minimalist. And in that spirit, please remember that I too am only a beginner developer; so if you see anything flat-out wrong in this writeup, or can think of a way to better word something I talk about, I strongly encourage you to either leave a comment or drop me a line at ilikejason@gmail.com. I hope this extra-long tutorial was of help to those like me finding it frustrating to build their own breadcrumb-style lists, or who are struggling with the overwhelming amount of possibilities in SASS. Happy coding!

UPDATE: Ha ha, ten minutes after posting this and I’ve already discovered yet another refactor! See, what did I tell you? Namely, since defining the z-index and left-margin of the :before and :after pseudo-classes of the colored boxes doesn’t actually affect the boxes without arrows, you can move all that into the general footer-box mixin:

@mixin footer-box($color, $link, $hover, $padding-right: 0.25em) {
color: white;
position: relative;
white-space: nowrap;
background-color: $color;
padding: 0 $padding-right 0 1.25em;
&:after { z-index: 2; }
&:before { z-index: 1; margin-left: 1; }

a {
&:link, &:visited { color: $link; }
&:hover { color: $hover; }
}
}

…and thus remove it from the footer-box__arrow mixin:

@mixin footer-box__arrow($color) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%;
}

…which means that the only thing you now have to define when calling on this mixin is the color itself:

.cat-links {
@include footer-box($blue, $blue-light, $blue-dark);
&:after { @include footer-box__arrow($blue); }
&:before { @include footer-box__arrow(white); }
}

And this actually provides a good excuse to talk about yet another adage in agile development: that sometimes efficient code is not as valuable as easily explainable code; and that there are occasionally moments when you can shorten code but might not want to, because the lengthier code is a lot clearer to someone seeing it for the first time. To be specific, technically you could refactor the above mixin even more, by making white the default color for the arrows:

@mixin footer-box__arrow($color: white) {
content: " ";
width: 0;
height: 0;
border-top: 0.7em solid transparent;
border-bottom: 0.7em solid transparent;
border-left: 0.7em solid $color;
position: absolute;
top: 50%;
margin-top: -0.7em;
left: 100%; }

…that way when you call on this mixin, you could leave out the “white” declaration (and accompanying parentheses) in the :before pseudo-class altogether:

.cat-links {
@include footer-box($blue, $blue-light, $blue-dark);
&:after { @include footer-box__arrow($blue); }
&:before { @include footer-box__arrow; } }

I don’t actually prefer this, though; because if some other coder came across this later in life (or if I myself revisit the code years later), I like how having a color declared in every single @include makes it clear that this is how you define the arrow’s color, a fact that’s not as clear when only half the @includes mention a color and the other half don’t. This is one of the core principles behind agile programming — that it’s humans writing all this code that makes computers work, and that it’s humans we have to keep in mind when making this code as useful as possible — a balance between efficiency and readability that all of us need to find as conscientious coders.

Show your support

Clapping shows how much you appreciated Jason Pettus’s story.