Course topics

By WebNest Studio Engineering Team

CSS Tutorial

CSS Specificity

Specificity is the scoring system the browser uses to decide which selector "wins" when two rules of the same origin and importance target the same element with conflicting declarations. The higher-specificity selector's declaration is applied, regardless of which rule was written first.

Specificity is calculated, not guessed — it follows a concrete, comparable formula. Learning to calculate it by hand removes almost all of the mystery around "why isn't my CSS working," which is one of the most common frustrations for developers new to the language.

Why it matters

When a style refuses to apply, the cause is nearly always specificity: another selector outweighs yours. Being able to calculate which of two selectors wins turns hours of trial and error into a quick look. It is also one of the most reliably asked CSS interview questions.

The Specificity Hierarchy

Specificity is commonly represented as a tuple of four values, often written as (a, b, c, d), compared left to right like version numbers.

  • Inline styles (style="...") — highest specificity, effectively (1, 0, 0, 0). Beats any selector in an external or internal stylesheet.
  • ID selectors — each contributes (0, 1, 0, 0). One ID always outweighs any number of classes.
  • Class, attribute, and pseudo-class selectors — each contributes (0, 0, 1, 0). Examples: .card, [type="text"], :hover.
  • Type selectors and pseudo-elements — each contributes (0, 0, 0, 1). Examples: div, p, ::before.

Calculating and Comparing Specificity

To score a selector, count how many of each category it contains, then compare tuples left to right — a single ID beats any number of classes, and a single class beats any number of type selectors. The universal selector (*) and combinators (>, +, ~, space) add no specificity value at all. For example, #nav .link scores (0,1,1,0) while nav ul li a scores (0,0,0,4) — the first wins even though the second selector has more parts written out.

A Concrete Conflict Example

Given .button { color: blue; } and #submit-btn { color: red; } both targeting the same button, the ID rule wins and the button renders red — its specificity (0,1,0,0) beats the class rule's (0,0,1,0), even if the class rule appears later in the file. If a third rule button { color: green; } is added, it loses to both, since a bare type selector has the lowest specificity of the three.

Examples

Specificity determining the winner regardless of source order

CSS
<!-- <button id="submit-btn" class="button">Submit</button> -->

button {
  color: green;           /* specificity (0,0,0,1) */
}

.button {
  color: blue;             /* specificity (0,0,1,0) */
}

#submit-btn {
  color: red;               /* specificity (0,1,0,0) -- wins */
}
Output
The button text renders red because the ID selector has the highest specificity of the three competing rules, even though it targets the same element as the others.

Combined selectors adding up specificity points

CSS
nav ul li a.active {
  /* type(4) + class(1) = (0,0,1,4) */
  color: orange;
}

.nav-list .active {
  /* class(2) = (0,0,2,0) -- higher, wins over the rule above */
  color: purple;
}
Output
The active navigation link renders purple, since two class selectors (0,0,2,0) outrank one class plus four type selectors (0,0,1,4) — the class column is compared first and 2 beats 1.

Common Mistakes

  • Assuming a selector with "more parts" automatically wins, when specificity actually compares category counts (IDs, then classes, then types) rather than raw selector length.
  • Overusing ID selectors for styling, which creates specificity so high that later, more maintainable class-based rules become impossible to override without another ID or !important.
  • Forgetting that combinators (>, +, ~, and the descendant space) contribute zero specificity themselves.
  • Fighting a specificity conflict with !important instead of simplifying the competing selector, which compounds the problem for future changes.

Exercise

The second rule comes later, but the paragraph is still red, because the first selector is more specific. Without using !important and without changing or moving the first rule, change the selector of the second rule so that it wins and the paragraph turns green.

Expected result: the paragraph is green.

CSS · starter code
/* The page contains: <main> with an <h1>, a <p> and a <button> */

main p {
  color: red;
}

p {
  color: green;
}
Show hints
  • main p has two element selectors; p has one.
  • A selector with three element selectors, such as body main p, is more specific than one with two.
Show solution
CSS · solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */

main p {
  color: red;
}

body main p {
  color: green;
}

Quick quiz

1. Which selector has the highest specificity?

  • A. #intro
  • B. .note
  • C. div p
  • D. p
Show answer

A. One id outweighs any number of classes, and one class outweighs any number of element selectors.

2. What is the specificity of ul li.active a, written as ids, classes, elements?

  • A. 0, 3, 1
  • B. 0, 1, 3
  • C. 1, 1, 2
  • D. 0, 1, 1
Show answer

B. There is one class (.active) and three element selectors (ul, li, a).

3. Two selectors have exactly the same specificity. What decides the winner?

  • A. The longer selector
  • B. The one written first
  • C. The one that comes later in the source
  • D. Alphabetical order
Show answer

C. Source order is the tie-breaker.

Interview questions

How is specificity calculated?

Show answer

Count three groups. The number of id selectors; the number of class selectors, attribute selectors and pseudo-classes; and the number of element selectors and pseudo-elements. Compare the groups from left to right: the first difference decides, so one id beats any number of classes. The universal selector and combinators add nothing. An inline style attribute outranks all selectors, and !important overrides the normal comparison.

How do you keep specificity under control in a large project?

Show answer

Style with single classes and keep selectors flat, so that almost every rule has the same low specificity and source order decides. Avoid ids and long chains of nested selectors in stylesheets. A naming convention such as BEM, component-scoped styles, or cascade layers with @layer all support this. :where() can wrap a selector to give it zero specificity, which is useful for defaults that should be easy to override.

Key Points to Remember

  • Specificity is scored as a tuple: inline styles > IDs > classes/attributes/pseudo-classes > type selectors/pseudo-elements.
  • Higher specificity wins regardless of source order; equal specificity falls back to source order.
  • Combinators and the universal selector add no specificity value on their own.
  • Prefer low, consistent specificity (mostly classes) to keep a stylesheet easy to override and maintain.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering