Specificity
Specificity is the score the browser calculates to decide which CSS rule wins — a system with a clear, learnable algorithm.
- Calculate the specificity score for a given CSS selector
- Predict which rule wins when multiple rules with different selectors target the same element
- Avoid common specificity problems (ID overuse, !important dependency)
- Describe why writing lower-specificity selectors usually leads to more maintainable CSS
The previous lesson introduced the cascade and noted that more-specific selectors win over less-specific ones. But "more specific" is a precise, calculable quantity — not a feeling.
The three-column score
Every selector gets a score made up of three columns, written as
(A, B, C):
| Column | What counts |
|---|---|
| A — IDs | One point per #id selector |
| B — Classes, attributes, pseudo-classes | One point each for .class, [attr], :hover, :first-child, etc. |
| C — Type selectors and pseudo-elements | One point each for p, div, h1, ::before, etc. |
The browser compares column A first. If tied, it compares B. If still tied, C.
Columns do not "carry over": twenty class selectors (0, 20, 0) do not beat
a single ID (1, 0, 0).
Examples
| Selector | Score | Reasoning |
|---|---|---|
p | (0, 0, 1) | One type selector |
.card | (0, 1, 0) | One class |
p.card | (0, 1, 1) | One class + one type |
article p.card | (0, 1, 2) | One class + two types |
#nav | (1, 0, 0) | One ID |
#nav .item | (1, 1, 0) | One ID + one class |
#nav .item a | (1, 1, 1) | One ID + one class + one type |
A concrete contest
Three rules all target the same <h1>:
/* Rule A — type selector */
h1 {
color: navy;
}
/* Rule B — class selector */
.page-title {
color: steelblue;
}
/* Rule C — ID selector */
#hero-heading {
color: crimson;
}If your markup is <h1 id="hero-heading" class="page-title">, all three rules
match. The scores are:
- Rule A:
(0, 0, 1) - Rule B:
(0, 1, 0) - Rule C:
(1, 0, 0)
Rule C wins — the heading is crimson. If you removed the id attribute from
the element, Rule C would no longer match, and Rule B would win: steelblue.
The easiest way to build intuition is to read selectors right-to-left and count: IDs first, then classes and pseudo-classes, then types. A quick mental check before writing a new rule saves hours of debugging later.
Inline styles
Inline style="" attributes sit above the three-column system. They always
beat any rule in any stylesheet, regardless of specificity. Effectively they
occupy a fourth column — (1, 0, 0, 0) in a four-column model.
This is why the advice from the previous lesson matters: inline styles are
nearly impossible to override from a stylesheet without escalating to
!important.
The !important flag
Adding !important after a declaration lifts it out of the normal specificity
contest entirely:
.card p {
color: red !important;
}This declaration wins over any competing rule that does not also carry
!important, regardless of specificity. When two !important declarations
conflict, the normal specificity algorithm runs again — but only between the
!important declarations.
Why is !important a code smell? Because it does not solve the underlying
specificity problem — it just raises the stakes. The next person to maintain
the stylesheet has to add another !important to override yours, and the
escalation continues. There is almost always a cleaner solution: restructure
the selector, or reduce specificity elsewhere.
When is it acceptable? Utility classes that must always win (.sr-only for
screen-reader-only text, .hidden) are a legitimate use case. Framework or
third-party CSS you cannot modify is another. Treat it as a warning sign when
you find yourself reaching for it.
Keeping specificity low
The most maintainable CSS uses mostly class selectors, keeps nesting shallow, and avoids ID selectors in stylesheets. A common guideline:
- Use type selectors for global resets and element defaults.
- Use class selectors for all component and utility styles.
- Use ID selectors in HTML for JavaScript hooks — not in CSS.
- Avoid
!importantexcept in the situations described above.
This keeps every rule at roughly the same specificity tier, which means later rules in the file reliably override earlier ones — exactly what source order is supposed to provide.
Browser DevTools show the specificity score for each rule in the Styles panel when you hover over a selector. In Chrome and Firefox, the score appears as three numbers. Use this when a rule is not applying as expected.
Where to go next
Next: The Box Model — how the browser thinks about space inside and around every element, and why understanding this eliminates an entire category of layout confusion.