Web Development · Foundations

CSS Specificity

Want it in plain words first? Jump to Eli explains — the same idea, no jargon.
On this page 9 sections
  1. In 30 seconds
  2. Why this matters
  3. The college version
  4. Eli explains
  5. Worked example
  6. Key takeaway
  7. Quick check
  8. Study tools
  9. Sources & references

In 30 seconds

is the selector weight CSS uses when competing declarations are in the same cascade origin and importance. Write a selector's weight as three columns: IDs, classes/attributes/pseudo-classes, and type selectors/pseudo-elements. Compare the leftmost column first. Thus #notice (1-0-0) outweighs .notice (0-1-0), which outweighs p (0-0-1). Specificity does not replace the cascade; it is one later tie-breaker.

Why this matters

Specificity explains many apparent CSS surprises: a rule can match an element and still lose to a more specific competing rule. Being able to calculate it makes browser developer tools more useful and helps you repair a rule without accumulating overly forceful selectors. In coursework, it supports careful reasoning about selectors and the cascade. In a shared stylesheet, keeping selectors intentionally low-specificity can make components easier to customize, while :where() can expose a useful hook without adding selector weight.

The college version

A selector weight, not a replacement for the cascade

Specificity is a weight associated with a selector. It matters only after CSS has reached the stage of comparing declarations that otherwise compete for the same property on the same element. That boundary is important. A selector first has to match. Then the cascade considers such factors as origin and importance; only when the relevant declarations are being compared at the same origin and importance does specificity provide a selector-based comparison. If specificity is tied, source order can settle the conflict. This lesson deliberately does not try to teach the whole cascade. Its narrower task is to make selector weights predictable.

For ordinary selectors, represent specificity as three columns: ID selectors; class selectors, attribute selectors, and pseudo-classes; then type selectors and pseudo-elements. A selector with one ID has 1-0-0. A selector with two classes and one attribute has 0-3-0. A selector with an element name has 0-0-1. The values are not a decimal score: compare the first column first, then the second only if the first ties, then the third. For example, #panel is 1-0-0 and main .card .title is 0-2-1. The first selector wins this comparison because one ID outweighs any number of class and type selectors. This is why turning the columns into an informal number such as 100, 10, and 1 is a mnemonic at best, not the rule itself.

Calculate what appears in the selector

Count the simple selectors that occur in the selector being compared. button.primary has one type selector (button) and one (.primary), so it is 0-1-1. form [type="email"]:focus has one attribute selector and one , so it is 0-2-1: form supplies the type count, while [type="email"] and :focus each supply the middle count. The universal selector *, combinators such as the space in article p, and the nesting combinator do not add weight. A such as ::before belongs in the type/pseudo-element column.

A has a separate specificity for each selector. In h1, .heading, the h1 branch is 0-0-1 and the .heading branch is 0-1-0; CSS does not add the two together. The matching branch is the relevant one. This prevents a common mistake: adding every token seen in a rule without first identifying the selector that matched the element. It also keeps specificity separate from the number of elements a selector happens to match. A broad selector can have low specificity, and a selector that matches one element can have high specificity. Specificity describes selector structure, not visual importance or author intent.

Functional pseudo-classes and the :where() boundary

Some functional pseudo-classes require a special calculation. The pseudo-classes :is(), :not(), and :has() do not add their own middle-column count. Instead, their specificity is replaced by the specificity of the most specific complex selector in their argument list. So :is(p, #summary) has 1-0-0 because #summary is the most specific argument, even when the element that matched was a paragraph. In article:not(.archived), article contributes 0-0-1 and .archived contributes 0-1-0, producing 0-1-1. Treating every functional pseudo-class as merely one pseudo-class would give a wrong result for these cases.

:where() is intentionally different. Its specificity is always zero, including the selectors inside its arguments. :where(nav a) therefore has 0-0-0. An author might use it to state a broadly useful default while making that default easy for a later ordinary selector to override. For example, :where(.card) a { color: teal; } can be overridden by .card a { color: rebeccapurple; }: the first selector is 0-0-1, because only :where(.card) is zeroed while the a outside it still counts, and the second is 0-1-1. This is a design technique, not a promise that the second rule wins in every possible stylesheet; cascade origin, importance, and later rules still matter. Avoid escalating to IDs or !important just to win one local disagreement. First inspect which rule matched, calculate the competing selectors, and choose a maintainable rule change.

Eli, the EliExplains learning guide

Eli explains

The same idea, in plain words

Explain it like I’m 10

Imagine several labels competing to tell a librarian where to put one book. An ID label is an exact shelf code, a class label is a category sticker, and a type label is a broad kind such as “book.” CSS counts those kinds of labels in three columns. It checks exact shelf codes first, then category stickers, then broad kinds. That is why one ID beats many classes.

The special label :where() is like a removable pencil note: it can help describe the books to include, but it deliberately carries no authority in the comparison. A normal selector can replace its default easily.

Specificity does not decide every CSS disagreement. It only helps after CSS has reached a point where two relevant rules compete.

Picture it like this

Specificity is a three-column priority label for competing CSS selectors.

Where the picture stops working

Real librarians use judgment and policies, while CSS applies formal cascade rules. The analogy also omits origin, importance, and source order, which can matter before or after specificity.

Worked example

Suppose both rules match the same link: :where(.card) a { color: teal; } and .card a { color: rebeccapurple; }. The first selector is 0-0-1: :where(.card) contributes 0-0-0, but the a type selector outside the parentheses still adds one to the type column. The second selector contains one class and one type selector, so it is 0-1-1. At the same origin and importance, the second declaration wins the specificity comparison and the link is rebeccapurple. Now compare #sale .card a (1-1-1) with .page .card a (0-2-1): the ID in the first column makes the first selector more specific; do not add the right-hand columns into a larger total.

Key takeaway

Calculate specificity as ordered ID, class/attribute/pseudo-class, and type/pseudo-element columns. Compare from left to right, and remember that :where() always contributes zero.

Quick check

3 questions here, of 5 in this lesson’s practice set. Answers stay hidden until you check.

Question 1 of 3foundational

Which three categories make up ordinary CSS specificity?

Choose an answer, then check it.
Question 2 of 3intermediate

What is the specificity of button.primary?

Choose an answer, then check it.
Question 3 of 3intermediate

At the same origin and importance, which selector composition is more specific?

Choose an answer, then check it.
Practice all 5

Keep learning

Ready to build on this? Continue to the next lesson.

Practice this lesson
Study tools & related lessonsYou’ll learn to · Common mistakes · Easily confused · Key vocabulary · Related

You’ll learn to

  • Define CSS specificity and state its limited role in conflict resolution.
  • Calculate the three-column specificity of common selectors.
  • Compare two selector weights from left to right.
  • Explain how :is(), :not(), and :has() obtain specificity from their arguments.
  • Apply the zero-specificity rule for :where() to a small styling decision.

Common mistakes

  • Adding specificity columns as if 0-10-0 could beat 1-0-0.

    Compare columns from left to right; any ID count beats a selector with zero IDs.

  • Counting a comma-separated selector list as one combined selector.

    Calculate each branch separately and use the branch that matched.

  • Giving :where() the weight of its argument.

    :where() is always 0-0-0, while :is(), :not(), and :has() use their most specific argument.

  • Using specificity to explain every conflict.

    Check matching and the cascade conditions first; specificity is not the complete cascade.

Easily confused

`:where(.card) a` vs. `.card a`

The first is 0-0-1; the second is 0-1-1 because :where() deliberately adds no specificity.

`:is(p, #summary)` vs. `:where(p, #summary)`

The former takes its most specific argument, 1-0-0; the latter remains 0-0-0.

specificity vs. the cascade

Specificity is one selector-weight comparison within conflict resolution; the cascade has additional ordering rules.

Key vocabulary

specificity
The selector weight used when CSS compares otherwise competing declarations at the relevant cascade stage.
ID selector
A selector beginning with # that contributes to the first specificity column.
class selector
A selector beginning with . that contributes to the middle specificity column.
pseudo-class
A selector beginning with one colon that matches an element in a state or relationship, such as :focus.
pseudo-element
A selector beginning with two colons that addresses a conceptual part of an element, such as ::before.
selector list
Comma-separated alternative selectors, each of which has its own specificity.
`:where()`
A functional pseudo-class whose specificity is always zero.

Sources & references

  1. CSS cascade: Specificity — Mozilla / MDN Web Docs
  2. Selectors Level 4 — W3C

EliExplains lessons are original prose written from the open, credible references above. See Copyright & Licensing.

Researched 2026-08-20

Educational content only. It is not medical, legal or professional advice. Found an error? Tell us.