Specificity & Rules Conflict
Specificity is the score the browser gives every CSS selector to decide which rule wins when two rules target the same element and set the same property. Understanding it ends most CSS debugging mysteries.
The Specificity Score
Every selector gets a three-column score written as (A, B, C):
| Column | What counts | Example |
|---|---|---|
| A — IDs | Each #id in the selector |
#header → (1,0,0) |
| B — Classes |
Classes .foo, attributes [type],
pseudo-classes :hover
|
.card:hover → (0,2,0) |
| C — Elements |
Tag names p, div, pseudo-elements
::before
|
ul li → (0,0,2) |
Compare scores left to right. The first column where one selector beats the other is the winner. Column A always beats column B, column B always beats column C — no matter how large the numbers get.
Think of specificity like medals: gold (IDs), silver (classes), bronze (elements). One gold medal beats a hundred silver medals. Ten silver medals beat a thousand bronze medals. The count within each medal type only matters when two selectors have the same number of higher-ranked medals.
Calculating Specificity Step by Step
/* Selector A B C */
p /* 0, 0, 1 */
.warning /* 0, 1, 0 */
p.warning /* 0, 1, 1 */
#sidebar /* 1, 0, 0 */
#sidebar p /* 1, 0, 1 */
#sidebar .note /* 1, 1, 0 */
#sidebar .note p /* 1, 1, 1 */
nav ul li a:hover /* 0, 1, 4 */
To find the winner between two selectors, start at column A. If they're equal, move to B. If still equal, move to C. If all three are equal, source order decides (last rule wins).
Common Conflicts and Who Wins
/* Conflict 1: element vs class */
p { color: grey; } /* (0,0,1) */
.intro { color: blue; } /* (0,1,0) — wins */
/* Conflict 2: class vs ID */
.hero { color: blue; } /* (0,1,0) */
#hero { color: orange; } /* (1,0,0) — wins */
/* Conflict 3: two classes vs one class + one element */
.card.featured { color: gold; } /* (0,2,0) — wins */
.card p { color: grey; } /* (0,1,1) */
/* Conflict 4: many elements vs one class */
html body main section article p { color: red; } /* (0,0,6) */
.intro { color: blue; } /* (0,1,0) — wins */
/* One class ALWAYS beats any number of elements */
The Universal Selector and Combinators
The universal selector * and combinators
(>, +, ~, space)
contribute zero specificity.
* { color: red; } /* (0,0,0) — zero specificity */
* > p { color: blue; } /* (0,0,1) — only the p counts */
div + p { color: green; } /* (0,0,2) — div and p count, + doesn't */
Inline Styles Beat Everything
An inline style attribute has a specificity of
(1,0,0,0) — a fourth column that beats any
selector, no matter how specific.
#page #sidebar .card p { color: teal; } /* (2,1,1) — very high */
<!-- inline style still wins over any selector -->
<p style="color: red;">Always red.</p>
!important Overrides Specificity
!important sits above the normal specificity system
entirely. Even a low-specificity rule with
!important beats a high-specificity rule without it.
#sidebar .note p { color: teal; } /* (1,1,1) — high specificity */
p { color: red !important; } /* (0,0,1) + !important — wins */
When two !important rules conflict, specificity decides
between them normally. This is one reason chains of
!important declarations become impossible to manage.
:is(), :not(), :has() and Specificity
Modern pseudo-classes take the specificity of their most specific argument:
/* :is() uses the specificity of its most specific argument */
:is(#sidebar, .card) p { color: blue; }
/* (1,0,1) — because #sidebar is the most specific argument */
/* :not() uses the specificity of its argument too */
p:not(.special) { color: grey; }
/* (0,1,1) */
/* :where() always contributes zero specificity — great for resets */
:where(h1, h2, h3) { margin: 0; }
/* (0,0,0) */
Use :where() when you want to apply base styles that
are easy to override. Because its specificity is always zero, any
class or element rule will beat it without needing
!important.
Practical Rules for Avoiding Conflicts
- Keep specificity low. Prefer classes over IDs for styling. Avoid chaining many selectors together.
- Never use IDs for styling. Save them for JavaScript and anchor links.
-
Avoid inline styles. They create specificity
that's nearly impossible to override without
!important. - Never use !important unless overriding third-party styles.
- Use DevTools. The Styles panel shows crossed-out rules that lost the specificity battle.
The site specificity.keegan.st lets you paste any selector and see its exact specificity score broken down column by column. Bookmark it.
Interview Questions
What is specificity in CSS?
A scoring system the browser uses to decide which CSS rule wins when multiple rules target the same element and property. It's scored as three columns (A, B, C) for IDs, classes, and elements respectively. Higher columns always beat lower ones regardless of number.
Does one ID always beat any number of classes?
Yes. One ID (A column) always beats any number of classes (B column). 100 classes (0,100,0) still loses to one ID (1,0,0) because column A is compared first.
What is the specificity of an inline style?
Inline styles have a (1,0,0,0) specificity — a fourth column that beats any selector in a stylesheet, no matter how many IDs it contains. Only !important can override it.
🏋️ Exercise
Calculate the specificity score for each selector, then rank them highest to lowest:
a) nav ul li a
b) .menu .item a
c) #nav .item
d) body #nav li.active a:hover
e) :is(.card, #box) p
See answers
a) nav ul li a → (0,0,4)
b) .menu .item a → (0,2,1)
c) #nav .item → (1,1,0)
d) body #nav li.active a:hover → (1,2,3)
e) :is(.card, #box) p → (1,0,1) —
#box is most specific argument
Ranking: d > c > e > b > a
Summary
- Specificity is scored as (A, B, C) — IDs, Classes/attributes/pseudo-classes, Elements/pseudo-elements.
- Column A always beats column B, which always beats column C — regardless of count.
- When specificity is equal, source order decides — the last rule wins.
- Inline styles have a fourth column (1,0,0,0) and beat all selector-based rules.
-
!importantbreaks out of the normal specificity system entirely — avoid it in your own code. -
:where()always has zero specificity — great for base styles you want to be easy to override. - Best practice: style with classes, keep selectors short, never use IDs for styling.