Cascades in CSS

Nick Gard
Nov 5, 2015 · 4 min read

You probably know that CSS stands for “Cascading Style Sheets” but what exactly is cascading? Is it the styles? The selectors? Or dare I say, the specificity? (Actually, I can’t say that… it’s a horribly difficult word to pronounce.) It turns out that there are actually two kinds of cascades in CSS: one happens in the DOM and the other happens in a CSS file. Both are cascades of properties (styles) and are affected by specificity.

Property Inheritance

The first type of cascade is when inheritable properties are applied to an element and are inherited by its children. I made the claim earlier that this cascade happens in the DOM but that is not really true: it happens because of the DOM. How the page is structured affects how the styles cascade through it. If you apply a color, say “red,” to a paragraph element, then every child will inherit that color as well.

<p style="color:red">
All of this text is red <b>and so is this!</b>
Every child of the paragraph element inherits its color as long as no styles are <em>directly applied to them.</em>
</p>

This comes in handy, since you can apply global styles to the body or html tag and expect those properties to flow through your page, acting as the default styles. You generally want to do this with font properties: color, font, font-size, font-weight, etc.

That’s not the end of the story, though. Inheritance is the lowest specificity that can be authored. True, inherited properties trump user-agent stylesheets but even the most generic, least specific authored rule (using the universal selector, *) with that property will trump it. This can be useful.

Rule Declaration Order

The second cascade occurs because of the order in which rules are declared. This is the cascade most developers think about and talk about. (Maybe I should have called this one the “first cascade.”) This cascade matters when a ruleset is written below a similarly specific ruleset in the stylesheet and overwrites the previous properties. For instance, if you write two rulesets whose selectors both consist of a single class name and both set the border-color property and are both applied to the same element, the ruleset written lower in the stylesheet will trump the prior declaration.

<style>
p {
border-width: 2px;
border-style: solid;
}
.blueBorder {
border-color: blue;
}
.errorBorder {
border-color: red;
}
</style>
<p class="errorBorder blueBorder">I have a red border</p><!--
Again, this cascade only applies when the following conditions are met:
1. Both rulesets have equal specificity
2. Both rulesets are applied to the same element
3. Both rulesets implement the same property
-->

This cascade can cause problems in any project with a moderately large stylesheet. The problems generally arise because many projects have multiple stylesheets that are concatenated together during a build process. If this concatenation is not done in the correct order, this cascade can work against you. To use this cascade to your favor, implement the most generic styles first (CSS resets, normalizations, universal/global styles) and then implement increasingly more targeted selectors later (e.g. “.component” first, followed by “.enhancement-for-component” later). Or use BEM, SMACSS, OOCSS or any number of CSS conventions that tend to bypass this issue by using only a single, very targeted class name selector.

!important declarations

I have told people in the past that !important declarations “break the cascade.” That is not correct. This nuclear option serves to reset the cascade (the second kind), but only for the one property that it is set on. Now all of the previous rules regarding specificity and ruleset ordering will apply to all rulesets targeting the same element and declaring the same property with !important.

Using !important will not affect the first kind of cascade. By that, I mean that the nuclear aspect of the style will not flow down to the child elements of the targeted element. The property will be applied to the children via the first cascade (inheritance) but will be easily overwritten by even the most generic selector. To see all of this in action, look at the pen below:

Conclusion

The cascades of CSS are not evil. Don’t work against them and curse them for your effort. Work with them. Feel the Force flow through you. Some rules of thumb:

  1. Apply inheritable properties as far up the DOM as you can and let them flow downward. For instance, rather than style each <li> by targeting them, target the <ul> (or <ol>) element and apply your styles there, letting the <li> elements inherit the desired styles.
  2. Write your rulesets in this order: CSS resets (get rid of cross-browser differences), CSS Normalize (apply some lightly opinionated styles like “* { box-sizing: border-box; }”), Global styles (e.g. font styles applied to the <body> and allowed to be inherited from there), then Component styles (again, targeting the uppermost container elements and using the least-specific rules first, getting more specific later).
  3. Keep specificity low. I highly recommend a CSS convention like BEM, SMACSS or OOCSS. Avoid using id selectors as no amount of class selectors will trump it.
  4. Use !important declarations sparingly. Here’s a good litmus test: if I ask why you have used !important and your answer is something like, “it doesn’t work if I remove it,” then it’s not being used correctly. Don’t be that person. Remove the declaration, do your debugging and fix the real issue (it’s probably cascade- or specificity-related.)

Nick Gard

Written by

Nick Gard

Web Developer, Oregonian, husband

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade