CSS Mixins vs. multiple classes

  Wynn Netherland • 2009-11-04

I like the idea of DRYing up my CSS. This usually begins simply by using selector grouping.

h1 {font-weight: bold;}
h2 {font-weight: bold;}
h3 {font-weight: bold;}

becomes:

h1, h2, h3 {font-weight: bold}

Pretty basic so far. But what happens when the styles become a bit more complex? Take the following example:

#sidebar {
  background: #efefef;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  padding: 10px;
}

#login {
  background: #999;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  padding: 10px;
}

Using selector groups in this case leads to less maintainable code in that you have to keep updating the permutations of selector groups. A common solution is to factor common properties into classes:

.rounded {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

.box {
  padding: 10px;
}

#sidebar {
  background: #efefef;
}

#login {
  background: #999
}

... and we employ these new classes like so:

<div id='notice' class='box'>
   ...
</div>

<div id='login' class='rounded box'>
   ...
</div>

<div id='sidebar' class='rounded box'>
   ...
</div>

The idea is that we group common styles into reusable classes that can be added to elements as needed. Breaking the styles down into rounded and box classes, lets us use them independently.

Mixins: a more programmatic approach

To my knowledge, Sass, heavily influenced by the Ruby language in which it's implemented, brought the idea of mixins to CSS. Consider this example:

=rounded
  -webkit-border-radius: 5px
  -moz-border-radius: 5px

#login
  background: #efefef;
  +rounded
#sidebar
  +rounded

If you're a newcomer to Sass, this example might be a bit hard to follow. Basically, we declare a mixin named rounded with a set of styles. These styles are then mixed into the elements in the following selectors. When compiled, this Sass stylesheet yields the following CSS:

#login {
  background: #efefef;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

#sidebar {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

Here's the same example using LessCSS which has a more CSSsy syntax:

.rounded {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

#sidebar {
  background: #efefef;
  .rounded;
}

#login {
  .rounded;
}

Where this truly becomes powerful is in the use of variables like in this Sass example:

=rounded(!radius=5px)
  -webkit-border-radius = !radius
  -moz-border-radius = !radius

#login
  background: #efefef
  +rounded
#sidebar
  +rounded(3px)

which yields:

#login {
  background: #efefef;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

#sidebar {
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
}

With a single mixin we can reuse rules in a parameter-driven fashion to create two sets of styles, one with a 5px border radius one with 3px. Nifty, huh?

The tradeoff

I'm a big fan of Sass, especially as implemented in the Compass meta-framework, and use this mixin approach frequently. However, in moving away from multiple css classes to mixins, I've noticed I've lost a part of my development workflow that I've come to depend on - quick style spelunking with Firebug.

Not so obvious style sources

When using multiple classes, the Firebug style inspector can take me to the exact line where the style was defined. With the mixin approach, these styles are now mixed into each rule. Firebug will of course show me the styles in each rule, but now I don't know where they come from unless I leave my browser and traverse my Sass code.

Harder to play eye doctor

Something else I do frequently in Firebug is playing eye doctor. You know "Better? Worse? Better Worse?" I add and remove additional css classes to elements to quickly apply and remove styles to play with different design aspects (like trying out rounded corners, etc.). When using Blueprint CSS's css classes, I can quickly prototype layouts and increase/decrease column widths just by modifying CSS classes like span-10 or span-12. When using Compass's mixin approach, I get all that grid layout goodness mixed into my semantic selectors. However since it's much harder to edit a dozen CSS style properties than to toggle a CSS class I lose the ability to quickly play around with the layout.

Conclusion

I think the growing list of CSS meta-frameworks are changing how we look at creating CSS. However with power comes certain tradeoffs. What's your take?

Wynn Netherland
Wynn Netherland

Engineering Director at Adobe Creative Cloud, team builder, DFW GraphQL meetup organizer, platform nerd, author, and Jesus follower.