Code of the Day
BeginnerStyling with CSS

Specificity

Specificity is the score the browser calculates to decide which CSS rule wins — a system with a clear, learnable algorithm.

Web FoundationsBeginner7 min read
Recommended first
By the end of this lesson you will be able to:
  • 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):

ColumnWhat counts
A — IDsOne point per #id selector
B — Classes, attributes, pseudo-classesOne point each for .class, [attr], :hover, :first-child, etc.
C — Type selectors and pseudo-elementsOne 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

SelectorScoreReasoning
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:

  1. Use type selectors for global resets and element defaults.
  2. Use class selectors for all component and utility styles.
  3. Use ID selectors in HTML for JavaScript hooks — not in CSS.
  4. Avoid !important except 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.

Finished reading? Mark it complete to track your progress.

On this page