CSS is read from top to bottom, this is important because it means that we need to be mindful about the order in which we select our elements, especially when we're selecting the same element twice.
So for example, just to demonstrate, in my index.html
, we see, in my body element, I just have this h1
element.. and then in my CSS, we see, I'm already selecting it, and giving it a color of red, and we see it's red in our live server.
But now, underneath our h1
ruleset, what would happen if I reselected our h1
element, and also gave it a color property, but this time, another color, like green. When I save, we see, our h1
is now green.
When two rulesets have the same selector, and both are attempting to define the same property but with different values, what you get as a result, is conflicting rulesets.
Our two rulesets are conflicting with each other, they're both competing to apply their styles on the same element, and on the same property.
In this case, the conflict is resolved by the order of appearance. Since CSS is read from top to bottom, our second ruleset gets interpreted last, and therefore our h1
receives the color of green.
The Specificity Algorithm
Now unfortunately it's not always so simple.. For example, in my index.html
, I'll give my h1
element a class called title
, and then back in my CSS, on my first ruleset, instead of selecting all h1
elements by their element name, I'll select our h1
element by the class we've just given it, and when I save, we see, despite the order of appearance, our first ruleset is the one getting applied, and we see our heading is red instead of green.
The reason this is happening, is because it turns out CSS uses an algorithm to resolve conflicting rulesets, a point system called specificity.
This algorithm, specificity, what with it being a point system, will resolve conflicting rulesets by looking for the selector with the most points, almost like how games have a scoring system, and the player with the highest score wins the game.
At the very top of my CSS file, I'll add a comment that says, style attribute, ids, classes, and elements.
Now above both of my existing rulesets, I'll add a comment above both of them, that says 0 0 0 0
.
These zeros above my rulesets represent the point system CSS uses under the hood, to determine the specificity of a selector.
The first 0
represents style attributes, the second represents ID selectors, the third represents class selectors, and the fourth represents selecting elements by their element name.
If I was to distribute points across our two rulesets, on the first, since we're using a class selector, I can replace the second to last zero with a one, and then on my second ruleset, since we're selecting our h1
by it's element name, I can replace the last zero with a 1
.
Points that are further left are worth more points than those to the right. Style attributes are worth the most, ids are worth the second most, classes are worth the third most, and selecting elements by their element name is worth the least.
Our first ruleset uses a selector worth more points than the one used on our second ruleset, therefore despite the order of appearance, the first ruleset wins the conflict.
ID Selectors
Now I'll head over to my index.html
, and I'll give my h1
element an id
called title
.. Then in my CSS, above our first ruleset, I'll reselect our h1
element, but this time by it's id
, and I'll give it a color of purple. By adding this new ruleset, we've just added another conflicting ruleset in the mix, but before I save, what do you suppose is going to be the color of our heading when I run this code? When I save, we see, our heading is now purple.
Above my new ruleset, I'll add a comment that says 0 0 0 0
, and since this ruleset is using an id selector, I'll replace the second zero for a 1
. This new ruleset uses a selector worth more points than any of the other ones, and so despite the order of appearance, this ruleset wins the conflict.
Style Attributes
Finally, to demonstrate the highest specificity, in my index.html
, I'll give my h1
element the style
attribute, and I'll give it a color of pink. I'll also add a comment above my h1
element that says 0 0 0 0
, and since we're using the style attribute, I'll replace the first 0
with a 1
. When I save, we see, our h1
is now pink, and this is because the style attribute is worth more points that everything else.
The style attribute is worth the most amount points, and therefore it has the highest specificity.. However, as a last resort, there is still one way to beat it. Back in my CSS, on my last ruleset, which by the way has the least amount of points, against my color of green, I’ll say !important
. This important keyword automatically wins over any conflict, and when I save, we see, despite this ruleset having the lowest specificity, our heading is green.
Specificity Brackets
Now there's one last thing I want to show you. In my index.html
, I’ll add an unordered list with one list element inside it. Inside this list element I’ll add a ordered list and one list element inside it. Inside this list element, I’ll add an unordered list with one list element inside it. Inside this list element, I’ll add a ordered list and one list element inside it. And finally, inside this list element, I’ll add an unordered list with one list element inside it. Now I'm just going to move my h1
element inside our deeply nested list element.
Back in my CSS, I’ll remove the !important
keyword from the color of green, and when I save, our heading is back to being pink since in my HTML, our h1
element is getting a color of pink from it's style attribute. But back in my CSS, on my last ruleset, instead of just selecting my h1
element like this, I’m going to do something silly and select my ul
, my li
, my ol
, my li
, ul
, li
, ol
, li
, ul
, li
and finally my h1
.
We’re now selecting eleven elements with the descendant combinator selector, which is kind of insane.. But since we're selecting eleven elements by their element name, I’ll update the point system above our ruleset, from 1
to 11
.
This selector is worth 11 points on the fourth bracket.. This is a lot, but despite it being a lot, when I save, we see, nothing happened, our heading is still pink. This is because brackets only compete with their own brackets. This means that, it doesn’t matter how many points we have in our last bracket, the style attribute bracket is still the winner.
If I head over to my index.html
, and remove the style attribute from our h1
element, and save, we see, our highest winning bracket is now the ruleset in my CSS with the id
selector, and so our heading is purple.
If I comment out this ruleset, it's now our ruleset with the class selector that has the highest winning bracket, and our heading is now red.
And finally if I comment it out, of course now our h1
has a color of green.
However, because brackets only compete with their own brackets, this 11 point element selector will win against other rulesets that uses element names for selectors.
So for example if I add new ruleset underneath this 11 point one, and select my h1
by it’s element name, and give it a color of brown, we see, when I save, our h1
is still green. I'll add a comment above it that says 0 0 0 0
and since we're selecting our h1
element by it's element name, I'll replace the last 0
for a 1
.
Both of these rulesets are competing within their own bracket, and despite the order of appearance, the one with the highest points wins the conflict.
For the order of appearance to win a conflict, competing rulesets must be competing within the same bracket and have the same amount of points. For example, if I copy my eleven selectors, and paste them on my new ruleset, and save, we see, our heading is now brown.
I'll update the points from 1
to 11
, and now that both rulesets share the same bracket, and have the same amount of points, the order of appearance is now the determining factor in resolving the conflict.
Now as you can see, specificity is obviously very complicated, and is probably the culprit for a lot of the headaches people get when working with CSS.
As a best practice, and especially to mitigate how unruly specificity becomes as your app grows in scale, a common convention, is to simply default to using classes for selecting everything. You can literally avoid all of the headaches, issues, complications, and unpredictability, by just always selecting your elements with classes.
When everything you want to style is selected with classes, you create consistency in your specificity, to where you no longer even need to think about it, and only ever have to think about the order of appearance, which is inherently much easier to track. By simply defaulting to using classes, you make CSS substantially easier to work it.