Himanshu SharmaCOBOX Technologies
Let’s talk

CSS · Lesson 4 of 29

The cascade and specificity

Understand why one CSS rule beats another, how to calculate specificity, and how to fix styles that refuse to apply without reaching for !important.

13 minGetting startedUpdated 16 October 2026

By the end of this lessonPredict which of several conflicting CSS rules will win, and fix a rule that loses.

When rules disagree

In real projects, several rules often target the same element and set the same property to different values. The browser needs a fair way to decide which one wins. That process is the cascade, and it is the "C" in CSS.

html
<style>
  p { color: grey; }
  .intro { color: teal; }
  p { color: purple; }
</style>
<p class="intro">Which colour am I?</p>
<p>And which colour am I?</p>

The first paragraph is teal, the second purple. Why teal beats a rule written later is what this lesson explains.

The three questions the browser asks

For each property of each element, the browser looks at every matching declaration and asks, in order:

  1. Where does it come from, and is it !important? Your styles beat the browser's built-in defaults. !important declarations beat normal ones.
  2. How specific is the selector? A more specific selector wins.
  3. Which came last? If the first two are tied, the declaration that appears later in the CSS wins.

The browser only moves to the next question when the previous one is a tie. In the example, both color rules for the first paragraph are normal author styles (tie), but .intro is more specific than p, so it wins before order is even considered. For the second paragraph only the two p rules match; they tie on specificity, so the later one, purple, wins.

Calculating specificity

Think of specificity as three numbers, written (IDs, classes, elements):

Part of the selector Adds to
#id the first number (IDs)
.class, [attribute], :hover and other pseudo-classes the second number (classes)
p, div, ::before and other elements/pseudo-elements the third number (elements)
*, combinators ( , >, +, ~) nothing
Selector Specificity
p (0, 0, 1)
nav a (0, 0, 2)
.button (0, 1, 0)
a:hover (0, 1, 1)
.menu .item a (0, 2, 1)
#header (1, 0, 0)
#header .logo (1, 1, 0)

Compare from the left: the first number that differs decides. That is why one ID, (1,0,0), beats ten classes, (0,10,0). The numbers do not carry over like ordinary arithmetic.

Two special cases: :not(...), :is(...) and :has(...) count as the most specific selector inside their brackets, and :where(...) always counts as zero, which makes it useful for low-priority default styles.

Inline styles and !important

A style="..." attribute on an element beats any selector in a stylesheet. Above that sits !important:

css
.price { color: crimson !important; }

!important makes a declaration jump ahead of all normal ones. It has a few legitimate uses, such as a utility class that must always work, or overriding a third-party widget you cannot edit. As a habit it is harmful: the next person who needs to change the colour has to use !important too, and soon nothing can be changed without it.

Debugging a style that will not apply

html
<style>
  #sidebar a { color: navy; }
  .highlight { color: orange; }
</style>
<div id="sidebar">
  <a href="#" class="highlight">Why am I not orange?</a>
</div>

The link stays navy because #sidebar a is (1,0,1) and .highlight is only (0,1,0). Some honest fixes, best first:

  1. Lower the winning selector. Style the sidebar with a class (.sidebar a, (0,1,1)) instead of an ID. Many teams avoid IDs in CSS for exactly this reason.
  2. Make your selector match the context: .sidebar .highlight (0,2,0) now beats .sidebar a (0,1,1).
  3. Move your rule later, if the specificity is already equal.

To see what is going on, right-click the element and choose Inspect. The Styles panel lists every matching rule, most specific first, with losing declarations crossed out. This one habit saves hours.

Common mistakes

  • Fighting specificity with more and more selectors (body div.main .content ul li a), making the next override even harder.
  • Styling with IDs, which are very hard to override.
  • Reaching for !important before inspecting the element.
  • Forgetting that order only matters on a tie. Moving a weaker rule to the bottom of the file does not help.

Exercise

Without running it first, predict the colour of each item, then press Try it on the example above or paste this into an editor to check:

html
<style>
  li { color: black; }
  .list li { color: green; }
  .item { color: blue; }
  ul li.item { color: red; }
</style>
<ul class="list">
  <li>One</li>
  <li class="item">Two</li>
</ul>
Show the answer
  • One is green. Only li (0,0,1) and .list li (0,1,1) match it, and the class wins.
  • Two is red. Four rules match: li (0,0,1), .item (0,1,0), .list li (0,1,1) and ul li.item (0,1,2). No IDs anywhere, and the top three all have one class, so the third column decides: ul li.item has two elements, more than any other, and wins.

If you predicted green for Two, look again at the third number. Specificity compares each column in turn, and a tie in one column is broken by the next column, not by source order.

Key points

  • When rules conflict, the browser compares origin and importance, then specificity, then source order.
  • Specificity is counted as three numbers: IDs, then classes/attributes/pseudo-classes, then elements.
  • With equal specificity, the rule that appears later wins.
  • Inline styles beat stylesheet rules; !important beats almost everything and should be a last resort.
  • Developer tools show every rule that matched an element and cross out the ones that lost.

Check yourself

1. Which selector is more specific: .menu a or #nav?
2. Two rules have the same specificity and set different colours. Which wins?
3. What is the specificity of ul li.active a?
4. A style will not apply and you are tempted to add !important. What should you try first?

Free tools for this lesson