CSS, DRY, and Code Optimization

Published on October 9, 2014 (↻ February 5, 2024), filed under (RSS feed for all categories).

This and many other posts are also available as a pretty, well-behaved ebook: On Web Development. If the optimization of CSS is of particular import to you, I’ve collected several concepts in a brief book: CSS Optimization Basics.

When I explained the problems with type sorting in CSS I realized that the type sorter’s motivation was a particular problem, not just mere preference. It was something more fundamental, something that has caused us web developers more headache. The underlying problem is that people repeat themselves too often in CSS—or repeat the wrong code too often, respectively.

Now, not repeating ourselves in code makes for an important maintainability principle. My own guide on maintainability is lacking in this regard, but if we pull up common definitions, we find that “don’t repeat yourself (DRY) is a principle of software development aimed at reducing repetition of information of all kinds” as it “usually makes large software systems easier to maintain” (Wikipedia), or that “duplication can lead to maintenance nightmares, poor factoring, and logical contradictions” so that “every piece of knowledge must have a single, unambiguous, authoritative representation within a system” (Cunningham & Cunningham). I’ll assume that it’s clear why maintainability is important and how DRY contributes to it.

The Natural Way of Writing CSS

What happens in our style sheets is that we’re too seldom making an effort to avoid repetition. The natural tendency is, if at all, to only involuntarily avoid repetitions of selectors, simply because of the way we code. That way consists of focusing on features, and features are represented by selectors and groups of selectors.

Let’s look at an example, although I’ll abstract it a little to find a balance that gives just enough understanding before flooding ourselves with unnecessary specifics. When we start with a project and its style sheet we tend to work a bit like the following:

feature1 {
  declaration1
  declaration2
  declaration3
}




feature2 {
  declaration4
  declaration5
  declaration1
}




feature3 {
  declaration6
  declaration2
}

(“featuren” stands for the selectors for our features, and “declarationn” for different declarations, unless carrying the same number.)

That is the basic approach, which doesn’t change, in principle, when adding more selectors and declarations. I claim that everyone works like this. It’s how we all write our CSS code, initially.

Naturally, then, through our focus on features we tend to avoid repetition of selectors. Not entirely: We tend to. But that’s not helping us.

For one, what is happening is that the bigger the style sheet, the more entropy and repetition there are. The order of selectors, declarations, rules, and sections gets more and more chaotic, and there are more and more duplicates both in terms of selectors and declarations (and possibly even rules). The web developer who hasn’t observed this should more carefully check his style sheets, and I say this kindly for I know the pressure we’re sometimes under.

For another, the seemingly fortunate circumstance of not repeating ourselves as much with our selectors is not just more of a coincidence owed to how we work, but also not as helpful because, as they’re supposed to, selectors are usually shorter than declarations. That is, any savings here in terms of avoided repetition get eaten up by the repetition we observe when it comes to our declarations. We get a glimpse at that in the code example.

The Optimized Way of Writing CSS

The improved way of writing CSS does not, in my book, mean changing the way we naturally code. It rather consists in focusing on post-optimization that avoids repetition. Surprisingly—or not! that’s why Google paid me a very good salary—, the solution to avoiding the bulk of repetition in CSS is a method I promoted and polished in 2007/2008: to use every declaration just once. (Yet, the first mention on this site didn’t get that much attention, while the Google version didn’t find open ears.)

Using every declaration just once appears to be much the most effective way of applying DRY to CSS. To get to the typically significant savings (20–40%), a bit of manual labor is involved, however. How this process looks like I had already shared in said posts on this site and at Google, so I’m not going to repeat myself, either.

Code Optimization, the Web Developer’s Stepchild

I’m sometimes a bit of a lazy writer who readily assumes that everyone can follow his accent and his way to write prose. Here perhaps, too, as I’ll for the moment take it that everyone understands why we need DRY in CSS, how DRY in CSS is work, how that work rests on declarations, not selectors, and that that work requires manual labor for it’s very difficult to automate.

But now I want to extend this point to my general concern that we don’t focus enough on CSS optimization. And I think DRY is a great example of this not just because it’s neglected much of the time, not just because it, when omitted, encourages type sorting, but because it, again forgotten, has also led us into the constants and mixins rabbit hole.

What happened here was that web developers found themselves writing a lot of code, as per the natural approach, to then demand to simplify CSS code through constants and mixins. That must now appear as a knee-jerk reaction, for three reasons:

  1. From a technology standpoint, using constants and mixins was already possible through languages like PHP. Bert Bos explored this issue in a nice article about variables back then, to which I could add only little.

  2. From a problem standpoint, using declarations just once already greatly reduced repetition and complexity to a diminishing need for constants.

    (As for the so criticized sorting of declarations by type, using declarations only once reduces the average number of declarations per rule to about two, down from ten or more type sorters use in examples. That is the realization I mentioned in the beginning: the type sorter’s root problem is likely the natural, non-optimized way of writing CSS.)

  3. From a problem-solving standpoint, looking at reducing complexity through variables was skipping another important step, which is trying to avoid that complexity (which may have led right to DRY).

Although I recognize a need for constants and mixins, I believe the whole no-DRY-but-cry-massively-for-variables incident is a great example of how we neglect CSS optimization. We don’t look closely enough at how we can write better CSS, how we get more out of it, and how we can avoid the problems we run into before calling in heavy CSS Working Group artillery.

Not as lazy as suggested, this must do for the moment. The most important thing to take out of this post, please, is to minimize repetition in style sheets—perhaps through using declarations just once—, to focus more on CSS optimization, and to consider that avoiding problems is also a way of solving them.

Update (July 3, 2017)

In the meantime I’ve taken and studied additional data around repetition in CSS (notably the repetition of declarations) and shared more thoughts on DRY CSS and the idea of using declarations just once: 70% Repetition in Style Sheets: Data on How We Fail at CSS Optimization.

Was this useful or interesting? Share (toot) this post, become a one-time nano-sponsor, or support my work by learning with my ebooks.

About Me

Jens Oliver Meiert, on November 9, 2024.

I’m Jens (long: Jens Oliver Meiert), and I’m a frontend engineering leader and tech author/publisher. I’ve worked as a technical lead for companies like Google and as an engineering manager for companies like Miro, I’m a contributor to several web standards, and I write and review books for O’Reilly and Frontend Dogma.

I love trying things, not only in web development (and engineering management), but also in other areas like philosophy. Here on meiert.com I share some of my experiences and views. (Please be critical, interpret charitably, and give feedback.)

Comments (Closed)

  1. On October 9, 2014, 15:41 CEST, Gunnar Bittersmann said:

    A CSS preprocessor like Sass might come to rescue.
    For your example: From Sass code

    %placeholder1 { declaration1 }
    %placeholder2 { declaration2 }
    %placeholder3 { declaration3 }
    %placeholder45 { declaration4; declaration5 }
    %placeholder6 { declaration6 }
    
    feature1 {
      @extend %placeholder1;
      @extend %placeholder2;
      @extend %placeholder3;
    }
    
    feature2 {
      @extend %placeholder45;
      @extend %placeholder1;
    }
    
    feature3 {
      @extend %placeholder6;
      @extend %placeholder2;
    }

    the desired CSS would be generated:

    feature1, feature2 { declaration1 }
    feature2, feature3 { declaration2 }
    feature1 { declaration3 }
    feature2 { declaration4; declaration5 }
    feature3 { declaration6 }

    (In the real world, the placeholder rulesets would usually contain more than one declaration, as indicated for %placeholder45.)

    Cf. slides 76/77 in my presentation CSS preprocessors for the best of both worlds at From the Front 2014. (PDF, 10 MB)

    Jens, you’ll love slides 43/44. đŸ˜‰

  2. On October 9, 2014, 15:50 CEST, Jens Oliver Meiert said:

    I need to look at this again so just a rather instinctive reaction—why would you want WET source files, and is that really simpler to author?

  3. On October 9, 2014, 17:26 CEST, Gunnar Bittersmann said:

    The Sass code is indeed WET in the sense that ‘@extend %placeholder#’ appear multiple times for different selectors.

    But it can be DRY regarding the values. For example:
    %placeholder1 { property1: value-foo }
    For changing the value from value-foo to value-bar there’s one single point of change, regardless how many feature# extend that placeholder.

  4. On October 10, 2014, 12:30 CEST, jaison justus said:

    I use BEM + SASS to achieve modularity and usability. Still figuring out to improve.

  5. On October 12, 2014, 15:57 CEST, Jens Oliver Meiert said:

    I think we’re right back at the beginning, Gunnar. Note that I don’t want preprocessor users to defend themselves. I want more focus on CSS optimization, whether “source” or production files, and I believe I’ve reasonably laid out why that is important.

  6. On October 14, 2014, 16:05 CEST, Drew Bell said:

    Doesn’t this spread the styles for a given element all over the place, though? I’m much more likely to be working on “containers for form alerts” or “filepicker lists” than I am “things that have auto margins”, right? I don’t trust myself to remember where all the styles for a given module live in the stylesheet, and hold them in my head at once.

  7. On October 16, 2014, 11:31 CEST, Kay said:

    Jens,
    thanks for the article!

    I’m full of questions:
    Where do you exactly see the benefit in repeating your ‘features’ over and over again and only write as few declarations as possible?
    By looking into the CSS of this site I’m spotting countless h1-selectors where you could have only one which would be a lot easier to locate in the stylesheet and easier to maintain (exspecially when you’re working in teams instead just for yourself..).
    I really don’t understand the part where you’re overwriting h1’s important-declarations with other important-declarations.
    My opinion is don’t ever use the !important declaration because it really quickly becomes a pain to maintain them.

    Your point in your article is to avoid declaration-repetions but isn’t selector-repetition even worse?

  8. On October 16, 2014, 12:41 CEST, Jens Oliver Meiert said:

    Hi Kay—as described in the post, declarations are usually longer, and so normalizing them means bigger code savings. Additionally, going by declarations does in turn make maintenance easier, for it’s simple to update, say, color values and such. As a matter of fact, the need for constants/variables may completely disappear—that’s how powerful the method is. Also as described đŸ˜Š

    As for !important, I don’t think it’s helpful to add here as it’s unrelated to DRY in CSS. And so my reasons to use !important are unrelated to using declarations once. (Exceptions prove the rule, as there may be needs due to changes in the cascade.) Please note that there’s nothing wrong with !important; it’s a legitimate part of the cascade and can be a very effective tool. That’s, so I found, a healthier mindset than the outright ban we find in other places.

  9. On October 16, 2014, 15:11 CEST, Kay said:

    Thanks for the quick reply, Jens!
    I’ll consider your suggestions in my next project.
    It’s always good to hear other attitudes then my own đŸ˜Š