Writing Better CSS

By Mathew Carella

Writing good CSS code can speed up page rendering. Essentially, the fewer rules the engine has to evaluate the better. MDN groups CSS selectors in four primary categories, and these actually follow the order of how efficient they are:

  1. ID Rules
  2. Class Rules
  3. Tag Rules
  4. Universal Rules

The efficiency is generally quote from Even Faster Websites by Steve Souders which was published in 2009. Souders list is more detailed, though, and you can find the full list referenced here. You can find more details in Google’s best practices for efficient CSS selectors.

In this article I wanted to share some simple examples and guidelines that I use for writing efficient and performant CSS. This is inspired by, and follows a similar format to, MDN’s guide to writing efficient CSS.

Don’t Overqualify

As a general rule, don’t supply more information than is necessary.

// bad
ul#someid {..}
.menu#otherid{..}

// good
#someid {..}
#otherid {..}

Descendant Selectors are the Worst

Not only is this not performant but it is fragile, as changes to the HTML can easily break your CSS.

// very bad
html div tr td {..}

Avoid Chaining

This is similar to overqualifying and it is preferable to simply create a new CSS class selector.

// bad
.menu.left.icon {..}

// good
.menu-left-icon {..}

Stay KISS

Let’s imagine we have a DOM like this:

<ul id="navigator">
    <li><a href="#" class="twitter">Twitter</a></li>
    <li><a href="#" class="facebook">Facebook</a></li>
    <li><a href="#" class="dribble">Dribbble</a></li>
</ul>

Following upon the prior rules…

// bad
#navigator li a {..}

// good
#navigator {..}

Use a Compact Syntax

Whenever possible, use the shorthand syntax.

// bad
.someclass {
 padding-top: 20px;
 padding-bottom: 20px;
 padding-left: 10px;
 padding-right: 10px;
 background: #000;
 background-image: url(../imgs/carrot.png);
 background-position: bottom;
 background-repeat: repeat-x;
}

// good
.someclass {
 padding: 20px 10px 20px 10px;
 background: #000 url(../imgs/carrot.png) repeat-x bottom;
}

Avoid Needless Namespacing

// bad
.someclass table tr.otherclass td.somerule {..}

//good
.someclass .otherclass td.somerule {..}

Avoid Needless Duplication

Whenever you can, combine duplicate rules.

// bad

.someclass {
 color: red;
 background: blue;
 font-size: 15px;
}

.otherclass {
 color: red;
 background: blue;
 font-size: 15px;
}

// good

.someclass, .otherclass {
 color: red;
 background: blue;
 font-size: 15px;
}

Condense Rules When You Can

Following on the prior rule, you can combine duplicate rules but still differentiate classes.

// bad
.someclass {
 color: red;
 background: blue;
 height: 150px;
 width: 150px;
 font-size: 16px;
}

.otherclass {
 color: red;
 background: blue;
 height: 150px;
 width: 150px;
 font-size: 8px;
}

// good
.someclass, .otherclass {
 color: red;
 background: blue;
 height: 150px;
 width: 150px;
}

.someclass {
 font-size: 16px;
}

.otherclass {
 font-size: 8px;
}

Avoid Unclear Naming Conventions

It is preferable to use semantic names. A good CSS class name should describe what it is about.

Avoid !importants

When possible, you should instead use good qualified selectors.

Follow a Standard Declaration Order

While there are a number of common ways to order CSS properties, this is a commonly used one that I follow.

.someclass {
 /* Positioning */
 /* Display & Box Model */
 /* Background and typography styles */
 /* Transitions */
 /* Other */
}

Format Your Code Properly

Code that is easier to read is easier to maintain. Here’s the format I follow:

// bad
.someclass-a, .someclass-b, .someclass-c, .someclass-d {
 ...
}

// good
.someclass-a, 
.someclass-b, 
.someclass-c, 
.someclass-d {
 ...
}

// good practice
.someclass {
    background-image:
        linear-gradient(#000, #ccc),
        linear-gradient(#ccc, #ddd);
    box-shadow:
        2px 2px 2px #000,
        1px 4px 1px 1px #ddd inset;
}

Where to Go From Here

Obviously these are just a handful of rules that I try to follow in my own CSS to make it both more efficient and easier to maintain. If you want to read more on the topic, I suggest reading Writing Efficient CSS on MDN and Google’s guide to optimize browser rendering.

This article was originally published at https://blog.mathewdesign.com/2013/07/04/writing-performant-and-quality-css/

Previous

Coding for Responsive State Changes with SimpleStateManager

Introduction to Animating in HTML

Next

22 thoughts on “Writing Better CSS”

  1. First off thanks for the great reminders and examples. I know and practice much if not all of what you have written here but can get lazy from time to time… which is well… // bad

    I have been building a lot of websites lately on WordPress with Roots a base theme based on Boostrap and I have a question about using !important. So many times I think I have a drilled down using the cleanest selector possible but the rule is still not changing. I imagine it is because the CSS that I would like to change is still caught in the cascade somewhere or I am not selected properly. Bottom line is that I find myself using important a lot more than I probably should. How do you tell when you are using important properly. What is the guideline there?

    • Hi Shaul!

      I’ve found myself asking the very same thing while reading the article. While developing a wordpress site I use !important quite often to overwrite themes’ and plugins’ defaults I cannot change by other means than editing their core files. With themes it’s quite easy though, you make a child-theme and done. Worse if a plugin determines your styling in unaceptable way – changing every time it updates is pain in the ass. Hence the !importants. Personally I don’t feel (very) bad about them, even though I’d love to skip using them anyway.

  2. A few good points in here for sure. However, some appear to be personal preferences as opposed to actual best practices. For instance, ordering declarations, while nice, is unnecessary. It’s more work to make sure everything is arranged how you like than just putting declarations in as you need them. Also, while I agree that you should avoid unnecessary chaining, it is very useful when you’re mashing up various types of elements. Your counter-example for chaining is awful: “.menu-left-icon”? Granting you the benefit of the doubt that this is a silly example and you didn’t mean to use a position in your class name, this is actually where chaining shines. You see, your example cannot be modularly modified whereas a chained method can be. Want to add .menu.right.icon? Super easy, just add the “right” class to it (again, don’t ever name anything “left” or “right”, but just as an example). In your situation, you’d have to create a whole new class and ruleset.

    Other than that, pretty good writeup, thanks 🙂

  3. I liked the article. There’s some very good advice in there.

    However, I’m not clear on the benefits of avoiding chaining. In fact, as Jason points out above, I can see real benefits to chaining. So specifically, what are the disadvantages of chaining? Is this a speed thing?

    To take another example, is .sprite-mail-posright better than .sprite.mail.posright? My style sheets are much lighter for chaining in this manner. Is there really a benefit for me or my users if I convert each chained selector in my CSS and group of class names in my HTML tags to a hyphenated selector and single class name?

    • I think the idea is that a class is reusable and not tightly coupled with a particular DOM structure. menu-left-icon is probably not a great class name but I think it was used by the author simply to illustrate the difference.

  4. I’m working through this chaining issue. Maybe this will shed some light.

    Instead of:

    .sprite:after { .. declarations shared by all sprite-based icons}
    .sprite.mail:after {… declarations shared by all sprite-based mail icons}
    .sprite.online:after {… declarations shared by all sprite-based online icons}
    .sprite.alert:after {… declarations shared by all sprite-based alert icons}
    .sprite.posright:after {… declarations shared by all sprite-based icons that sit right of parent}

    I would have:

    .sprite-mail:after, .sprite-online:after, .sprite-alert:after, .sprite-mail-posright:after, .sprite-online-posright:after, .sprite-alert-posright:after { .. declarations shared by all sprite-based icons}
    .sprite-mail:after, .sprite-mail-posright:after {… declarations shared by all sprite-based mail icons}
    .sprite-online:after, .sprite-online-posright:after {… declarations shared by all sprite-based online icons}
    .sprite-alert:after, .sprite-alert-posright:after{… declarations shared by all sprite-based alert icons}
    .sprite-mail-posright:after, .sprite-online-posright:after, .sprite-alert-posright:after {… declarations shared by all sprite-based icons that sit right of parent}

    That’s a lot more CSS. Now multiple that by 20. Because our site has a lot of sprite-based icons.

    Even if you use Sass and mix-ins and variables, the user ends up downloading a heap more CSS which will slow page-load speed. So isn’t chaining in this context a better option?

  5. I’m a huge advocate of chaining, and while there will always be instances where a descriptive class is better than a series of chained classes – I have never found someone who has been able to successfully argue against the flexibility of chained classes.

    e.g.
    // Chained button variant.
    Email us

    Imagine writing descriptive classes for all the variations you would want to have for this style of button. For me, good CSS is all about flexibility, and unless I’m in a situation where I would benefit from a descriptive class, then I’ll be chaining.

    • I completely agree about chaining. The ability to use composition in CSS is a strength, not something to be avoided. It opens doors to effective CSS design patterns that reuse classes, rather than make tons of new ones.

  6. Yea, I agree with most of these except “Avoid Chaining”. I’d say chain whereever possible, especially for modular elements like buttons. You can create a base then extend in different directions. eg.

    .btn {
    display:inline-block;
    padding:0.6em 0.8em;
    font-size:14px;
    font-weight:bold;
    color: #363636;
    border:1px solid #d7d7d7;

    &.rounded {
    border-radius:4px;
    }

    &.red {
    background: #9c0606;
    border:1px solid #690404;
    color:#FFFFFF;

    &:hover {
    background:#690404;
    }
    }

    &.green {
    background:#089f1a;

    }
    }

  7. I’m in the minority here but I am a huge advocate of keeping tags in rules (ul.nav instead of just .nav). Maintenance and understandability are so vastly improved that way. It isn’t just about writing fewer characters in CSS but knowing exactly what your CSS is doing and, if you need to, expanding the rules later (e.g. ul.nav, div.nav) if it’s needed. This promotes brevity of process and effect, which is vastly more crucial than brevity of characters used, especially when said brevity costs readability.

    • Selector performance is hindered when keeping tags in your rules. It also adds unnecessary specificity. Your class names should be semantic enough (and your markup predictable enough) that a tag selector should not be needed to understand what’s going on. If needed, use a comment to clear up doubt and minify it away for production.

      The only time you should use tag selectors paired with a class (‘ul.menu’, ‘strong.highlight’) is if the type of tag actually affects the styles of that class. ( ‘.highlight { /* blah */ } strong.highlight { /* modified blah */ }’)

  8. I can’t agree with the example:

    // bad
    #navigator li a {..}

    // good
    #navigator {..}

    For instance:
    #navigator li a { color: red; }
    makes the link red, whereas
    #navigator { color: red; }
    the link stays blue. This is not equivalent, and so cannot be replaced.

    Similarly
    For instance:
    #navigator li a { margin: 10px; }
    makes each link have a 10px margin, whereas
    #navigator { margin: 10px; }
    makes each link, LI, and UL have a 10px margin, or with the above text, equivalently each link has a 20px margin and the entire block has an additional 10px margin, doubling the spacing between links and tripling the spacing between links and the rest of the content.

    Bad recommendation, bad example.

    • Quite correct. I would add that the purpose of code is not to read or maintain, but to serve content efficiently. Code that is full of carriage returns, newlines and tabs is not performant.

  9. Thanks for your article!
    More than some rigid rules that everyone must follow writing CSS, my experience is that you must adapt your rules to the website design you are working on.
    As mentioned in an earlier comment, using CSS sprites makes your example rules counter-productive.
    Anyway, your advices are quite clever in general, as google now takes the page loading time in account for its ranking.

    Cya!

  10. I’m confused with the Stay KISS section. Am I being really stupid or are you not going to be able to target the anchor with the “good” example?

    Apologies if someone has already commented on this but I didn’t have time to check.

  11. A write-up with good intentions, but some things I feel need to be clarified:

    1. The KISS example is incorrect. It will not select the element want to select, the a. A much better idea would be “nav a { … }”, as list items can be ignored in this selector and the nav element should be used. I’m afraid to toss out the “never use IDs” here but especially for an element that already has its own tag available: don’t use an ID. Use the tag. And a class if necessary.

    2. “Whenever possible, use the shorthand syntax.” This needs a side note: only use this if you definitely want to set all four of (e.g.) the padding sides. You will reset all the available parts of a property if you use short-hand syntax. For example, if you only need padding at the bottom, don’t do “padding: 0 0 5px 0” too quickly, because there might be other specifications for padding lying around somewhere that you will be breaking. If you are not sure, use “padding-bottom: 5px”.

    3. The “Condense Rules When You Can” example can actually be even shorter:

    // good
    .someclass, .otherclass {
    color: red;
    background: blue;
    height: 150px;
    width: 150px;
    font-size: 16px; /* just put this rule in here, no need for separate element */
    }

    .otherclass {
    font-size: 8px; /* reset it here */
    }

    4. Formatting your code nicely can actually be professionalized even further by using SASS/LESS. Selectors can be nested for real there. Very usable and maintainable.

  12. I’m not sure how ‘#navigator li a {..}’ and ‘#navigator {..}’ are supposed to be at all comparable. Need a better example for that section.

Comments are closed.