How CSS Specificity Resolves Style Conflicts
CSS specificity is a weight-based scoring system that browsers use to
determine which style declarations apply to an element when multiple
conflicting rules are present. By assigning different numerical weights
to various types of selectors, the browser calculates a specificity
value for each rule; the declaration associated with the highest score
takes precedence and gets rendered. This article explains how
specificity values are calculated, how ties are broken by source order,
and how special modifiers like !important alter the normal
cascade.
The Specificity Hierarchy
When conflicting declarations target the same CSS property on an element, the browser evaluates selectors across four distinct tiers of priority:
- Inline Styles: Styles applied directly to an HTML
element via the
styleattribute have the highest standard specificity. - ID Selectors: Selectors that target elements by ID
(e.g.,
#header) carry high weight and easily override classes and elements. - Class, Attribute, and Pseudo-Class Selectors: This
category includes class selectors (e.g.,
.button), attribute selectors (e.g.,[type="submit"]), and pseudo-classes (e.g.,:hover,:focus). - Element and Pseudo-Element Selectors: The lowest
priority tier belongs to raw HTML tag selectors (e.g.,
div,p,h1) and pseudo-elements (e.g.,::before,::after).
Universal selectors (*), combinators (+,
>, ~, space), and the :where()
pseudo-class contribute nothing (0-0-0) to specificity.
Calculating Specificity Values
Specificity is typically calculated as a four-part tuple:
(Inline, IDs, Classes, Elements).
Instead of adding these values up as standard base-10 numbers, browsers compare them from left to right, matching values at the highest tier first:
style="color: red;"has a value of (1, 0, 0, 0).#navhas a value of (0, 1, 0, 0)..menu .itemhas a value of (0, 0, 2, 0).nav ul li ahas a value of (0, 0, 0, 4).
Because comparison proceeds from left to right, a single ID selector
(0, 1, 0, 0) will always override any combination of
classes and element tags, regardless of how many classes are chained
together (e.g., (0, 0, 15, 0) still loses to
(0, 1, 0, 0)).
Resolving Ties: The Cascade and Source Order
If two conflicting CSS rules have the exact same specificity calculation, the browser resolves the conflict using the rule of source order: the declaration defined last in the stylesheet wins.
/* Specificity: (0, 0, 1, 0) */
.alert {
color: blue;
}
/* Specificity: (0, 0, 1, 0) */
.alert {
color: red; /* This declaration wins because it appears later */
}This principle also applies across multiple linked stylesheets; the
stylesheet loaded later in the document's <head> will
override identical-specificity rules from earlier stylesheets.
The !important Rule
Appending !important to a CSS declaration removes it
from standard specificity calculations entirely. An
!important rule overrides all normal declarations,
including inline styles. When two conflicting rules both feature
!important, the browser falls back to standard specificity
and source order to resolve the tie between those two specific rules.
Due to its disruptive effect on the stylesheet cascade,
!important is typically reserved for critical utility
overrides.
Modern Specificity Controls
Modern CSS offers dedicated pseudo-classes to manage specificity proactively:
:is()and:not(): The specificity of these pseudo-classes adopts the specificity of the most specific selector in its argument list.:where(): Always maintains a specificity of(0, 0, 0), regardless of the selectors passed inside it, making it ideal for creating easily overridable default styles.@layer: CSS Cascade Layers allow developers to establish explicit priority tiers regardless of selector specificity. Rules in a higher layer always override rules in a lower layer, with standard specificity only evaluated within each individual layer.