Recently I’ve been working on a SitePoint project: The Ultimate CSS Reference (Coming soon! If you’re into CSS it’s going to rock your world). While researching the shadowy corners of the web for traces of arcane CSS lore, I’ve realized that a lot of information about CSS on the web is in dire need of an update.
Between 2001 and the present we’ve had an explosion in knowledge and general understanding of CSS, the web is full of tutorials, articles and blog posts written during this era of enlightenment. But, time moves on and browsers improve. The level of CSS support in modern browsers is pretty darn good and just as an intimate knowledge of CSS hacks is fast becoming redundant so is a lot of that material. In fact, some of it is down right misleading and your search results are bound to be chock full of well intentioned, but out-of-date information.
Among the pages of arcane CSS lore you’ll find something called the CSS cascade; the thing that ultimately decides what each element’s style will eventually be. It has a reputation for being difficult to understand and is often the cause of those frustrating, obscure CSS problems when what happens in the browser is nothing like what you were expecting to happen. The amount of misinformation on the web certainly doesn’t help, so this is my small effort to correct the situation: putting to rest two of the biggest myths about the CSS Cascade.
Myth: Embedded styles take priority over external styles and inline styles take priority over embedded styles.
As far as browsers are concerned it makes no different how CSS is linked to a document; all three of these methods are considered to have the same origin: the author style sheet. What causes one to overwrite the other has nothing to do with how they are linked to the document.
If importance and specificity are equal the only thing that matters is source order; when a style sheet
link element appears after the
style element (the embedded style), in the document’s
head, the external styles overwrite the embedded styles. I think this myth developed because generally people put their
link elements before their
Inline styles overwrite identical style declarations in other style sheets only because they have a higher specificity (see below), but important declarations (see below as well) overwrite inline styles no matter where the declarations come from—even external style sheets.
Myth: Specificity can be represented by a total score.
You’ve probably seen this formula before:
specificity = number of IDs * 100 + number of classes * 10 + number of elements * 1
So a selector like
p.introduction would have a selector score of 11 (10 + 1). While certainly easy to understand it can be very misleading; you may begin to think that if you have 10 element names in your selector then it’s equivalent to 1 class name and that’s just plain wrong. This myth is probably the legacy of the badly worded explanations in the older CSS1 and 2 specs.
1 ID selector will always have a higher specificity than any number of class selectors, even a million class selectors! Once the cascade reaches the point of having to sort two or more property declarations by specificity, it does so like this:
- Is one an inline style? It wins! If none are inline proceed to b.
- Count the number if IDs in the selectors. The highest score wins! Same score? Proceed to c.
- Count the number of attributes, class names and pseudo-classes. The highest score wins! Same score? Proceed to d.
- Count the number of element names or pseudo-elements. The highest score wins!
If they have the same score in the last step then they have the same specificity and source order dictates which one wins (the one that comes last in the source).
The CSS2.1 specification would have expressed the result of the counting above in the form
a,b,c,d (a = 1 if true, 0 otherwise). So an inline style has a specificity of
1,0,0,0 while a selector like
p.introduction has a specificity of
0,0,1,1 (one class and one element name). You can’t just remove the commas.
This also puts to rest a few other minor misconceptions:
Wrong: A child selector like
div>phas a higher specificity than a descendant selector:
div p. From the process above you can see that combinators are not even included; they make no difference. Those two selectors have the same specificity
0,0,0,2(2 element names). The universal selector:
*, is also ignored.
Wrong: A selector like
#someidhas a higher specificity than
p#someidbecause the ID selector comes first. The order makes no difference, just count the number of components in the selector.
#someidhas a specificity of
p#someidhas a higher specificity of
!importantdeclaration has a higher specificity than a normal one. As you’ll see below, specificity has nothing to do with it.
- Wrong: an inherited property has a lower specificity than a declared one. Again, as you’ll see below, specificity has nothing to do with it. In fact inheritance has nothing to do with the cascade at all!
Gettin’ Cozy With The Cascade
The CSS cascade is easier to understand than you think, and once you get it, your understanding of CSS takes a huge leap.
Here’s the cascade in 4 simple steps; this is the process that occurs for each CSS property for each web page element:
- Gather all the declarations for the property from all sources. This includes default browser styles and custom user styles, as well as author style sheets. If there is more than one, proceed to 2.
- Sort the declarations by importance and origin in the following order (from lowest to highest priority):
- user agent style sheets (default browser styles)
- normal declarations in a user style sheet (a user’s custom style sheet)
- normal declarations in an author style sheet (web page style sheets; external, embedded, and inline styles)
!importantdeclarations in an author style sheet
!importantdeclarations in a user style sheet
The one with the highest priority wins. If more than one have the same priority then proceed to 3.
- Sort by selector specificity (see the process above). The one with the most specific selector wins. If no clear winner, proceed to 4.
- The one that comes last in the source wins!
If the cascade does not set a CSS property on an element, then the browser will fall back to using an inherited property from the element’s parent (this only happens for some properties), otherwise the property is set to the CSS default value.
That’s it! Not too difficult eh? Now you understand something about CSS that once only the gurus knew! Now, if you’re game, get up and dance a jig to celebrate. I certainly did!
*runs around with t-shirt over head, hands in the air, screaming WOOOOHOOOOO!*
OK, try not to picture that in your head…
If you want to really get to know CSS in a way that won’t melt your brain try the SitePoint video: The CSS Video Crash Course.