monkinetic weblog

Steve Ivy's Weblog - Since 1999 - XII Ed.

CSS, Ids, >, and IE

(Geekery ahead)

Wow. I just got schooled in the specifics (pun intended) of CSS cascade specificity. As usual, the trouble started when IE would not render a layout properly. After hunting down a bug related to IE's non-support for the child selector (>), I found that I could not get some descendant selelectors to override properly.

Firstly, here's the structure and css I was using initially:

HTML:

ol#container
    li
        ul.subcontainer
            li

CSS:

ol#container>li { float: left; ... }

Fireflox floated the LIs perfectly, while IE completely ignored the float on the intial LI. After hunting down the fact that IE does not support that initial child selector, I removed it and went about overriding those styles to reset them to default values for descendant LIs.

ol#container li { float:left ... }

So, that fixed the initial problem, but now both Firefox and IE were floating both the initial LIs and all descendant LIs. I tried selectors of varying specificity, trying to override those first styles, and nothing worked.

So, next step was to figure out what the cascade was doing. I poked around the W3C CSS spec until I found the section on specificity. Lo and behold, I discovered that id selectors are like the "bunker-busters" of the CSS world:

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 a-b-c (in a number system with a large base) gives the specificity.

Some examples:

* {} / a=0 b=0 c=0 –> specificity = 0 /

LI {} / a=0 b=0 c=1 –> specificity = 1 /

UL LI {} / a=0 b=0 c=2 –> specificity = 2 /

UL OL+LI {} / a=0 b=0 c=3 –> specificity = 3 /

H1 + [REL=up]{} / a=0 b=1 c=1 –> specificity = 11 */

UL OL LI.red {} / a=0 b=1 c=3 –> specificity = 13 /

LI.red.level {} / a=0 b=2 c=1 –> specificity = 21 /

#x34y {} / a=1 b=0 c=0 –> specificity = 100 /

Adding an id to a css selector adds 100 to the specificity value, making it almost impossible, short of another id selector, to override. The final fix involved removing the id selector on the initial selector, then I was able to override the LI styles later on the css. Whew!

My name is Steve Ivy and I write about technology, the open web, social software, and general nerdity on monkinetic.com. You should follow me on Twitter or subscribe to this blog if you like what you're reading. I spend my days hacking Movable Type, python, Django, and various other efforts at Wallrazer. This is my personal site.