Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Getting DRY With Sass

0.00/5 (No votes)
8 Jun 2012CPOL5 min read 5.9K  
Getting DRY With Sass

I really like Sass. Like LESS, Sass is a language / framework which compiles down to CSS and, as its creators so accurately describe, “makes CSS fun again”. Indeed.

The thing I like most about Sass is how it helps you apply the DRY principle to your style sheets. If you use it well, you can drastically cut down “copy-and-paste” code and actually get reasonably maintainable CSS to work with.

So, how does Sass help your CSS get and stay DRY? Let’s go to the videotape.

Define Your Hierarchy

Actually, before getting into the whole DRY thing, let’s first use Sass to get organized.

I always found it really painful to organize large CSS files in a way that makes sense. Do I group by area of the page? Do I separate things into element-based, id-based, and class-based styles? What happens if I try to use the same style rules for different elements? Inevitably, I would get something like this:

CSS
/* common */
body {font-family: 'Armata', sans-serif; font-size: 1em; 
color: #3F3E3A; background-image:url(images/background.png);}
article p, article ul, article ol, .comment-body p {font-family:"merriweather", serif;}
a {text-decoration: none; font-weight: normal; color: #43B1E1; }
p, li {line-height: 1.7em; margin: 0 0 10px 0; font-size: 1em;}
ul, ol {padding-bottom: 20px; padding-left: 20px;}
h1 a {color: #006794; font-size: 1em; line-height: 1.4em;}
h2, h3, h4 {color: #3283A6; font-size: 1.1em; padding: 20px 0 10px 0; 
line-height: 1.5em; font-weight: normal;}
h3, h4 {font-size: 1em; padding: 10px 0 5px 0;}

/* header */
#main-header {padding: 30px; background-color: white; border-top: 5px solid #B3B3B3;}
#header-container {width: 920px; margin: auto;}
#blog-title {font-size: 2em; padding-right: 10px; margin-left: 40px; border-right: 1px solid #B3B3B3; }
#blog-title a {color: #323036;}
...

While not terrible, this is pretty noisy and hard to modify, especially if you’re sharing styling rules.

How would I make it better? Well, to me, the most logical way to organize a CSS file is to make it follow the same hierarchy as the HTML it styles. Let’s look at an example. Suppose you had to style the following HTML:

HTML
<header>
	<h1>Welcome to my site</h1>
</header>
<nav>
	<ul>
		<li>page 1</li>
		<li>page 2</li>
	</ul>
</nav>
<article>
	<h2>Lorem ipsum dolor sit amet</h2>
	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit....</p>

	<h3>Lorem ipsum dolor sit amet</h3>
	<p>Vestibulum ante ipsum primis in faucibus orci luctus et...</p>

	<aside>
		<h2>did you know?</h2>
		<p>Duis egestas ornare justo eget iaculis....</p>
		<ul>
			<li>important point 1</li>
			<li>important point 2</li>
		</ul>
	</aside>
</article>
<footer>
	<section>
		<h2>Archives</h2>
		<ul>
			<li>April 2012 (5)</li>
			<li>March 2012 (6)</li>
			<li>February 2012 (9)</li>
			<li>January 2012 (4)</li>
		</ul>
	</section>
</footer>

The markup above is semantically sound and follows a logical hierarchy: there is a header, a nav bar, an article, a footer, and so on. Wouldn’t it be great if you could organize the style sheet in a similar fashion?

Unfortunately, this is impossible to do with pure CSS because you can’t nest rules. Fortunately, it’s no problem with Sass:

sass
body {
	header {
		h1 {
		}
	}

	nav {
		ul {
			li {
			}
		}
	}

	article {
		h2 {
		}

		h3 {
		}

		p {
		}

		aside {
		}
	}

	footer {
		section {
			ul {
				li {
				}
			}
		}
	}
}

By the way, though I haven’t defined any actual rules yet, the Sass code above is perfectly valid.

A Note About Performance

It’s true that deep nesting of rules negatively impacts CSS rendering performance. For instance, a rule like body footer selection ul li {...} will get processed slower by the browser than a rule like .menu-option {...}. Furthermore, using generic selectors like body is slower than using specific selectors like .post-headline or, even better, #post-content.

I should note that you can address some potential performance issues with additional markup. For example, you can tag HTML elements with ids and classes and then use them in your Sass:

XML
<nav>
	<ul id="site-menu">
		<li class="menu-option">page 1</li>
		<li class="menu-option">page 2</li>
	</ul>
</nav>
sass
#site-menu {
	.menu-option{
		...
	}
}

This trick might be especially useful for “leaf” elements because CSS is evaluated right to left by the browser.

So, the bottom line here is this: as is typical in software development, using certain approaches can mean a choice of maintainability over performance. Whether that trade-off is justified is up to you to determine.

On a separate note, there is a whole science on how to optimize your CSS performance. Even if you don’t have performance issues with page rendering, it may still be worth investigating.

Define Your Levers

Once your hierarchy is laid out, you can finally focus on getting DRY. A key enabler of reuse in Sass is variables. For example, I really like to define a small set of “site control” variables for layout, color, and fonts:

sass
$base-font-size: 16px;
$body-width: 90%;

$text-color: #44474F;
$main-color: #C46D3B;
$accent-color: #9C9985;

$body-font: 'Arial';
$heading-font: 'Georgia';

From then on, I only use the variables (or derivations of thereof) to specify actual styling rules:

sass
h1 {
	font-size: $base-font-size * 1.8;
	font-family: $heading-font;
	color: lighten($main-color, 10%);
	border: 1px solid lighten($text-color, 40%);
}

This is helpful for a number of reasons, but the biggest one is that I can change a whole bunch of aspects of how the page looks by just tweaking a single parameter. Awesome.

Mix in Some Reuse

In addition to variables, Sass lets you define reusable chunks of rules called mixins.

For example, I sometimes need to create a container which is centered inside its parent and automatically expands to contain any floated children. However, because I may use it in different parts of the page, other aspects like width and gutter size (clearance on left and right) might change.

So, I can define a @mixin for such a container and then @include it wherever I need it, specifying parameters which differ:

sass
/* define mixin */
@mixin centered-container ($width, $gutter) {
	margin: auto;
	overflow: hidden;
	width: $width;
	padding-left: $gutter;
	padding-right: $gutter;
}

/* use mixin */
header {
	@include centered-container(90%, 5%);
}

Modularize Your Code

One of the big problems with CSS is a lack of modularization. Yet, whether you have chunks of CSS you’d like to reuse between projects or because you’d like to break up your project’s CSS into chunks, it’s good to have modularity.

Sass addresses this with the @import feature. Just as the name implies, you can import styles defined in different files into your main stylesheet. And the best part is that import directives get processed at compile time. This means that you still get only 1 CSS file to include in your page, saving precious round trips to the server (By the way, this feature is the main reason I prefer Sass over LESS).

What About Compass?

Compass is a framework built on top of Sass which does a lot of stuff. It takes the ideas discussed here (along with many others) to a whole other level of sophistication (and perhaps complexity). I personally find Compass to be a bit of overkill for smaller projects and haven’t really used it for larger ones. That said, it may very well be that the additional complexity Compass brings is justified if you have large teams and/or code base.

You May Also Like


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)