What Is CSS Specificity: How Browsers Decide Which Styles Win

You wrote a CSS rule, you saved the file, you refreshed the browser, and… nothing. The style you wanted is being completely ignored. Sound familiar? You are not alone, and the culprit almost always has a name: CSS specificity.

In this beginner-friendly guide, we will break down exactly how browsers decide which styles win, walk you through the specificity calculation step by step, and give you the tools to debug those frustrating “why isn’t my CSS applying” moments forever.

What Is CSS Specificity?

CSS specificity is the algorithm browsers use to decide which CSS rule to apply when multiple rules target the same element. Think of it as a scoring system: each selector earns points, and the rule with the highest score wins. If two rules tie, the one written last in your stylesheet takes precedence.

This is why your .button class style sometimes loses to a rule you wrote ten minutes earlier. The browser is not broken. It is just doing math.

css code laptop

The Specificity Calculation: A Four-Column Score

Specificity is calculated using four categories, often written as four numbers separated by commas (for example, 0,1,2,1). Here is what each column represents:

Column What It Counts Example
1st (highest) Inline styles style=”color: red;”
2nd IDs #header
3rd Classes, attributes, pseudo-classes .btn, [type=”text”], :hover
4th (lowest) Elements and pseudo-elements div, p, ::before

The universal selector (*) and combinators (+, >, ~) add zero points.

Concrete Examples: Calculating Specificity Step by Step

Let’s look at real selectors and score them.

Example 1: A Simple Element Selector

p { color: blue; }

One element selector. Specificity score: 0,0,0,1

Example 2: A Class Selector

.intro { color: green; }

One class. Specificity score: 0,0,1,0

Example 3: Combining Element and Class

p.intro { color: orange; }

One class plus one element. Specificity score: 0,0,1,1

Example 4: An ID Selector

#main-title { color: purple; }

One ID. Specificity score: 0,1,0,0

Example 5: A Complex Selector

nav#primary ul li.active a:hover { color: red; }

Let’s count:

  • IDs: #primary = 1
  • Classes/pseudo-classes: .active + :hover = 2
  • Elements: nav + ul + li + a = 4

Specificity score: 0,1,2,4

Example 6: Inline Style

<p style="color: black;">Hello</p>

Inline styles get the top column. Specificity score: 1,0,0,0

Why Your CSS Isn’t Applying: The Real Reason

Here is the scenario that trips up almost every beginner:

#sidebar p { color: blue; }
.highlight { color: yellow; }

You add class="highlight" to a paragraph inside the sidebar, expecting it to turn yellow. It stays blue. Why?

  • #sidebar p scores 0,1,0,1
  • .highlight scores 0,0,1,0

The ID selector wins because column 2 beats column 3, no matter how many classes you stack. This is the number one reason styles “don’t apply.”

How to Fix Specificity Conflicts (Without Using !important)

When your styles are losing the fight, you have several clean options:

  1. Increase specificity intentionally. Write #sidebar p.highlight to score 0,1,1,1 and beat the original rule.
  2. Avoid IDs in CSS. Use classes for styling and reserve IDs for JavaScript hooks or anchor links. This keeps specificity flat and predictable.
  3. Use the :where() function. Anything inside :where() has zero specificity, making it easy to write low-priority defaults that any class can override.
  4. Reorder your CSS. When two selectors have equal specificity, the last one declared wins. Sometimes moving a rule down the file is the simplest fix.
  5. Use !important as a last resort. It overrides everything except another !important declaration, but it makes your CSS hard to maintain.
css code laptop

Specificity Order, From Weakest to Strongest

  1. Universal selector and inherited values (lowest)
  2. Element and pseudo-element selectors
  3. Class, attribute, and pseudo-class selectors
  4. ID selectors
  5. Inline styles
  6. !important declarations (highest, use sparingly)

A Quick Mental Trick

When debugging, open your browser’s DevTools, inspect the element, and look at the Styles panel. Rules that are crossed out are losing the specificity battle. The winning rule is at the top, unstruck. This visual feedback is faster than counting selectors by hand.

Best Practices for 2026

  • Keep selectors as flat as possible. Aim for single-class selectors like .card-title instead of div.card > h2.title.
  • Adopt a naming methodology like BEM to avoid needing nested selectors.
  • Use modern CSS layers (@layer) to control specificity at an architectural level.
  • Reserve !important for utility classes only, never for component styles.
  • Lean on :where() for resets and base styles so users of your CSS can override easily.

Frequently Asked Questions

What is the specificity of CSS?

CSS specificity is the algorithm browsers use to determine which CSS rule applies when multiple rules target the same element. It assigns a weighted score to each selector based on its components.

How do you calculate specificity in CSS?

Count the inline styles, IDs, classes/attributes/pseudo-classes, and elements/pseudo-elements in your selector. Express the totals as a four-number score (for example, 0,1,2,1) and compare column by column from left to right.

Which is the correct order of CSS specificity?

From lowest to highest: element selectors, then classes and attributes and pseudo-classes, then IDs, then inline styles, and finally !important declarations override everything else.

Does !important have a specificity score?

No, !important sits outside the specificity calculation. It overrides any normal declaration regardless of selector strength. The only thing that beats an !important rule is another !important rule with higher specificity.

Do combinators like > or + add specificity?

No. Combinators such as >, +, ~, and the descendant space add zero specificity points. Only the selectors on either side of them count.

What is the specificity of the :not() selector?

The :not() pseudo-class itself adds nothing, but the selector inside it counts. So :not(.active) has the same specificity as .active, which is 0,0,1,0.

Wrapping Up

Once you understand how specificity works, the mystery of “why isn’t my CSS applying” disappears. Count your selectors, check the score, and adjust accordingly. Better yet, write flat, class-based CSS from the start so specificity battles never happen in the first place. Your future self will thank you.