Sunday, March 10, 2013

What is Specificity?

I must say, it's really difficult to explain this topic but anyway, here goes nothing.

Specificity (it's a tongue twister really!) is one of the most important concepts in CSS. A lot of times when we see that our CSS rules have no affect, we think that there's a bug in our code, but actually, in most cases, it is Specificity that we usually ignore while coding. Here's what it is.

When several rules can be applied to the same HTML element but have conflicting properties, there's a priority scheme to determine which rule applies. So in simple words, it just determines which CSS rule should be applied by the browser. 

It's kind of a weighting system. Every selector has a weight attached to it (either it is an element, ID, class, pseudo-class, inline styles or anything). There's actually a precedence hierarchy for all selectors. We'll look at that later in the article.

Let's look at some cases that we should take into consideration while writing CSS. We'll start off with the simplest case of Specificity.

Case # 1: When Selectors have Equal Specificity

Consider the following code:

<h1>Hello World</h1>

See the figure below:

Simple H1 tag containing some generic text
 
Now we'll use CSS to style the H1 tag. We'll apply the same property 3 times, but with different values, to the same element as shown below:

h1 { background: red; }
h1 { background: green; }
h1 { background: yellow; }

See the figure below to see which property will be preferred over others:

CSS rules conflicting with each other
 
Element selectors have a specificity weight if 1. In the case above, if we try to calculate the specificity for all three selectors one at a time, we'll find out that all of them have a specificity equal to 1. In such cases, when selectors weigh equally, the selector that is declared last will be the one that counts and its styling will be applied to the targeted element. 

In our case, H1's background color will be yellow because the previous two conflicting declarations will get overridden by the rule that is declared last.  

Actually when we target the same element (either using element selectors, class selectors, ID selectors etc.)  by repeatedly using the same weight selectors, the latter ones will inherit the properties of the same selectors that were previously declared (that is why it is called Cascading). It only overrides those properties that conflict with its rules. (as explained above).

So after all that explanation, let's get back to our final output, that is an H1 heading with a yellow background. See the image below:

Final output with a background: yellow declaration applied
 
Our previous example is one of the simplest cases of Specificity (when selectors have equal specificity). We'll now take a look at a slightly complicated example but before that we need some knowledge about the precedence system of Specificity. After that, I'll explain Case # 2.


Precedence Hierarchy for Specificity

As explained previously, Specificity has a hierarchy through which it determines the precedence level of a selector. We can divide this hierarchy into four groups. Be sure to remember this sequence as specificity determination depends on this sequence.

Specificity Hierarchy


1. Inline Styles - CSS that is written inside an HTML/XHTML file using, <style></style> tags, is referred to as Inline Styles. They are at number one in the precedence list of Specificity which obviously means that it will override any conflicting CSS rule that is defined in an External CSS StyleSheet.


2. IDs (defined with a hash # symbol in CSS) - IDs are second in the hierarchy.


3. Classes and Pseudo Classes - Classes are defined with a period (.) and Pseudo Classes (like :focus, :first-line, :hover etc.) are used to add special functionality to some selectors. This group comes at 3rd position in the hierarchy.


4. Element and Pseudo Elements - Elements and Pseudo-Elements have the least most precedence in the hierarchy. 


Calculating Specificity

Remember the Specificity Hierarchy? Then let's jump right into calculating some Specificity values. The Hierarchical sequence for specificity was Inline Styles, IDs, Classes and Elements. We can start off by giving all of them a value of zero by default. We can write these values in their hierarchical sequence as 0 for Inline Styles, 0 for IDs, 0 for Classes and Pseudo Classes and 0 for Elements and Pseudo Elements. There are two different ways to write this. One way is to write them as a comma separated list and the second way is without commas or anything (refer to the screenshot below).

Default weight values of Specificity

Now before moving on to calculating some real-world selector weights, I should tell you the actual weights of the groups mentioned above.

1. Inline Styles - (1,0,0,0) or 1000 - Highest Specificity
2. IDs - (0,1,0,0) or 100 
3. Classes and Pseudo Classes - (0,0,1,0) or 10
4. Elements and Pseudo Elements (0,0,0,1) or 1 - Lowest Specificity

You might have noticed how these values are derived. Only the position that corresponds to that group is set to 1, all other positions are set to their default values i-e: 0.  So you don't need to remember their specific values, just remember the hierarchical sequence. 

Now that we have a basic understanding of Specificity and their weights. We can now solve an actual example, suppose that we have the following selector:

html #graftalks .blog a:hover { }

We're only concerned about the selector, not their property:value (declaration), so I didn't write anything inside the declaration area.

- We'll start off again with default values (0,0,0,0). In the above rule, we can clearly see that  HTML is an element so we'll add 1 (weight for Element selectors) to the Element position which will result in (0,0,0,1). 

Weight Calculation after Step # 1


- Then there's an ID selector, #graftalks. Because of this, we'll add 1 to the ID position. Our updated weight values will become (0,1,0,1).

Weight Calculation after Step # 2

 - After that there's a Class selector, .blog. We'll add 1 to the Classes position. Our updated weight values will become (0,1,1,1).

Weight Calculation after Step # 3

 - After that there's another Element selector, a. We'll add 1 to the Elements position. Our updated weight values will become (0,1,1,2).

Weight Calculation after Step # 4

- And finally, there's a Pseudo Class selector, :hover. We'll add 1 to the Pseudo Classes  position. Our final updated weight values will become (0,1,2,2) or 122.



Example of Specificity calculation


Case # 2: When Selectors have Unequal Specificity

We now know that the last CSS rule overrides any of its previously defined conflicting rules and hence the browser will prefer the most specific rule. (as explained in case 1). 

Assuming that you've understood everything explained above, the second case won't be a problem for you now. This case deals with CSS rules that have unequal specificity weights. The only thing to remember is that the most specific rule will always win out.

Let's take a look at an example.

Suppose we have two conflicting rules for an html element as shown below:

HTML

<div id="box">
  <p class="paragraph">Hello World!</p>
  <a href="#" class="link">I'm a Link</a>
</div><!--end .box-->


CSS


a.link {
  background: red;
  color: #fff;
  padding: 10px;
}

#box a.link {
  background: gray;
}

a:hover.link {
  background: yellow;
  color: #000;
}

According to the HTML and CSS written above, two CSS rules (highlighted in blue) are trying to apply their properties to the <a> tag. At this point, the browser will calculate their specificity and the one with higher specificity will win. Let's see how much weight these rules contain.


Specificity calculation for a.link rule


It is quite evident that #box a.link has higher specificity than a.link. Therefore, properties defined inside #box a.link will be applied to the <a> element. Following is the output:
Specificity calculation for #box a.link rule
Final Output with some explanation
It should be noted that #box a.link selector only overrides those properties that conflict with a.link. Other than that, it inherits the remaining properties from the a.link selector. That's the reason why we see 10 pixels of padding and a white colored text on the <a> element.

Important Notes

  • Universal selector (*) has no specificity value.
  • If we use !important value, it will override all conflicting rules regardless of the CSS Specificity rules mentioned above. That's why it is always recommended to use this value ONLY for testing specificity purposes and should be removed after testing.
  • :first-line is a Pseudo Class but unlike other classes, it gets (0,0,0,1).
  • Use minimum number of selectors while writing a CSS rule.
  • Try to use an ID selector to increase the specificity of a selector.
  • It is a best practice to follow the LVHA (link, visited, hover, active) sequence when styling anchor tags.
  • ID selectors have higher specificity than attribute selectors.

No comments:

Post a Comment