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? 🙂