CSS, DRY, and Code Optimization

Published on October 9, 2014 (ā†» February 5, 2024), filed under (RSSĀ feed).

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.

Toot about this?

About Me

Jens Oliver Meiert, on September 30, 2021.

Iā€™m Jens, and Iā€™m an engineering lead and author. Iā€™ve worked as a technical lead for companies like Google, Iā€™m close to W3C and WHATWG, and I write and review books for Oā€™Reilly and Frontend Dogma. I love trying things, not only in web development, but also in other areas like philosophy. Here on meiert.com I share some of my views andĀ experiences.

If you have a question or suggestion about what I write, please leave a comment (where applicable) or aĀ message.

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 šŸ˜Š