How can CSS be object-oriented? In short: it can’t. But if we stopped there, then this would be a pretty short article! Let’s take a look at what is meant by the term “Object-Oriented CSS” and how it can help improve your stylesheets.
What’s the Problem?
First, we should take a step back and look at the problems that lead to CSS being difficult to maintain. Most projects start out easily enough. We write some styles for page structure, some default content styles, then some specialty content styles, right? We always ensure that our styles are separate from our markup and that our markup is clean and semantic, thanks to the web standards movement. When the project launches, we feel good about our work and don’t think much about code maintainability. After all, it’ll be easy to maintain because we wrote the code.
The first round of updates comes and it’s not too difficult. The stylesheets are still fairly small and easy to navigate, so the appropriate styles are found and updated. Some new styles are added because the new content is a little different than the existing content. Soon, a second round of updates is made, then a third. Each time, a few more styles are added to the stylesheets. Then we’re taken off the project for one of a hundred reasons, and someone else begins maintaining the site. Or perhaps we simply don’t touch the site for a year and then have to come back and make some updates; after such a long interval, we don’t remember as much about the code as we think we do. In either case, it’s difficult to understand the mix of ids, classes and element names, so more style declarations are added, overriding previous styles where necessary to get the desired effect.
Before long, some styles become defunct and other styles become unnecessarily complex as the stylesheets become overly complicated. We’ve ended up with spaghetti code, and the site’s stylesheets now take longer to download as they get more bloated. How do we get out of this mess? Is there a way to create truly maintainable style sheets?
Yes, there is a way, and it’s recently come to be known by the term, “ Object-Oriented CSS,” thanks to Nicole Sullivan. Style declarations are not objects in the programming sense of the word, but the term “Object-Oriented” refers to a mindset of thinking about how styles should be written and applied.
Keep Your Styles Where They Belong
Before we begin, we need to lay down a ground rule for keeping our styles maintainable: keep styles in the stylesheets. This means two things:
Styles don’t belong in markup.
Nowadays, we usually are pretty good about this, but it’s always a good reminder. Don’t succumb to the temptation to insert a quick style attribute here and there. It seems innocuous now, but we may regret it later.Styles don’t belong in Javascript.
This may or may not be up to us depending upon how the division of labor happens where we work. But if JS is on our plate along with CSS…great! That means we have the power to ensure that each site’s behavior (JS) is separated properly from its styling. Javascript should almost never be used to manually modify styles on an element—the lone exception to this is when a style value has to be calculated based upon other information. If we set styles via JS, what happens when we need to change those styles in the future? We’ll have to go dig through the JS and find every place those styles are being changed, instead of just making some simple stylesheet edits.
If we can’t edit styles with Javascript, how should JS interact with styles? The best method is using JS to modify elements’ classes. As we’ll see below, classes are the keys to elements’ identities and run-time states. By using Javascript only for modifying class names, all of the actual styling is handled by the stylesheets, making styles easy to find and modify.
And Now For Something Completely Different…Sort of
When we discuss this new, “Object-Oriented” mindset, there’s one key word that will be our mantra: “reusability.” Ok, there are really two key words: “reusability” and “patterns,” so I guess you could say we’re looking for “reusable patterns” here.
If we start thinking about creating “reusable patterns” at the beginning of a project, it’s a lot easier than going back and fixing a site that has already launched. But it still may be worthwhile to revisit existing sites’ code. Putting in the work now to make our stylesheets maintainable will not only save us future headaches, but it will also speed up our sites since the CSS files will be smaller and will download more quickly.
Before writing any code, we should pull out our layout comps and review them, looking for patterns. The patterns could be large and easy to spot like column arrangements or page block arrangements, or the patterns could be small like a box for entering login information. The patterns could be structural like column or block layouts, or the patterns could be stylistic like font and color choices. But finding the patterns is only the first step. When thinking about how to style these patterns, we need a paradigm shift in our approach. Instead of writing style selectors using IDs and element names, we will use classes to describe the elements’ identities and states. As a simple example of this, we’ll look at a small toolbar of zoom controls.
When thinking on how to style a zoom toolbar, our first inclination might be to style the entire toolbar using its ID #zoom_bar
and then style the individual controls using their IDs or element names, either #zoom_bar #zoom_out
or #zoom_bar button
. But we should stop and think about this for a moment. If we use IDs, then these styles are only applicable to this particular instance of the toolbar. If we want to reuse the styles we have to add another set of selectors, giving us #zoom_bar button, #another_bar button
. Now we have two sets of selectors and will probably need to add more in the future. And what if we’re styling link states? #zoom_bar a:hover, #zoom_bar a:active
would turn into #zoom_bar a:hover, #zoom_bar a:active, #another_bar a:hover, #another_bar a:active
. Writing styles this way quickly results in bloated stylesheets with way too many selectors.
Decouple Your Styles
Instead of styling elements according to “what we call them,” let’s shift our thinking to “reusable patterns” and style elements according to two things: their identities and their run-time states. Now, what is the core identity of the zoom toolbar? Zooming is what it “does,” not what it “is.” Its core identity is that of a toolbar, or perhaps more generically, “a group of buttons.” So what if we give it the class .button_set
? This refers to its generic identity and is very reusable. As for the controls inside, we don’t use their element names either (like “button”), but instead we create a class based on their identity, which could be as simple as .button
. This decouples an element’s styling from its markup; now we can use .button_set .button
as our selector, which avoids the specificity of a particular instance ID, which is not really reusable. This also gives us the flexibility to use buttons in our zoom toolbar or to change the elements in other (or future) instances of .button_set
. What if we encounter an issue that makes us change all the buttons to anchors? By using an “identity class” and decoupling the styles from the markup, all we need to do is change the markup, not the stylesheets.
The other type of classes we will create for our .button_set
is for the elements’ run-time states. In this instance we may have three different states: the default state, an active state and an inactive state. Creating classes for these states is as easy as creating .active
and .inactive
classes. Notice that these class names only describe the elements’ states, not what the elements look like or where they are positioned. Again, this decouples the markup from the styles, making the class selectors generic and extremely reusable.
When making this paradigm shift, we need to be careful to only use classes when defining our patterns—don’t fall back into the trap of using IDs for styling. It’s easy to relapse, but because of IDs’ high specificity, we should be especially vigilant about not using them for styling except to define occasional exceptions. And even then we should always ask ourselves, “Can I take this exception and make it reusable?” If we have to make an exception once, chances are good that we'll have to make it again later, and then we'll have two exceptions instead of one reusable pattern.
Please note that one ramification of decoupling our styles is that we may have to use multiple classes on a single element, making Internet Explorer 6 tricky to work with. In general, we should be able to work around this issue by adding concurrent classes onto different elements. But if we must support IE6 with multi-class selectors, we have two options: we can either use a Javascript fix (like IE7-JS), or we can create "joining classes" which join together the styles from the individual declarations (e.g. create .one_two
instead of using .one.two
).
Our Future Selves Will Thank Us
So to recap: Object-Oriented CSS is a phenomenal approach to writing stylesheets, even if it has very little to do with traditional Object-Oriented-ness. However, one thing that all CSS does have in common with object-oriented programming is inheritance. Always remember to take advantage of inheritance! If we find ourselves wanting to add the same class or style to several elements, we should make sure it is absolutely necessary; we may be able to get the same results by adding the class or style to a parent element.
Taking advantage of inheritance…creating reusable patterns…decoupling our styles…it's pretty easy to see the benefits of using this approach on an entire site. Gone are the layered styles, on top of styles, on top of styles. Gone is the need to overwrite some previous style to get the effect we need. With a little careful planning, we all can have lean, fast stylesheets that make maintenance a breeze.