Chapter 2. Understanding the Cascade

There’s nothing worse than writing a bunch of CSS only to test it out and find that other styles are being applied instead of the ones you intended. To better understand why this happens, this chapter explains how web browsers determine which styles to apply to which elements using the cascade.

What Is the Cascade?

The cascade is the method by which the browser determines how styles should be applied to elements. Because multiple styles can be applied to the same element, understanding how the cascade works is important in the event that styles are not applied they way you expect them to be. Luckily, it’s not as complicated as it sounds; styles are applied based on the specificity of their selectors as well as the order in which they appear.

Selector Specificity

Specificity is a measure of how precisely elements are identified based on the CSS selectors used. Specificity is calculated by analyzing the different types of selectors (except the universal selector, *) that are combined to select an element. A specificity is determined by plugging numbers into (a, b, c, d):

  1. If the styles are applied via the style attribute, a=1; otherwise, a=0.

  2. b is equal to the number of ID selectors present.

  3. c is equal to the number of class selectors, attribute selectors, and pseudoclasses present.

  4. d is equal to the number of type selectors and pseudoelements present.

When all of these calculations are completed, those numbers are concatenated to give the specificity. To make this a bit more concrete, consider the selector in Example 2-1.

Example 2-1. An example ruleset to calculate specificity for
#nav-global > ul > li > a.nav-link {
    color: #000000;
}

Using the algorithm just defined, we can determine that this selector has a specificity of (0,1,1,3):

  1. The styles are not applied via the style attribute, so a=0

  2. There is 1 ID selector (#nav-global), so b=1

  3. There is 1 class selector (.nav-link), so c=1

  4. there are 3 type selectors (ul, li, and a), so d=3

When comparing the specificity of selectors, the selector that has the largest number farthest to the left has the highest specificity. If the two leftmost numbers being compared are equal, then the next number to the left is used, and so on. For example, a specificity of (1, 0, 0, 0) is higher than (0, 1, 1, 3), in the same way (0, 2, 1, 3) is higher than (0, 1, 1, 3). However, a specificity of (0, 1, 1, 3) is lower than a specificity of (0, 1, 1, 4) or (0, 1, 2, 0). Example 2-2 gives some more examples of calculating specificity.

Example 2-2. Examples of calculating specificity
/**
 * This selector has a specificity of (0,0,2,2) because there are:
 *   0 inline styles
 *   0 IDs
 *   1 class (.title), 0 attribute selectors, and 1 pseudoclass (:first-child)
 *   2 type selectors (li, h2)
 */
li:first-child h2 .title {}

/**
 * This selector has a specificity of (0,1,2,1) because there are:
 *   0 inline styles
 *   1 ID (#nav)
 *   1 class (.selected), 0 attribute selectors, and 1 pseudoclass (:hover)
 *   1 type selector (a)
 */
#nav .selected > a:hover {}

/**
 * This selector has a specificity of (0,1,2,3) because there are:
 *   0 inline styles
 *   1 ID (#nav)
 *   1 class (.selected), 0 attribute selectors, and 1 pseudoclass (:hover)
 *   3 type selectors  (html, body, a)
 */
html body #nav .selected > a:hover {}

Ruleset Order

Ruleset order describes the location of a CSS ruleset in a stylesheet. When two declaration blocks that have selectors of equal specificity attempt to style a property on the same element, the properties in the declaration block that appears later in the stylesheet have precedence. This means that the color property for the element styled in Example 2-3 is assigned the value #000000 because a declaration block with the same specificity appears later in the stylesheet and assigns that color.

Example 2-3. Assigning a value to the color property with a later declaration block
<!doctype html>
<html>
  <head>
    <title>Inline Styles and Specificity</title>
    <style type="text/css">
      #nav-global > ul > li > a.nav-link {
        color: #FFFFFF;
      }
      #nav-global > ul > li > a.nav-link {
        color: #000000;
      }
    </style>
  </head>
  <body>
    <nav id="nav-global">
      <ul>
        <li>
          <a href="#" class="nav-link">Link</a>
        </li>
      </ul>
    </nav>
  </body>
</html>

Inline CSS and Specificity

Specificity and ruleset order are paramount in determining how styles are applied to an element, unless it has inline styles applied via the style attribute. In Example 2-4 there are inline styles present on the anchor tag. Because the color property is being set on the actual element, that style will be applied. No matter how specific the selector is in either a <style> block or an external stylesheet, it will never be more specific than styles on the actual element.

Example 2-4. Assigning a value to the color property with inline CSS via the style attribute
<!doctype html>
<html>
  <head>
    <title>Inline Styles and Specificity</title>
    <style type="text/css">
      #nav-global > ul > li > a.nav-link {
        color: #000000;
      }
    </style>
  </head>
  <body>
    <nav id="nav-global">
      <ul>
        <li>
          <a href="/" class="nav-link" style="color: #1200FF;">Link</a>
        </li>
      </ul>
    </nav>
  </body>
</html>

Overriding the Cascade with the !important Declaration

The only way to ensure that styles present in a <style> block or an external stylesheet are more specific than any other styles (including inline styles applied with the style attribute) is to append !important to a declaration. When !important is appended to a declaration, it indicates to the browser that that declaration should be used for elements that match its containing ruleset’s selectors, regardless of the properties applied by selectors that have a higher specificity. When multiple declaration blocks that select the same elements make use of !important, the one that appears last is applied.

In Example 2-5, for instance, the anchor tag appears with white (#FFFFFF) text because the !important declaration is used in the first ruleset. If !important was used in both rulesets, the anchor tag would have black (#000000) text because that ruleset appears last. If !important was not used at all, the anchor tag would have blue (#1200FF) text because inline styles have the highest specificity. Note that !important cannot be used on styles applied with the style attribute (i.e., <a href="/" style="color: #1200FF !important">Link</a>).

Example 2-5. Assigning a value to the color property with an earlier declaration block using !important
<!doctype html>
<html>
  <head>
    <title>Inline Styles and Specificity</title>
    <style type="text/css">
      #nav-global > ul > li > a.nav-link {
        color: #FFFFFF !important;
      }
      #nav-global > ul > li > a.nav-link {
        color: #000000;
      }
    </style>
  </head>
  <body>
    <nav id="nav-global">
      <ul>
        <li>
          <a href="/" class="nav-link" style="color: #1200FF;">Link</a>
        </li>
      </ul>
    </nav>
  </body>
</html>

Chapter Summary

With a solid understanding of the cascade and how to calculate specificity, learning more about refactoring will be a bit easier because it all hinges on these ideas. In the coming chapters, be sure to think about how the cascade plays into each concept and it will become much more apparent how everything is tied together. Next, we’ll shift gears a bit and look at some recommendations for how to write better CSS.