A Beginner’s Guide to Leaner CSS

Don’t repeat yourself (DRY). It’s a simple concept with the ability to completely change the way you write code. If I look back to my early days with CSS though, I don’t think the concept meant much to me. Sure, all right, don’t repeat myself, thanks for the advice. How does that translate to applicable advice though?

Today we’re going to look at the very core concepts for how to rethink the CSS you’ve been writing by trimming the fat and reducing the redundancy. The result will be leaner CSS that’s easier to author and maintain.

Where I Think You Are

This article isn’t for the guy who spends his days trolling through web development blogs for a chance to leave comments and show off how much more he knows that everyone else. If you’re already a CSS genius, go write a book.

Instead, I’m writing this for the masses of developers who are finally beginning to get the hang of this CSS thing. You’ve been styling websites for a year, maybe two and you’re ready to take a step beyond how to write effective code and learn how to write efficient code.

“You’re ready to take a step beyond how to write effective code and learn how to write efficient code.”

Just like me at this point in my career, you’ve heard the jargon plenty of times but have yet to experience that “click” where suddenly writing efficient CSS makes sense. Hopefully, we’ll find that click today.

Sass @extend

Don’t freak out. We’re going to talk about a preprocessor. Don’t worry, I’m not going to demand that you use one or turn this entire piece into a Sass tutorial rather than a CSS tutorial. We’re going to learn how to write plain old CSS in a more efficient way.

“Don’t freak out. We’re going to talk about a preprocessor.”

That being said, Sass, for all of its complexities, actually utilizes selector inheritance in a much easier manner to wrap your mind around than CSS. It’s a very straightforward and linear process that makes the concept of not repeating yourself incredibly easy to grasp. Let’s take a look.

Three Boxes

To see how this works, imagine that you had three very similar but still distinct classes. Here’s an example with the classes “box,” “bigbox,” and “smallbox” (hopefully your class names are a little more specific).

.box { height: 100px; width: 100px; border-radius: 10px; border: 1px solid #222; background: #eee; } .bigbox { height: 200px; width: 200px; border-radius: 10px; border: 1px solid #222; background: #eee; } .smallbox { height: 50px; width: 50px; border-radius: 10px; border: 1px solid #222; background: #eee; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .box { height : 100px ; width : 100px ; border-radius : 10px ; border : 1px solid #222 ; background : #eee ; } .bigbox { height : 200px ; width : 200px ; border-radius : 10px ; border : 1px solid #222 ; background : #eee ; } .smallbox { height : 50px ; width : 50px ; border-radius : 10px ; border : 1px solid #222 ; background : #eee ; }

As you can see, all three of the boxes require the same basic styles, but the size for each is different so we do in fact require three separate classes. Still, all this redundancy is no good.

So how do we DRY this up? That’s where the Sass @extend feature comes in. Check it out:

.box { height: 100px; width: 100px; border-radius: 10px; border: 1px solid #222; background: #eee; } .bigbox { @extend .box; height: 200px; width: 200px; } .smallbox { @extend .box; height: 50px; width: 50px; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 .box { height : 100px ; width : 100px ; border-radius : 10px ; border : 1px solid #222 ; background : #eee ; } .bigbox { @extend .box; height : 200px ; width : 200px ; } .smallbox { @extend .box; height : 50px ; width : 50px ; }

See what happened here? Even if you know nothing of Sass, this code is pretty easy to understand. Essentially, rather than writing the same properties over and over again with the same values, we can use @extend to say “Hey, just grab the code from the box class.” We then go on and only define the properties that require changing.

The resulting code is shorter and more concise while not sacrificing readability. Unfortunately, this is Sass and the @extend feature doesn’t exist in plain old CSS.

Learning From the Sass Output

So what was the point of the big Sass lesson if we can’t use @extend in CSS? The thing to remember is that the ultimate product of Sass, LESS and any other preprocessor is plain Jane CSS. So if Sass is indeed working any magic in the actual output, we should be able to spot it.

Let’s see how it handled turning those @extends into CSS:

.box, .bigbox, .smallbox { height: 100px; width: 100px; border-radius: 10px; border: 1px solid #222; background: #eee; } .bigbox { height: 200px; width: 200px; } .smallbox { height: 50px; width: 50px; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .box, .bigbox, .smallbox { height : 100px ; width : 100px ; border-radius : 10px ; border : 1px solid #222 ; background : #eee ; } .bigbox { height : 200px ; width : 200px ; } .smallbox { height : 50px ; width : 50px ; }

Aha! This code is much more concise than our original concept and yet it’s still pure CSS. The @extend feature is actually smart enough to output lean CSS through the use of grouped selectors, which are separated by commas.

Through @extend, we pointed Sass to the redundancies in our code and it thew them all into a single rule. After this, we defined the other classes using only the code that needed to be differentiated.

Skipping Sass

The great lesson to learn here is that you don’t need a preprocessor to do all of this work for you. You can skip Sass altogether and simply write clean, concise code in the first place (though I personally still think Sass is the bees knees). It may not be as intuitive, but once you get the hang of spotting redundancy, it becomes a pretty easy task.

The important lesson for me was that I might not necessarily realize that I’m coding sloppily as I’m experimenting with different ideas in my code, and that’s ok! Coding like this from the start is something that comes with experience. In the mean time, just be sure to get one simple principle into your head: you’re not finished simply because everything works!

“Get one simple principle into your head: you’re not finished simply because everything works!”

You might fiddle around for hours with a design or funky layout, and when everything finally falls into place you heave a sigh of relief and move on with life. But you’re not done! The step that you should be taking next is to examine all of that code that you wrote in frustration and figure out how to simplify it.

A Real Example

To see what this process looks like, let’s take a look at a real life example that I coded up for a previous tutorial. Basically, the effect that I was going for is a stack of cards.

To pull it off, I created one element with some basic styling, then used :before and :after to duplicate the element. The result was a stack of three cards from a single class. Here’s the chunk of code that resulted:

.hireme { margin: 40px; height: 100px; width: 250px; background: #ccc; border-radius: 10px; position: relative; -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.5); box-shadow: 3px 3px 5px rgba(0,0,0,0.5); background-image: linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -o-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -moz-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -ms-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(217,217,217)), color-stop(0.7, rgb(245,245,245))); } .hireme:after { content: ""; height: 100px; width: 250px; position: absolute; border-radius: 10px; top: 0px; left: 4px; z-index: -1; -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.5); box-shadow: 3px 3px 5px rgba(0,0,0,0.5); -webkit-transform: rotate(4deg) ; -moz-transform: rotate(4deg) ; -o-transform: rotate(4deg) ; background-image: linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -o-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -moz-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -ms-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(217,217,217)), color-stop(0.7, rgb(245,245,245))); } .hireme:before { content: ""; height: 100px; width: 250px; position: absolute; border-radius: 10px; top: 0px; left: 10px; z-index: -2; -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.5); box-shadow: 3px 3px 5px rgba(0,0,0,0.5); -webkit-transform: rotate(8deg) ; -moz-transform: rotate(8deg) ; -o-transform: rotate(8deg) ; background-image: linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -o-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -moz-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -ms-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(217,217,217)), color-stop(0.7, rgb(245,245,245))); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 .hireme { margin : 40px ; height : 100px ; width : 250px ; background : #ccc ; border-radius : 10px ; position : relative ; -webkit-box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; background-image : linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -o-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -moz-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -ms-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-gradient ( linear, left bottom, left top, color-stop ( 0.4, rgb ( 217,217,217 ) ) , color-stop ( 0.7, rgb ( 245,245,245 ) ) ) ; } .hireme:after { content : "" ; height : 100px ; width : 250px ; position : absolute ; border-radius : 10px ; top : 0px ; left : 4px ; z-index : -1 ; -webkit-box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; -webkit-transform : rotate ( 4deg ) ; -moz-transform : rotate ( 4deg ) ; -o-transform : rotate ( 4deg ) ; background-image : linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -o-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -moz-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -ms-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-gradient ( linear, left bottom, left top, color-stop ( 0.4, rgb ( 217,217,217 ) ) , color-stop ( 0.7, rgb ( 245,245,245 ) ) ) ; } .hireme:before { content : "" ; height : 100px ; width : 250px ; position : absolute ; border-radius : 10px ; top : 0px ; left : 10px ; z-index : -2 ; -webkit-box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; -webkit-transform : rotate ( 8deg ) ; -moz-transform : rotate ( 8deg ) ; -o-transform : rotate ( 8deg ) ; background-image : linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -o-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -moz-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -ms-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-gradient ( linear, left bottom, left top, color-stop ( 0.4, rgb ( 217,217,217 ) ) , color-stop ( 0.7, rgb ( 245,245,245 ) ) ) ; }

I run into this problem all the time when using :before and :after. I build each element individually, take a step back, then realize that I’ve basically just written the same code three different times!

All we have to do to clean this up is something that any school child could do: spot the repetition. In the code above, each card has the same height, width, shadow, gradient and border-radius. All of this code should be written only once. We can group this together like so:

.hireme, .hireme:before, .hireme:after { height: 100px; width: 250px; background: #ccc; border-radius: 10px; -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.5); box-shadow: 3px 3px 5px rgba(0,0,0,0.5); background-image: linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -o-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -moz-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -ms-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(217,217,217)), color-stop(0.7, rgb(245,245,245))); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 .hireme, .hireme:before, .hireme:after { height : 100px ; width : 250px ; background : #ccc ; border-radius : 10px ; -webkit-box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; background-image : linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -o-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -moz-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -ms-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-gradient ( linear, left bottom, left top, color-stop ( 0.4, rgb ( 217,217,217 ) ) , color-stop ( 0.7, rgb ( 245,245,245 ) ) ) ; }

This takes care of the properties and values that repeat across all three cards, but there’s also some redundancy that exists only between the pseudo elements. Let’s take care of this next.

.hireme:before, .hireme:after { content: ""; position: absolute; top: 0px; } 1 2 3 4 5 .hireme:before, .hireme:after { content : "" ; position : absolute ; top : 0px ; }

Now that we’ve grouped all of this content together, we can remove it from the other sections where it was repeated. Here’s the complete, trimmed block of code:

.hireme { margin: 40px; position: relative; } .hireme, .hireme:before, .hireme:after { height: 100px; width: 250px; background: #ccc; border-radius: 10px; -webkit-box-shadow: 3px 3px 5px rgba(0,0,0,0.5); box-shadow: 3px 3px 5px rgba(0,0,0,0.5); background-image: linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -o-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -moz-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -ms-linear-gradient(bottom, rgb(217,217,217) 40%, rgb(245,245,245) 70%); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(217,217,217)), color-stop(0.7, rgb(245,245,245))); } .hireme:before, .hireme:after { content: ""; position: absolute; top: 0px; } .hireme:before { left: 10px; z-index: -2; -webkit-transform: rotate(8deg) ; -moz-transform: rotate(8deg) ; -o-transform: rotate(8deg) ; } .hireme:after { left: 4px; z-index: -1; -webkit-transform: rotate(4deg) ; -moz-transform: rotate(4deg) ; -o-transform: rotate(4deg) ; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 .hireme { margin : 40px ; position : relative ; } .hireme, .hireme:before, .hireme:after { height : 100px ; width : 250px ; background : #ccc ; border-radius : 10px ; -webkit-box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; box-shadow : 3px 3px 5px rgba ( 0,0,0,0.5 ) ; background-image : linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -o-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -moz-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -ms-linear-gradient ( bottom, rgb ( 217,217,217 ) 40%, rgb ( 245,245,245 ) 70% ) ; background-image : -webkit-gradient ( linear, left bottom, left top, color-stop ( 0.4, rgb ( 217,217,217 ) ) , color-stop ( 0.7, rgb ( 245,245,245 ) ) ) ; } .hireme:before, .hireme:after { content : "" ; position : absolute ; top : 0px ; } .hireme:before { left : 10px ; z-index : -2 ; -webkit-transform : rotate ( 8deg ) ; -moz-transform : rotate ( 8deg ) ; -o-transform : rotate ( 8deg ) ; } .hireme:after { left : 4px ; z-index : -1 ; -webkit-transform : rotate ( 4deg ) ; -moz-transform : rotate ( 4deg ) ; -o-transform : rotate ( 4deg ) ; }

As you can see, we’ve cut out nearly twenty lines of code in one little example! Think about how much impact these principles have over the course of an entire website.

Easy Maintenance

Too often, principles like those we’re learning today are presented as rules to be followed simply because that’s the way you should be doing it. For most people, this isn’t really a convincing argument. Who cares about common practices and the greater good? What’s in it for me?

“You’re saving yourself a lot of time and trouble by using these techniques.”

The good news here is that leaner CSS is vastly easier to maintain and sift through in the long run. You’re saving yourself a lot of time and trouble by using these techniques. For instance, imagine you had written the original code from the example above, then decided that you wanted to change the gradient colors. You’d have to go through and update eighteen lines of code for this one simple change!

Now imagine you had to do the same on our concise example, here you only have to update six lines of code. Granted, the gradient syntax and browser prefix situation still sucks heavily, but six is better than eighteen.

Rethinking Your Code Structure

Before we wrap this up, it’s worth discussing that there are tons of ways to make your CSS leaner. This is a broad category of discussion that’s definitely not limited to the techniques above.

For instance, many coders have developed systems whereby you intentionally separate different types of code if your CSS in the name of efficiency. One way to do this in our original box example might be to separate the arbitrary visual style from the structural CSS. Following this route, our code would look more like this:

.box { border-radius: 10px; border: 1px solid #222; background: #eee; } .smbox { width: 50px; height: 50px; } .medbox { width: 100px; height: 100px; } .lgbox { width: 200px; height: 200px; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .box { border-radius : 10px ; border : 1px solid #222 ; background : #eee ; } .smbox { width : 50px ; height : 50px ; } .medbox { width : 100px ; height : 100px ; } .lgbox { width : 200px ; height : 200px ; }

Here we’ve developed a base “box” class that would be applied to several HTML elements, each of which would also then get paired with a structural class that defines the shape, positioning, stack order, etc. of the box. Pretty much anything that isn’t extra eye candy.

Obviously, the tradeoff here is going to be a little extra markup. You’ll find that coders fiercely differ on whether or not it’s acceptable to add even a single character of extra markup in the name of more efficient CSS.

Popular Schools of Thought

Currently, there are two very popular ideas or methods in this vein of rethinking how we structure our stylesheets: OOCSS (Object Oriented CSS) and SMACSS (Scalable and Modular Architecture for CSS).

Though effectively quite distinct, these methodologies are quite conceptually similar and both seek to help you create more efficient, maintainable and even reusable CSS. I highly recommend that you give both a closer look to see what you think.

How Do You Write Lean CSS?

In the examples above, we learned all about how Sass uses a magical feature called @extend to help you write leaner code with very little effort and how we can use the principles at work in that feature to perform these feats using plain old CSS. We also briefly discussed the idea of completely reevaluating your code structure in order to create something that’s more modular and maintainable.

Leave a comment below and let us know what you think of these methods. Do you use Sass? How often do you group selectors to reduce redundancy? What do you think of OOCSS and SMACSS? Which is better?