On this page

Specificity: how CSS decides which style wins

10 min read TextCh. 1 — CSS Basics

How does CSS decide which style to apply?

When multiple CSS rules target the same element, the browser needs a system to decide which one wins. That system is called the cascade and is based on three factors, evaluated in this order:

  1. Origin and importance (user styles, author styles, browser styles)
  2. Specificity of the selector
  3. Order of appearance in the code

What is specificity?

Specificity is a numeric value that the browser assigns to each CSS selector. It is represented as a tuple of three numbers: (ID, Class, Element).

Selector ID Class Element Value
p 0 0 1 (0,0,1)
.card 0 1 0 (0,1,0)
p.card 0 1 1 (0,1,1)
#hero 1 0 0 (1,0,0)
#hero .card p 1 1 1 (1,1,1)

Each column is compared from left to right. A single ID (1,0,0) outweighs a hundred classes (0,100,0) because the ID column is evaluated first.

What counts as a "class"?

The class column includes:

  • Class selectors (.card)
  • Attribute selectors ([type="text"])
  • Pseudo-classes (:hover, :focus, :nth-child())

What counts as an "element"?

The element column includes:

  • Type selectors (div, p, h1)
  • Pseudo-elements (::before, ::after, ::placeholder)

The universal selector and combinators

The universal selector (*), combinators (>, +, ~, ), and the :where() pseudo-class have zero specificity. They add nothing.

/* Both have specificity (0, 1, 0) */
.card { color: red; }
*.card { color: blue; } /* Same specificity */

The :is() and :not() pseudo-classes

Unlike :where(), the :is() and :not() pseudo-classes take the specificity of their most specific argument.

/* Specificity: (1, 0, 0) because of #hero inside :is() */
:is(#hero, .card, p) {
  color: red;
}

The cascade: order of appearance

When two selectors have the same specificity, the one that appears last in the source code wins.

.title { color: blue; }
.title { color: red; }  /* Wins: appears later */

Cascade layers with @layer

Modern CSS introduces @layer to control priority without depending on the physical order of the code. Layers declared later have higher priority.

@layer base, components, utilities;

@layer base {
  .button { padding: 0.5rem; }
}

@layer utilities {
  .button { padding: 1rem; } /* Wins because it is a later layer */
}

Inline styles and !important

Inline styles (style="...") override any selector in the stylesheet. Only !important can override them, but excessive use makes maintenance difficult.

The complete priority, from lowest to highest, is:

  1. Browser styles (user-agent)
  2. Author styles (your CSS)
  3. Author styles with !important
  4. Inline styles
  5. Inline styles with !important

To keep CSS predictable:

  • Use classes as the basis for your selectors
  • Avoid IDs for styling (reserve them for JavaScript or anchors)
  • Avoid !important except for very specific utilities
  • Use @layer to organize priority between base, components, and utilities
  • Keep selectors simple: one level of class is usually enough

Now that you understand how CSS resolves conflicts, in the next lesson we will explore the box model, the foundation of all layout in CSS.

Practice

  1. Calculate specificity: Write five different selectors that target the same element and rank them from lowest to highest specificity. Verify your calculation using the browser inspector.
  2. Organize with @layer: Create three layers (base, components, utilities) with @layer and define conflicting rules in each. Confirm that the layer declared last has higher priority.
  3. Eliminate an !important: Find a case where you need !important and refactor the selectors so the correct style wins through specificity or cascade order alone.
Remember
Specificity is calculated as (ID, class, element). A single ID selector outweighs any number of classes. Avoid using !important: it is a sign that the CSS architecture needs improvement.
Be careful with !important
Using !important breaks the natural flow of the cascade. Only use it as a last resort, for example to override third-party styles that you cannot modify.
/* Specificity: (0, 0, 1) */
p {
  color: black;
}

/* Specificity: (0, 1, 0) */
.message {
  color: blue;
}

/* Specificity: (0, 1, 1) */
p.message {
  color: green;
}

/* Specificity: (1, 0, 0) */
#alert {
  color: red;
}

/* Specificity: (0, 2, 0) - greater than (0, 1, 1) */
.container .message {
  color: purple;
}
/* Both selectors have the same
   specificity: (0, 1, 0) */

.button {
  background-color: blue;
  color: white;
}

/* This one wins by order: it comes later */
.button {
  background-color: green;
}

/* Use layers to control priority */
@layer base, components, utilities;

@layer base {
  .button { background: gray; }
}

@layer components {
  .button { background: blue; }
}

@layer utilities {
  .button { background: green; } /* Wins */
}