Learn CSS · Lesson 1 of 7 · 7 min
The cascade & specificity
Sooner or later, two CSS rules will try to style the same element in two different ways. One says the text is blue, another says it is red. Only one can win. The cascade is the set of rules the browser uses to pick the winner, and once you understand it, those mysterious "why is my style being ignored?" moments simply stop happening.
What "the cascade" actually means
The "C" in CSS stands for Cascading Style Sheets, and that word is the whole point. Your styles
arrive from many places at once: the browser's own defaults, your stylesheets, maybe a framework, maybe an inline
style attribute. Many of those rules will overlap and target the same element. The cascade is the
browser's tie-breaking algorithm, the procedure it runs to decide, property by property, which value an element
finally gets.
It helps to know that the cascade resolves one property at a time. If one rule sets the
color of a paragraph and a different rule sets its font-size, there is no conflict:
each property is decided on its own. Conflict only happens when two rules set the same property on the
same element. That is when the deciding factors below come into play.
The three deciding factors, in order
When two rules set the same property on the same element, the browser walks through three tests, in this exact order. It stops at the first test that produces a clear winner.
- Importance and origin. First the browser sorts by where a rule comes from and whether it is
marked
!important. An author rule flagged!importantbeats a normal author rule, which beats the browser's defaults. For everyday CSS you write, this rarely decides things, so move on. - Specificity. If importance and origin tie, the browser scores how specific each selector is. A more specific selector wins. This is the test that decides most real conflicts, so it is worth understanding well (next section).
- Source order. If specificity also ties, the last rule wins: whichever appears later in the stylesheet (or in the later-loaded stylesheet) takes effect. This is why the order of your rules matters, and why pasting a rule at the bottom of the file can quietly override an identical one above it.
Read that order again, because it is the part people get backwards: specificity is checked before source order. A rule earlier in the file can still beat a later one if it is more specific. Source order only breaks ties between selectors of equal specificity.
How specificity is scored
Every selector earns a specificity score made of three numbers, often written as (a, b, c). You can
think of them as three columns, compared left to right like a version number:
- a counts ID selectors (
#header). - b counts classes, attribute selectors and pseudo-classes
(
.btn,[type="text"],:hover). - c counts element (type) selectors and pseudo-elements
(
div,p,::before).
To compare two selectors, compare the a values first. Higher wins. Only if they are equal do you look
at b, and only if those tie do you look at c. The columns do not "carry": eleven classes
score (0, 11, 0) and still lose to a single ID at (1, 0, 0), because the ID column is
compared first. A few examples:
p -> (0, 0, 1) one element
.note -> (0, 1, 0) one class
nav a -> (0, 0, 2) two elements
.menu a -> (0, 1, 1) one class + one element
a.menu:hover -> (0, 2, 1) one element + class + pseudo-class
#hero -> (1, 0, 0) one ID
#hero .title -> (1, 1, 0) one ID + one class Two special cases worth remembering. An inline style (a style="..." attribute on the
element itself) sits above the ID column and beats any selector in your stylesheet. And the universal
selector * and combinators like > or + add nothing to
the score, so they never raise specificity.
See it: a specificity face-off
Below, three rules all try to color the same word. An element selector scores (0, 0, 1), a class
scores (0, 1, 0), and an ID scores (1, 0, 0). The browser compares the ID column first,
so the ID wins outright, even though all three rules are valid and the class and element rules appear later in the
source order. Source order never gets a chance to matter here, because specificity already settled it.
strong (0, 0, 1) teal overridden .word (0, 1, 0) orange overridden #target (1, 0, 0) indigo winner The selected word is indigo.
The HTML behind that word is just <strong id="target" class="word">word</strong>, so all
three selectors genuinely match it. The element is rendered in the winning color (indigo) because the ID selector
out-scores the class and the element. Change nothing but the selectors, and you change the winner.
!important and why to avoid it
There is one escape hatch that jumps the whole queue: adding !important to a declaration. An
!important value beats every normal declaration no matter how specific, because it is sorted in the
very first test (importance), before specificity is ever considered.
.btn { color: white !important; } /* wins over #id, inline, everything normal */ It is tempting, and it is a trap. The trouble is that !important breaks the orderly cascade. The only
way to override one !important is with another !important that is more specific,
so a single one tends to breed more, and soon your stylesheet is an arms race nobody can reason about. Reach for it
only as a genuine last resort (for example, forcing a style over a third-party widget you cannot edit). In your own
code, a cleaner selector almost always solves the problem without it.
Practical rules of thumb
You do not have to compute specificity scores all day. In practice, a few habits keep conflicts from ever starting:
- Keep specificity low and flat. Prefer a single class (
.card-title) over long chains (section.content div.card h2). Low, even specificity means new rules are easy to override later, in the obvious way. - Style with classes, not IDs. IDs are great as anchors and JavaScript hooks, but as style selectors they are heavy: one ID outranks any number of classes. Save yourself the future fight and target classes instead.
- Let source order do the work. When two rules have equal specificity, the later one wins, so you can override a style simply by writing the new rule afterward. Keep your stylesheet ordered from general to specific and the cascade cooperates with you.
- Treat
!importantas a fire alarm. If you feel you need it, that is usually a sign the selectors above it are too specific. Lower them instead.
Practice
A couple of minutes each, and each one makes the cascade concrete. Use a blank CodePen, your browser's DevTools, or one of our tools to experiment:
- Make a paragraph with both a class and an ID. Write three rules (one element selector, one class, one ID) that
each set a different
color. Predict which color shows, then check. Now add!importantto the element rule and watch it leapfrog the ID. - Write two rules with the same selector (for example
.btntwice), each setting a differentbackground. Swap their order in the file and confirm the last one always wins. That is source order breaking the tie. - Open a real site, inspect a heading, and find one declaration in the Styles panel that is struck through. Figure out which rule beat it, and whether it won on specificity or on source order.
The mental model to keep
- The cascade decides conflicts one property at a time, using three tests in order.
- The order is importance and origin, then specificity, then source order (last one wins on a tie).
- Specificity is scored
(IDs, classes, elements)and compared column by column, left to right. - Keep specificity low, prefer classes, lean on source order, and avoid
!important.
That is the cascade. It is the rulebook behind every "winning" style on the page, and knowing it turns CSS from guesswork into something predictable. Next, see how styles travel down the page from parents to children in Inheritance: Parents & Children.