The Cascade: Part 2 (Finally!)

Exactly one year ago from this day, I published part
1
in a series of articles about
CSS cascading and inheritance. However, due to various factors (mostly laziness),
the sequel never got published… Until now! Today, I’m going to take a break
from the XBL series of articles (which will resume in a day or so) and
finally publish the long awaited conclusion to this series. If you haven’t
read part 1 (or even if did a long time ago), I suggest you do so now
before continuing.

Following on from the first article in which we looked at how to find all
the style declarations that applied to each element, we’re going to show how
these are sorted by order of precedence, to determine which ones are applied
to the element.

Sorting

Steps 2, 3 and 4 of the algorithm deal with sorting the declarations into
the order of precedence. From the exercise in the part 1, we were left with
4 rule sets which applied to the p element in the sample document. For the
purpose of this exercise, I’m just going to add a few more declarations and
annotate them with their origin.

p { margin: 1em 0; } /* User Agent */
* { background: blue none !important; color: white !important; } /* User */
P { text-indent: 1em; text-align: left; } /* User */
p { text-align: justify; } /* User */
#content p { margin: .8em 0; line-height: 1.2; text-indent: 0 !important; } /* Author */
p { line-height: 1.4; } /* Author */

Step 2

Step 2 of the cascade says to sort the rules according to importance and origin.
The following is the
order of precedence specified in CSS 2.1
.

  1. user agent declarations
  2. user normal declarations
  3. author normal declarations
  4. author important declarations
  5. user important declarations

If we discard the selectors for now (they’ll be need again in step
3
below), we’re
left with a list of declarations. A declaration is a property and its associated
value. We can then proceed to sort them into the order specified.

  1. User Agent Declarations
    1. margin: 1em 0;
  2. User Normal Declarations
    1. text-indent: 1em;
    2. text-align: left;
    3. text-align: justify;
  3. Author Normal Declarations
    1. margin: .8em 0;
    2. line-height: 1.2;
    3. line-height: 1.4;
  4. Author Important Declarations
    1. text-indent: 0 !important;
  5. User Important Declarations
    1. background: blue none !important;
    2. color: white !important;

Step 3

Step 3 of the cascade then says to sort the rules with the same importance
and origin by the specificity of the selector. The details of calculating
the specificity
is out of scope for this article, but it has been covered by others.
See Andy Clarke’s Specificity
Wars
and Molly Holzschlag’s CSS2
and CSS2.1 Specificity Clarified
.

This step is important for cases where two declarations for the same property
have the same importance and origin. In this example, this occurs for both
user normal declarations (two text-align declarations) and author
normal declarations
(two line-height declarations).

For the user normal declarations, these are the rule sets involved:

p { text-indent: 1em; text-align: left; } /* User */
p { text-align: justify; } /* User */

As you can see, both have the same selector (p), which has a specificy of
0,0,1. So, sorting by specificity in this case makes no difference. However,
for the author normal declarations, these are the rule sets involved:

p { line-height: 1.4; } /* Author */
#content p { margin: .8em 0; line-height: 1.2; text-indent: 0 !important; } /* Author */

These 2 rule sets each use different selectors which have different specificity.
The selector #content p has a specificity of 101. The selector p has a specificity
of 0,0,1. Since 1,0,1 is a higher specificity than 0,0,1, the former takes
precedence. So the order of author normal
declarations
is changed to the following:

  1. margin: .8em 0;
  2. line-height: 1.4;
  3. line-height: 1.2;

Step 4

The forth and final step of the sorting process involves sorting declarations
which have the same importance, origin and specificity by the order they are
specified in the CSS. This is where the order of the user
normal declarations
from step 3 is resolved. In this example, given that I listed the declarations
in the order in which they appeared, no change needs to be made to the above
list.

In cases where there is more than one declaration for a property, the latter
declaration overwrites the former, which is effectively discarded. This leaves
the following list of declarations to be applied to element.

  1. text-align: justify;
  2. margin: .8em 0;
  3. line-height: 1.4;
  4. text-indent: 0;
  5. background: blue none;
  6. color: white;

This concludes the series about the cascade, but the related issue of inheritence
still needs to be addressed and I intend to do so at some point in the future.
However, I don’t expect that it will take another year before I do… But who
knows? 🙂