Link selector Specificity

While it’s always possible that you have a stupid browser– that’s not really for me to say– it is more often the case that the styles have simply been written in the wrong order. To ensure that you see your various link styles, you’re best off putting your styles in the order “link-visited-hover-active”, or “LVHA” for short. If you’re concerned about focus styles, they may go at the end– but wait until you’ve read this explanation before you decide.

The core of the situation is this: every selector has a specificity. If two selectors apply to the same element, the one with higher specificity wins. Thus:

 P.hithere {color: green;}    /* specificity = 1,1 */
 P {color: red;}              /* specificity = 1   */

So any paragraph which has a class of hithere will be green, not red. Both selectors apply, and both try to set the color, so the more-specific selector wins out.

Aside: this only comes into play when the same property is involved. Thus:

   P.hithere {color: green;}    /* specificity = 1,1 */
   P {font-family: Courier;}    /* specificity = 1   */

All paragraphs will be in Courier, but those with a class of hithere will be in Courier and green in color.

Okay, so how do pseudo-classes affect specificity? They all have equal weight, as it turns out. So the following styles all have the same specificity:

 A:link {color: blue;}        /* specificity = 1,1 */
 A:active {color: red;}       /* specificity = 1,1 */
 A:hover {color: magenta;}    /* specificity = 1,1 */
 A:visited {color: purple;}   /* specificity = 1,1 */

All of them can apply to a hyperlink, and in some cases, more than one will apply. For example, an unvisited link can be hovered and active at the same time as it’s an unvisited link. Since three of the above rules apply to the hyperlink, and the selectors all have the same specificity, then the last one listed wins. Therefore, the “active” style will never appear, because it will always be overridden by the “hover” style. Now consider a hyperlink which has been visited. It will always and forever be purple, because its “visited” style beats out any other state, including “active” and “hover.”

This is why the recommended order in CSS1 goes like this:

   A:link
   A:visited
   A:hover
   A:active

The first two can be in either order, actually, because a link can’t be both visited and unvisited at the same time. (:link means “unvisited”; and no, I don’t know why they didn’t call it that.)

This is also why CSS2 now allows the chaining of pseudo-classes. For example, you could write:

   A:visited:hover {color: maroon;} /* specificity = 2,1 */
   A:link:hover {color: magenta;}   /* specificity = 2,1 */
   A:hover:active {color: cyan;}    /* specificity = 2,1 */

They have the same specificity, but they apply to fundamentally different beasts, and so don’t conflict. You can get hover-active combinations, for example.

Finally, a word about how I represented specificity in this article. The specification simply concatenates numbers together, so that the first example in the post would have been written like this:

   P.hithere {color: green;}    /* specificity = 11 */
   P {color: red;}              /* specificity = 1  */

This implies a base-ten arithmetic. However, specificity calculations do NOT use base ten, a fact at which the specification hints but doesn’t come right out and say in big bold letters. If you chain fifteen simple selectors together, they still have a lower specificity than a simple class selector. Here’s how the specification would represent this:

   .hello {color: red;}    /* specificity = 10 */
    HTML BODY DIV UL LI OL LI UL LI OL LI UL LI OL LI {color: green;}
   /* specificity = 15 */

That “10” is actually a 1 followed by a zero, not “ten.” We could try representing the previous rules specificities in hexadecimal, like this:

   .hello {color: red;}    /* specificity = 10 */
   HTML BODY DIV UL LI OL LI UL LI OL LI UL LI OL LI {color: green;}
   /* specificity = F */

The only problem there is that if you try to add two more selectors to the second rule, then you get a specificity of 11, and once again we get confused. In fact, the counting base for specificity is effectively infinite, and so these kinds of comparison start to break down. So I’m switching over to comma-delimited specificity calculations in the future just to avoid more confusion.

CSS specificity

CSS: Specificity Wars

Join me, and together we can rule the galaxy as father and geeks!

A few weeks back in Cupertino, I saw Aaron explain how the specificity of CSS selectors is calculated in a way which I hadn’t seen before. Then today I came across a knotty problem while building XHTML and CSS templates for a new project where two selectors behaved differently to how I expected and I realised that I had not completed my training.

The Dark Side

My problem was a simple one, how to feed a transparent PNG image to browsers which support transparency and a GIF image to older browsers which don’t, without resorting to hacks. Here’s the markup,

<div id="nav-supp">
<p><a id="a-02" href="#webstandards-org">Top</a></p>
<!-- etc. -->
</div>

and my CSS starting point.

a#a-02 { background-image : url(n.gif); }
a[id="a-02"] { background-image : url(n.png); }

I had assumed that a modern browser would see and apply both rules (with the second overriding the first) and that an older browser which does not understand attribute selectors would see and apply only the first, ignoring the second. I was wrong. Modern browsers did not apply the PNG image as I expected. The reason? A standard id selector wins over an attribute selector in terms of the cascade. Dagnammit! I know I should have read the specs, but somehow that particular pleasure had escaped me. If I had, I might have learned that;

ID selectors have a higher specificity than attribute selectors. For example, in HTML, the selector #p123 is more specific than [id=p123] in terms of the cascade.

Sith Lords

A little Googling uncovered some rather dry reading on the subject of selector specificity (resources below).

First, let’s look back at what Lord Elasticus (Patrick Griffiths) wrote on the subject of specificity (with one or two minor changes to fit our nefarious purpose).

You give every id selector (“#whatever”) a value of 100, every class selector (“.whatever”) a value of 10 and every HTML selector (“whatever”) a value of 1. Then you add them all up and hey presto, you have the specificity value.

  • p has a specificity of 1 (1 HTML selector)
  • div p has a specificity of 2 (2 HTML selectors; 1+1)
  • .sith has a specificity of 10 (1 class selector)
  • div p.sith has a specificity of 12 (2 HTML selectors and a class selector; 1+1+10)
  • #sith has a specificity of 100 (1 id selector)
  • body #darkside .sith p has a specificity of 112 (HTML selector, id selector, class selector, HTML selector; 1+100+10+1)

If all of these examples were used, div p.sith (with a specificity of 12) would win out over div p (with a specificity of 2) and body #darkside .sith p would win out over all of them, regardless of the order.

Darth (Gez) Lemon quotes the W3C.

A selector’s specificity is calculated as follows:

  • count the number of ID attributes in the selector (= a)
  • count the number of other attributes and pseudo-classes in the selector (= b)
  • count the number of element names in the selector (= c)
  • ignore pseudo-elements.

Concatenating the three numbers abc (in a number system with a large base) gives the specificity.

Too much! For me, the W3C really is in a galaxy far, far away!