Publishers of technology books, eBooks, and videos for creative people

Home > Articles > Web Design & Development > Usability

Web Design Reference Guide

Hosted by

Templating Solutions with CSS: Style Organization Designed To Last

Last updated Oct 17, 2003.

By Dave Shea, co-author of The Zen of CSS Design: Visual Enlightenment for the Web

So you figured out how to build CSS-based layouts. And then you watched your CSS files grow and evolve over the lifecycle of your site. But the cruft is really starting to add up, and making simple changes is more difficult than it should be. Going into that CSS file feels a little like playing the game Jenga: You might get lucky and manage to grab one of the bottom blocks without toppling the tower, but there's always a chance that your next move will bring the whole thing crashing down.

Enter this series of articles. I've struggled with this issue just like anyone, and I'll be outlining the templating system I use that helps keep me in control of my code. This is meant to serve as a starting point for your own system, rather than an authoritative final word on the subject.

In this first article, I'll outline some basic long-term CSS management strategies that will set the stage for what's to come.

Name by Function, Not by Presentation

Of course, CSS and markup have a direct relationship; the former styles the latter, so it requires HTML class and id attributes that it can hook into. The classes and ids themselves can be anything you want, but it helps to have a logical naming convention so you don't have to second-guess yourself later.

There's no real standard to class and id naming. Common wisdom says it's wise to avoid presentational names, as in the following example:

<div class="red-left-sidebar">

<p id="greytext">

Instead, describe elements by function:

<div class="navigation">

<p id="legal">

If you later change the color of your red-left-sidebar to, say, blue, it's going to make debugging much more difficult than it needs to be.

A counterpoint to this argument exists, however: It's possible that the function of an element will change, and then you run into the same problem. The only true way to avoid conflicts in either case is to use a database-like unique identifier for every single element on the page, but then you create a whole new problem for yourself when managing these identifiers, so I wouldn't recommend that solution. It's generally true that function will change less frequently than presentation, so common sense says that naming based on function is probably good enough in most instances.

Name Consistently

I could share my naming convention, but it's rather arbitrary and likely only relevant to me. What's more important is that you develop a system that makes logical sense to you and your team. More relevant than what you choose to name your class and id attributes is how consistently they're used. If you build multiple sites for many different clients with a large team, a naming convention is not a bad idea to minimize guesswork as to what an element is meant to do, and what's styling it.

Even if you're a one-man or one-woman shop building for a handful of clients, naming conventions are still a good idea. Sites usually feature contact forms, for example. There are only so many ways a contact form can be laid out, so if you consistently name the form elements and surrounding divs across multiple sites, you can reuse and adapt the CSS that establishes the basic layout, instead of creating it from scratch each time.

Move Your CSS to External Files

The benefits of CSS come mainly from the fact that CSS can be moved outside of the HTML, into external files. These files are cached, meaning that your browser will only download them on the first visit to the site, which can potentially halve your site's bandwidth. And no one says you have to stop at a single file, which brings us to our next point.

Consider Global and Local Files

If you've done much programming, you'll be familiar with the concept of variable scope. Similarly, with external CSS files you can create a system of global and localized style, to separate generic site-wide styling from individual sections that may need extra customization.

This idea is particularly effective when you consider the number of templates an average web site might require. Let's say you need customized layouts for your home page, your individual article pages, your search results, and your contact form. That makes four templates in total, but does the user's browser really need CSS for the search results when he initially loads the home page?

Consider creating a base style sheet for elements that all pages share in common—things like navigation and paragraph formatting—but then moving section-specific style out to different style sheets.

Here's an example. Listing 1, default.css, is an excerpt of the large style sheet on mezzoblue.com. The full CSS file contains styles for absolutely everything on the site, and weighs in at 20KB (see Example 1).

Listing 1 default.css

/* -- Basic HTML Elements -- */
a:link {
    color: #0088FF;
    text-decoration: none;
}
a:visited {
    color: #005387;
    text-decoration: none;
}
a:hover {
    color: #fff;
    background-color: #0088ff;
    text-decoration: none;
}
abbr, acronym {
    font-style: normal;
    border-bottom: dotted 1px #ABB1B7;
    cursor: help;
}
body {
    color: #3A4956;
    background: #52A5F2 url(/i/ice/container-bg-nosidebar.gif) center top repeat-y;
    font: 12px/19px "Lucida Grande", "Lucida Sans Unicode", Verdana, Tahoma, Arial, sans-serif;
    margin: 0;
    padding: 0;
    text-align: center;
    min-width: 730px;
}
code, var, kbd {
    font-family: monaco, courier, monospace;
    color: #B96A0E;
}
dd {
    margin: 0;
    padding: 0 0 1em 17px;
}
dt {
    font-weight: bold;
    padding: 0 0 0 17px;
    background: url(/i/ice/deco-dt.gif) 2px 4px no-repeat;
}
form {
    margin: 0;
}
h1 {
    font: bold 24px Helvetica, Arial, sans-serif;
    color: #E62C00;
    letter-spacing: -1px;
    margin: 0;
}
h2 {
    color: #EB694A;
    font: normal 18px Helvetica, Arial, sans-serif;
    letter-spacing: -1px;
    margin: 2em 0 0 0;
}
h3 {
    font-size: 12px;
    color: #6F90B5;
}
h4 {
    font-size: 12px;
}
hr {
    border: none;
    height: 1px;
    background: #ccc;
    margin: 2em 0;
}
img {
    border: 0;
    padding: 0;
}
ins {
    text-decoration: none;
    color: #EC694A;
}
pre {
    width: 580px;
}
table {
    margin: 2em 0;
}
table caption {
    color: #fff;
    background: #98A0A6;
}
th {
    text-align: left;
    background: #C7CBCE;
}




/* -- article styling -- */
.entry .lead {
    color: #7A9EC5;
    float: right;
    width: 265px;
    font-size: 17px;
    line-height: 24px;
    text-align: left;
    margin: 0 0 1em 0;
    position: relative;
    left: 35px;
}
html>body .entry .divider {
    position: relative;
    left: -57px;
    margin: 2em -57px;
    border: 0;
    height: 1px;
    background: #ccc;
}

.archiveBrowser {
    line-height: 42px;
    height: 42px;
    background: url(/i/ice/archivebrowser-bg.gif) repeat-x;
    padding: 0 10px 0 0;
    position: relative;
    left: -58px;
    width: 457px;
}
#mainContent .archiveBrowser ul {
    margin: 0;
    padding: 0;
}
#mainContent .archiveBrowser li {
    display: inline;
    background: none;
}
#mainContent .archiveBrowser a:link, #mainContent .archiveBrowser a:visited {
    color: #fff;
    border: 0;
}
#mainContent .next {
    float: right;
}

.metadata {
    margin: 30px 0;
    padding: 0 10px;
    background: #eee;
    border: solid 1px #ccc;
}
.metadata h2 {
    margin: 10px 0 0 0;
}


/* -- replies -- */
#mainContent p.reply {
    color: #9FB6CA;
    font-size: 10px;
    line-height: 1;
    padding: 0 0 2em 10px;
    background: url(/i/ice/deco-reply.gif) 0 0 no-repeat;
}
.entry #mainContent p.reply {
    padding-bottom: 1em;
}
div.reply {
    position: relative;
    margin: 0 40px 3em 40px;
}
div.reply .replynumber {
    position: absolute;
    font-size: 22px;
    top: 2px;
    left: -38px;
    letter-spacing: -2px;
}
div.reply .replynumber a:link, div.reply .replynumber a:visited {
    color: #ccc;
}
div.reply .replynumber a:hover {
    color: #fff;
}
.reply-body {
    width: 500px;
    overflow: hidden;
}
.dave {
    background: #F4CFC6;
}
.dave p {
    padding: 0 5px;
}
.entry #mainContent p.postedBy {
    font-weight: bold;
    margin-bottom: 0;
}
.entry #mainContent p.posttimestamp {
    position: absolute;
    top: -1em;
    right: 0;
    color: #999;
}
.dave .postedBy {
    background: #E62E00;
    color: #fff;
    padding: 3px;
}
.dave .postedBy a {
    color: #fff;
}
.entry #mainContent .dave p.posttimestamp {
    top: -0.8em;
    color: #fff;
}
.entry #mainContent .dave p.posttimestamp a:link, .entry #mainContent .dave p.posttimestamp a:visited {
    color: #fff;
}

Listing 2, default-trimmed.css, is a trimmed-down version that excludes the formatting for individual articles and comments (see Example 2). Many non-article pages don't require this particular style, so, on those pages and those pages alone, we include Listing 3, contentformatting.css, which contains that formatting (see Example 3).

Listing 2 default-trimmed.css

/* -- Basic HTML Elements -- */
a:link {
    color: #0088FF;
    text-decoration: none;
}
a:visited {
    color: #005387;
    text-decoration: none;
}
a:hover {
    color: #fff;
    background-color: #0088ff;
    text-decoration: none;
}
abbr, acronym {
    font-style: normal;
    border-bottom: dotted 1px #ABB1B7;
    cursor: help;
}
body {
    color: #3A4956;
    background: #52A5F2 url(/i/ice/container-bg-nosidebar.gif) center top repeat-y;
    font: 12px/19px "Lucida Grande", "Lucida Sans Unicode", Verdana, Tahoma, Arial, sans-serif;
    margin: 0;
    padding: 0;
    text-align: center;
    min-width: 730px;
}
code, var, kbd {
    font-family: monaco, courier, monospace;
    color: #B96A0E;
}
dd {
    margin: 0;
    padding: 0 0 1em 17px;
}
dt {
    font-weight: bold;
    padding: 0 0 0 17px;
    background: url(/i/ice/deco-dt.gif) 2px 4px no-repeat;
}
form {
    margin: 0;
}
h1 {
    font: bold 24px Helvetica, Arial, sans-serif;
    color: #E62C00;
    letter-spacing: -1px;
    margin: 0;
}
h2 {
    color: #EB694A;
    font: normal 18px Helvetica, Arial, sans-serif;
    letter-spacing: -1px;
    margin: 2em 0 0 0;
}
h3 {
    font-size: 12px;
    color: #6F90B5;
}
h4 {
    font-size: 12px;
}
hr {
    border: none;
    height: 1px;
    background: #ccc;
    margin: 2em 0;
}
img {
    border: 0;
    padding: 0;
}
ins {
    text-decoration: none;
    color: #EC694A;
}
pre {
    width: 580px;
}
table {
    margin: 2em 0;
}
table caption {
    color: #fff;
    background: #98A0A6;
}
th {
    text-align: left;
    background: #C7CBCE;
}

Listing 3 contentformatting.css

/* -- article styling -- */
.entry .lead {
    color: #7A9EC5;
    float: right;
    width: 265px;
    font-size: 17px;
    line-height: 24px;
    text-align: left;
    margin: 0 0 1em 0;
    position: relative;
    left: 35px;
}
html>body .entry .divider {
    position: relative;
    left: -57px;
    margin: 2em -57px;
    border: 0;
    height: 1px;
    background: #ccc;
}

.archiveBrowser {
    line-height: 42px;
    height: 42px;
    background: url(/i/ice/archivebrowser-bg.gif) repeat-x;
    padding: 0 10px 0 0;
    position: relative;
    left: -58px;
    width: 457px;
}
#mainContent .archiveBrowser ul {
    margin: 0;
    padding: 0;
}
#mainContent .archiveBrowser li {
    display: inline;
    background: none;
}
#mainContent .archiveBrowser a:link, #mainContent .archiveBrowser a:visited {
    color: #fff;
    border: 0;
}
#mainContent .next {
    float: right;
}

.metadata {
    margin: 30px 0;
    padding: 0 10px;
    background: #eee;
    border: solid 1px #ccc;
}
.metadata h2 {
    margin: 10px 0 0 0;
}


/* -- replies -- */
#mainContent p.reply {
    color: #9FB6CA;
    font-size: 10px;
    line-height: 1;
    padding: 0 0 2em 10px;
    background: url(/i/ice/deco-reply.gif) 0 0 no-repeat;
}
.entry #mainContent p.reply {
    padding-bottom: 1em;
}
div.reply {
    position: relative;
    margin: 0 40px 3em 40px;
}
div.reply .replynumber {
    position: absolute;
    font-size: 22px;
    top: 2px;
    left: -38px;
    letter-spacing: -2px;
}
div.reply .replynumber a:link, div.reply .replynumber a:visited {
    color: #ccc;
}
div.reply .replynumber a:hover {
    color: #fff;
}
.reply-body {
    width: 500px;
    overflow: hidden;
}
.dave {
    background: #F4CFC6;
}
.dave p {
    padding: 0 5px;
}
.entry #mainContent p.postedBy {
    font-weight: bold;
    margin-bottom: 0;
}
.entry #mainContent p.posttimestamp {
    position: absolute;
    top: -1em;
    right: 0;
    color: #999;
}
.dave .postedBy {
    background: #E62E00;
    color: #fff;
    padding: 3px;
}
.dave .postedBy a {
    color: #fff;
}
.entry #mainContent .dave p.posttimestamp {
    top: -0.8em;
    color: #fff;
}
.entry #mainContent .dave p.posttimestamp a:link, .entry #mainContent .dave p.posttimestamp a:visited {
    color: #fff;
}

The link that imports this file has to occur within the HTML itself. If we were simply to use an @import statement within Listing 2, default-trimmed.css, we'd be undoing any advantage gained by breaking up the files; they would both be imported on every page of the site. On pages that don't require the content formatting, we'd link only the main style sheet:

<link rel="stylesheet" type="text/css" href="default.css" />

And on pages that require it, we'd link both:

<link rel="stylesheet" type="text/css" href="default.css" />
<link rel="stylesheet" type="text/css" href="contentformatting.css" />

Serving up CSS files on an as-needed basis has two distinct advantages:

  • It breaks up initial load times so users who haven't already cached the CSS aren't hit with quite as much overhead on the first visit to your site, saving a bit of bandwidth on your end as well.
  • As a site's CSS gets more complex, large single files containing all of a site's style quickly become unruly and hard to manage. Smaller files allow you to focus on only the style you need to look through when debugging.

But there's a caveat.

Keep Debugging in Mind

The Achilles heel of the multiple-file method is that in some cases your debugging actually becomes more difficult, at least perceptually. Instead of a single file to hunt through, now you have upwards of a dozen files spread across your site that could potentially be affecting the element you're trying to debug. Thanks to CSS specificity, you might have declared a color for that element in five different files, and only one is actually causing it to be red. Which file? Therein lies the extra challenge.

But when you consider that, thanks to the multi-file setup, you've automatically excluded the dozens or hundreds of lines of CSS in other files that you positively know you don't need to look through, you'll realize that you've actually sped up the debugging process, provided you can keep the files straight.

Again, more importantly than how exactly you choose to divvy up your CSS into multiple files—the method I described above being one of many possible ways—is that you come up with a method that makes sense to you and stick with it. Once you've established a file naming convention, it will become more familiar as you work with it. The additional file management when debugging will become second nature, and the reduced overhead of CSS code to hunt through will speed up the entire process.

With good tools at your side, this should be a non-issue. What if you could select any element on the page and get a list of all the style rules that apply to it—which one is actually causing it to do what you're trying to debug, and which file that rule resides in? Problem solved, wouldn't you say? Happily, such tools exist, and they're handy for more debugging scenarios than just this one:

Comment Your CSS

And finally, make sure that as you create your CSS, you add comments to describe what areas of code are meant to do. They do add extra bytes to a CSS file, so you may be inclined to comment sparingly in order to keep download times lower. But they really help with comprehension when you or someone else needs to later edit the file. It's important to find a balance between bulky, well-commented files and indecipherable yet fast-loading CSS.

And that's it for this first installment of our CSS templating series. Next up: the ugly truths of browser management.