Cascading Style Sheets: an art to some, a chore to others. I equate writing CSS with putting clothes on in the morning: is it necessary to make it through the day? No – but everyone is much happier when you do.
For those that do not know, CSS (or Cascading Style Sheets) is the markup we, as developers, use to make the different parts of a website look pretty. It’s where we define colors, layouts, fonts… all manner of properties that affect the look and feel of each page.
If you are unfamiliar with CSS, this where you stop being interested.
At the core of CSS are selectors – the statements that we use to define which element on a given page to which we would like to apply styling. Most of us try to keep these simple using IDs, classes, and sometimes generic elements to point out the item(s) we are targeting. Others of us might go a bit crazier and throw in some psuedo-classes or psuedo-elements to spice things up. And of course, there are those that make liberal use of the dreaded “!important” tag for which there is a special circle of Hell awaiting their wretched souls. I am hoping to possibly redeem some of this latter group today.
As web pages get more complicated, inevitably CSS selectors begin colliding. So how does the DOM decide which CSS selectors should be honored if there are 10 definitions all saying that this one div is supposed to have different background colors? It determines which one is the most specific. CSS selectors each have an inherent level of specificity. This is a measurement of how distinctly we are defining the intended element(s). For example, which of these appears the most specific?
.fox { /* all foxes */ }
.box .fox { /* all foxes that are inside of boxes */ }
.socks:not(.rocks) + .box .fox { /* Foxes inside of boxes which come directly after socks that are not rocks */ }
The last one, right? But of course, any object that can be in the last group is a member of each of the other groups as well. So how does CSS calculate this? There’s a pretty simple formula from which we will get 3 values: A, B, and C: A is the count of IDs inside of the selector B is the count of class selectors, attribute selectors, and pseudo-classes in the selector C is the count of generic types and pseudo-elements in the selector.
div { /* A=0, B=0, C=1 */ }
#theSea .bottom .hole .log .knot .frog .wart { /* A=1, B=6, C=0 */ }
a.duck.duck.goose { /* A=0, B=3, C=1 */ }
It’s important to remember that these are separate values. I’ve read articles before treating the “a” value as “hundreds”, “b” as “tens”, and “c” as “ones” – this is NOT how these work. You can have a selector using 100 generic elements – it will not overtake styles defined with just 1 class. The formula checks these values as 3 individual steps: (1) we check “a” (and if it there is an element with distinctly high “a” value, then it wins), then if that is a tie, (2) we check “b”, and if that too is a tie, then (3) we check “c”. If all “a”, “b”, and “c” values are equal, then the winner is the selector that is defined last.
Of course, there are caveats. For instance, inline styles trump any values loaded from files or defined in the header UNLESS any of the predefined styles make use of the !important tag. Adding “!important” after a property means that no matter what other selectors may try to do, this setting will not change. Of course when two “!important” statements collide, then we just go back to our aforementioned specificity rules to decide who wins.
div[data-test="Test"] { background: blue !important; }
div { background: red !important; }
div.test { background: yellow; }
#test { background: white; }
/* The background of the "test" div is going to be blue */
“So what’s the problem with using ‘!important’?” you may ask. “!important” forces CSS to break its own rules which can make future updates much more difficult. In order for it to even be necessary, “!important” must be tacked on to a selector that is LESS specific than the one it is overwriting, which means it’s very possible that there are unforeseen consequences to using it. This usage is generally just lazy. Yes, I’ve used it used it in a crunch, we’ve all done things we’re not proud of, but it really is just a sloppy way to solve a problem. The ideal approach is to come up with a selector specific enough that its styles are applied correctly according to CSS rules.
There are times when the use of “!important” can be forgiven. Generally, these cases treat “!important” styles more like constants – things that should not be allowed to change. For example, some complicated CSS effects require very specific settings elements that work together. If certain styles change, the effects may no longer work, so using “!important” helps protect things from breaking.
So in conclusion, as a developer, it’s useful to be familiar with the concept of CSS specificity. Not only from a reactionary standpoint where you’re working on an existing product – it’s also something to consider when you first begin working on a new project. Think about the hierarchy of elements and consider the classes, ids, and other parameters that will go into the selectors for your styling. Planning ahead can save you (or others that have to pick up after you) some serious headaches in the long run.
eLink Design is a national web design, application development, SEO, and business consulting firm, founded in 2001 that specializes in custom solutions for over 800 clients around the country. With this blog, we hope to provide insights into what we are working on, areas where we think we can help shed light on problems we hear, and sometimes just cool things we have come across.