Skip to main content
Syllabus
On this page

CSS Specificity Explained

Badar KhalilUpdated September 29, 2026 4 min read

What Is CSS Specificity?

CSS specificity is the algorithm browsers use to decide which rule wins when several rules target the same element and set the same property. If you ever wondered why your style is ignored, the answer is almost always specificity. Our focus keyword is CSS specificity, and along the way we cover trending questions: how to calculate CSS specificity, CSS specificity hierarchy, CSS specificity vs order, why is my CSS not working, CSS specificity calculator, and how @layer and :where() reduce specificity problems.

The Specificity Scoring Model

Think of specificity as a three-part score written (A, B, C). Compare the parts from left to right, and the first difference decides the winner. Never treat it as a decimal number: 11 classes never beat one ID.

ColumnCountsExamples
AID selectors#header
BClass selectors, attribute selectors, pseudo-classes.card, [type='text'], :hover, :nth-child()
CType selectors and pseudo-elementsdiv, p, ::before

The universal selector * and combinators (+, >, ~, and the space) add nothing. Inline styles (the style attribute) beat every selector-based rule, and !important sits in a different level of the cascade (covered in the next lesson).

Worked Examples

Code
p                    /* (0,0,1) */
p.intro             /* (0,1,1) */
#main p.intro       /* (1,1,1) */
ul li a:hover       /* (0,1,3) */
.nav .item.active   /* (0,3,0) */
#a #b               /* (2,0,0) */
* {}                /* (0,0,0) */

Comparison: .a.b.c.d.e is (0,5,0), and #x is (1,0,0). The ID wins because column A is compared first.

What Happens When Specificity Is Equal?

If two rules have identical specificity, the rule that appears later in the source order wins. This is why the order of your stylesheets matters, and why link order in HTML can change results.

Functional Pseudo-classes and Specificity

  • :is(), :not() and :has() take the specificity of their most specific argument. So :is(#a, .b) is (1,0,0).
  • :where() always contributes zero.
  • :nth-child(2n+1 of .item) counts as one pseudo-class plus the most specific selector in the of list.
  • With native CSS nesting, the & behaves like :is() of the parent selector list, so it inherits the parent's most specific selector.

Common Specificity Traps

  • Overusing IDs for styling. They are hard to override later. Prefer classes.
  • Deep descendant chains like .sidebar .widget ul li a. They are fragile and hard to override.
  • Inline styles from JavaScript libraries, which can only be beaten by !important.
  • Third-party CSS that uses high-specificity selectors.
  • Escalation by !important, where each fix requires another !important.

Strategies to Keep Specificity Low

  • Use a single class for styling (BEM-style naming such as .card__title).
  • Avoid qualifying classes with element names, for example use .button not a.button.
  • Use :where() for base styles so components can override them easily.
  • Use cascade layers (@layer) to control priority regardless of specificity between layers.
  • Order your CSS from generic to specific: reset, base, layout, components, utilities.
  • Increase specificity intentionally and minimally when needed, for example doubling a class: .btn.btn (a known hack, but layers are cleaner).

Debugging Specificity in DevTools

  1. Right-click the element and choose Inspect.
  2. In the Styles panel, look for rules that are struck through. These lost the cascade.
  3. Hover over a selector to see its specificity in Chrome or Firefox tooltips.
  4. Check the Computed tab and click the arrow next to a property to see which rule provided the final value.

Specificity Cheat Sheet

Code
Inline style          > ID  >  class / attribute / pseudo-class  >  element / pseudo-element
Equal specificity     -> last rule in source order wins
:where()              -> always 0
:is() :not() :has()   -> most specific argument

Best Practices

  • Write flat, low-specificity selectors and rely on source order and layers.
  • Never fix a specificity problem by adding an ID or !important first.
  • Document any intentional specificity boosts with a comment.
  • Use a linter (Stylelint) with rules like selector-max-id and selector-max-specificity.

Summary

Specificity is just a scoring system: IDs, then classes, then elements, then source order. Keep it flat, understand how :is() and :where() behave, and use layers for scalable projects. Play with the examples below to see the winner change live.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Exercise: Calculate the SpecificityJAVASCRIPT

For each selector in the comment block, write its specificity as (A,B,C) in the console output by completing the object. Then explain which selector would win for a link with class 'nav-link' inside an element with id 'menu'.

Try it Yourself JAVASCRIPT
Output

Press Run to execute.

Show expected output
{ s1: '(0,0,1)', s2: '(0,1,0)', s3: '(1,0,1)', s4: '(1,2,0)' }

This is a self-check — compare your result with the expected output above.

Exercise: Fix the Losing Rule Without !importantHTML

The button should be green, but a more specific rule keeps it blue. Change ONLY the CSS (no !important, no inline styles, no IDs) so the button becomes green.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
The Save button has a green background.

This is a self-check — compare your result with the expected output above.

Quiz: Specificity Quiz Pass 70%
Loading quiz…

Was this page helpful?