Cascade, Specificity & Inheritance

CSS · Lesson 5 of 55 7 min read Updated

Sooner or later you will write a rule that "does not work". Usually the rule is fine: another rule is simply winning. CSS has a clear, predictable process for choosing between conflicting declarations, and once you know it you can fix these problems in seconds instead of guessing.

This lesson explains the cascade, how specificity is scored, where !important fits, and how inheritance fills in values you never set.

The cascade in short

For every element and every property, the browser collects all the declarations that apply. If they disagree, it compares them in this order and stops at the first step that produces a winner:

  1. Origin and importance. Your style sheets beat the browser's defaults. A declaration marked !important beats normal declarations.
  2. Inline styles. A declaration in a style attribute beats any selector-based rule.
  3. Cascade layers. When you use @layer, later layers beat earlier ones. You will learn this in Cascade Layers.
  4. Specificity. The more specific selector wins.
  5. Order of appearance. If everything else is equal, the declaration that comes last wins.

Most everyday conflicts are decided at step 4 or step 5, so those are the ones to understand well.

Order of appearance

When two selectors are equally specific, the later one wins. This also applies across files: a style sheet linked later in the <head> comes after one linked earlier.

CSS
.notice { color: navy; }
.notice { color: teal; }   /* this one wins: same specificity, comes later */

Specificity

Specificity is a score the browser gives each selector. Think of it as three columns, written as (A, B, C):

  • A: the number of ID selectors.
  • B: the number of classes, attribute selectors and pseudo-classes.
  • C: the number of type selectors and pseudo-elements.

The universal selector * and combinators (space, >, +, ~) add nothing.

SelectorScore (A, B, C)
p(0, 0, 1)
nav a(0, 0, 2)
.card(0, 1, 0)
.card p::first-line(0, 1, 2)
a:hover(0, 1, 1)
input[type="email"](0, 1, 1)
#login .btn(1, 1, 0)

To compare two scores, look at column A first. The higher A wins, no matter what is in B and C. Only if A is equal do you compare B, and then C. So one ID beats any number of classes: (1, 0, 0) beats (0, 15, 0). These are not ordinary decimal numbers; a column never "carries over" into the next.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Specificity</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 1rem; }
    #fees .amount { color: green; }     /* (1, 1, 0) */
    .box .amount { color: orange; }     /* (0, 2, 0) */
    p.amount { color: purple; }         /* (0, 1, 1) */
    .amount { color: red; }             /* (0, 1, 0), last but weakest */
  </style>
</head>
<body>
  <div id="fees" class="box">
    <p class="amount">Semester fee: ₹18,500</p>
  </div>
</body>
</html>

The text is green. Even though the red rule comes last, #fees .amount has an ID, so it wins at the specificity step and order of appearance is never reached. Delete the first rule and run again: now .box .amount (0, 2, 0) wins over p.amount (0, 1, 1), because B is compared before C.

Pseudo-classes that take selectors

:is(), :not() and :has() take the score of the most specific selector inside their brackets. :where() always scores zero, which makes it handy for default styles that are easy to override. All four are covered in :has(), :is() and :where().

!important

Adding !important after a value makes that declaration jump ahead of all normal declarations:

CSS
.hidden {
  display: none !important;
}

This is acceptable for a small number of utility classes whose whole job is to always win, like the one above. Using it to win ordinary battles is a trap: the only way to beat an !important is another !important, and soon your style sheet becomes a fight nobody can follow. When a rule loses, first find out why it loses, then fix the selector or the order.

Inheritance

Some properties pass their value from a parent element to its children. This is inheritance, and it is why setting font-family once on body changes the font of the whole page.

Usually inheritedNot inherited
colormargin and padding
font-family, font-size, font-weightborder
line-heightbackground
text-alignwidth and height
visibility, cursordisplay

Text-related properties inherit; box and layout properties do not. Imagine the mess if every child copied its parent's border.

An important rule: an inherited value always loses to a declaration that targets the element directly, even a weak one. Specificity only compares rules that match the same element.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Inheritance</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 1rem; }
    #results { color: darkred; border: 2px solid darkred; padding: 0.5rem; }
    em { color: steelblue; }
  </style>
</head>
<body>
  <section id="results">
    <h2>Class 12 results</h2>
    <p>Priya scored <em>96 per cent</em> in Physics.</p>
  </section>
</body>
</html>

The heading and paragraph inherit dark red from the section, but the <em> is steel blue. The em rule is weak, yet it matches the <em> directly, while the ID rule only reaches it through inheritance. Notice also that the border is drawn once around the section; the children do not inherit it.

Controlling inheritance

Every property accepts these keywords:

  • inherit: take the parent's value, even for a property that does not normally inherit.
  • initial: use the property's starting value from the CSS specification (for color, that is usually black, not the browser's default for that element).
  • unset: act like inherit for inherited properties and initial for the others.
  • revert: go back to the browser's default style for that element.
CSS
button {
  font: inherit;      /* buttons do not inherit fonts by default */
  color: inherit;
}

Form controls are the classic case: browsers give them their own font, so font: inherit makes buttons and inputs match the rest of your page.

Common mistakes

  • Fighting with !important. Find the winning rule in the developer tools and lower its specificity instead.
  • Over-qualified selectors. body div.container ul li a is long and very specific. .nav-link is easier to read and easier to override.
  • Expecting specificity to beat inheritance. A direct rule on the child always wins over an inherited value.
  • Thinking 11 classes beat one ID. Columns never carry over; one ID wins.

Key points

  • The cascade compares origin and importance, inline styles, layers, specificity and finally order of appearance.
  • Specificity is scored as (IDs, classes/attributes/pseudo-classes, types/pseudo-elements) and compared column by column.
  • When specificity is equal, the declaration that appears last wins.
  • !important overrides normal declarations; keep it for rare utility classes.
  • Text properties like color and font-family inherit; box properties like margin and border do not.
  • inherit, initial, unset and revert let you control inheritance per property.

Exercise

Write the specificity score for each selector, then decide which colour a <a class="nav-link"> inside <nav id="top"> would get: nav a { color: black; }, .nav-link { color: blue; }, #top a { color: green; }, nav .nav-link { color: orange; }. Check your answer by building the page in the Try-it editor.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Which colour wins?</title>
  <style>
    nav a { color: black; }           /* (0, 0, 2) */
    .nav-link { color: blue; }        /* (0, 1, 0) */
    #top a { color: green; }          /* (1, 0, 1) */
    nav .nav-link { color: orange; }  /* (0, 1, 1) */
  </style>
</head>
<body>
  <nav id="top"><a class="nav-link" href="#">Home</a></nav>
</body>
</html>

The link is green. #top a is the only selector with an ID, so it wins in column A before classes or order are considered.

Quick Quiz

1. What is the specificity of .menu li:hover?

2. Two rules have exactly the same specificity. Which one wins?

3. A section with an ID sets color: red. A rule strong { color: blue; } exists. What colour is a <strong> inside the section?

4. Which property is NOT inherited by default?

Learn CSS with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call