CSS Code Smells

Writing stable, cross-browser CSS isn't nearly as easy as it should be. Wouldn't it be nice if you could easily avoid some of the most painful CSS pitfalls?

In this post, I'm going to follow the spirit of Martin Fowler's classic code smells and list a small number of CSS-specific smells that cause bugs and limit productivity. I'm limiting my list to writing for modern browsers and IE versions 7 and later. This isn't a definitive or final list, and I plan to add to it as I get feedback and advice.

As Martin Fowler points out, "The best smells are something that's easy to spot and most of time lead you to really interesting problems." Not every instance of a code smell indicates that there's a problem, but the presence of one should at least cause you to pause and think.

Nothing on this list is original. I think of it as a complement to CSSLint tool by Nicholas Zakas and Nicole Sullivan, which automatically scans your CSS for a set of problems. I'm also a huge fan of Nicole Sullivan's OOCSS methodology and I've liberally repeated some of its ideas in this post.

My (least) favorite code smells

Here's a small list of some of my least favorite code smells.

Smell: Width/height properties mixed with padding/margin/border properties

I stole this one straight from the CSSLint box-model rule. An element's actual width and height is the sum of its content area width/height, its padding, its borders, and its margins. 

To make things look right, I used to do mental arithmetic. If I had a 300px-wide module that needed 10px of horizonal padding, I'd define the width to be 280px and the horizontal padding to be 10px. This approach didn't work for three reasons.

  1. The arithmetic made things harder to understand at a glance that my element took up 300px of horizontal space and not 280px.
  2. I've found several cases (see screenshot below) where a container's padding varied depending on its content. It didn't always make sense to preordain a container's padding.
  3. It just won't work for containers with fluid (percentage) width and fixed (pixel) padding.


Two instances of the same container, one internally padded and one not

There are two workarounds. If you don't need to support IE7, you can use the box-sizing: border-box CSS property setting. This will cause the width and height properties to set the width/height of the content area, padding, and borders together. An alternative workaround that works before IE7 is to add padding and borders on contained elements only. e.g.,

<section class="aContainerType"> <!-- width and height applied here. -->
  <div class="body"> <!-- Padding and borders applied here. -->


This workaround feels a little icky, but it works consistently and it's remarkably flexible. 

Smell: Overuse of explicit widths

If your layout catastrophically breaks when you change the width of a column or container, then you're probably overusing explicit widths.

Twitter's website gives a good example of how to do things right. The right column has a width of 522px.

But, if I hack the width to be 300px, the tweets still lay out in a reasonable way:

Make your container widths fluid except when they have to be fixed. You might need to practice a little to make this work, but, once you have the hang of it, you'll love the flexibility that fluid widths provide you.

Smell: Application semantics

Avoid CSS class names that say too much about your application's behavior and too little about its design language.

But shouldn't markup and styles be semantic? The meaning of "semantic" is a little confusing. CSS semantics are visual. For example, the classes buttonLarge and buttonSmall are decent class names because they describe two types of buttons that your design allows. The classes addUser or hideProfile are poor choices because they likely refer to a visual style that applies equally well to other behaviors. e.g., a button or link style.

Why does this matter? If your application succeeds, its functionality will expand dramatically. But its CSS should remain relatively small in size. If you tie your classes to your design language instead of to your application's functionality, then you'll easily be able to apply them to new functionality while writing a minimum amount of new CSS.

To see a good example of well-defined class names, see Bootstrap's button color and list styles

Smell: Deep nesting (aka high specificity)

Deeply nested styles – those preceeded by a long chain of IDs or selectors – are very hard to reuse. This is the core message of OOCSS. This smell is similar to the Application Semantics smell. A section header is a section header is a section header. There's no need for its styling to be nested under .rightColumn or .feedbackBox.

If you have multiple styles of a particular type (say, multiple section headers), then define style variants that work across the board and separate content styles from container styles. Here's a simple example from my company's style guide:

A darkerHeaderBar and an urgentHeaderBar can be used anywhere on the site. They aren't nested under other selectors. They can also contain anything: lists, tables, even other sections.

Writing good container-independent styles with low nesting is tricky and it takes some practice. I highly recommend reading Nicole Sullivan's blog (I love this post) on the subject and watching this video.

Smell: Hand-rolled float layouts

This one will be the most controversial. Floats cause a ton of layout glitches because floated elements are taken out of the document flow. Floats were intended to support flow of text around images. But, because developers lacked alternatives, they used floats to lay out columns. There are subtleties involved in correctly floating elements and in clearing floats. And I've seen a lot of people trip up on those subtleties. 

Either take the time to learn how floats work, or use a grid framework. I advise the latter because I've found it easier for people to grasp how grids work than to understand the nuances of floats.

The future is brighter

While I don't think we'll ever love CSS, it's going to get a lot cleaner in the coming years. The flexible box model should make more trickly layout problems easy to fix. And the eventual obsolecence of IE7 will make CSS authoring more fun still.

In the mean time, keep practicing! And please send along your candidates for CSS code smells.

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Response: weblink
    Amazing Site, Stick to the great job. Appreciate it!

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
« Critical Mass and Aspiration | Main | Getting Real About Hacking »