How Does CSS Specificity Work?
CSS specificity is the browser’s algorithm for resolving styling conflicts when multiple CSS rules target the same HTML element. It calculates a weight for each selector based on its components, ensuring that the most specific rule takes precedence over more general ones regardless of where they appear in the stylesheet. Understanding this scoring model prevents unintended style overrides and keeps stylesheets clean, maintainable, and predictable.
The Specificity Hierarchy
When conflicting styles target an element, the browser assigns a
numeric weight across four distinct tiers. Think of this calculation as
a four-part score represented as
(Inline, IDs, Classes, Elements):
- Inline Styles (
1-0-0-0): Styles attached directly to an HTML element via thestyleattribute carry the highest default specificity. For example,<div style="color: red;">overrides styles defined in external or internal stylesheets unless explicitly overridden by!important. - ID Selectors (
0-1-0-0): Selectors referencing unique identifiers (e.g.,#header,#main-nav) carry significant weight. A single ID selector overrides an unlimited number of class or element selectors. - Class, Attribute, and Pseudo-Class Selectors
(
0-0-1-0): This tier includes standard classes (e.g.,.button), attribute selectors (e.g.,[type="text"]), and pseudo-classes (e.g.,:hover,:focus). - Element and Pseudo-Element Selectors
(
0-0-0-1): Basic HTML tags (e.g.,div,p,h1) and pseudo-elements (e.g.,::before,::after) represent the lowest level of specificity.
Calculating Specificity Scores
Specificity is compared from left to right, matching values at the
highest tier before evaluating lower tiers. Higher tiers always trump
lower tiers, meaning a score of 0-1-0-0 (one ID) beats
0-0-12-0 (twelve combined classes).
| Selector | Calculation | Specificity Score |
|---|---|---|
h1 |
1 element | 0-0-0-1 |
h1.title |
1 element + 1 class | 0-0-1-1 |
.card .title |
2 classes | 0-0-2-0 |
#sidebar .widget p |
1 ID + 1 class + 1 element | 0-1-1-1 |
<h1 style="..."> |
Inline style | 1-0-0-0 |
If two selectors evaluate to the exact same specificity score, the Source Order rule applies: the declaration that appears latest in the CSS source code wins.
Exceptions and Special Modifiers
Certain selectors and declarations alter how specificity is calculated or applied:
- The
!importantRule: Adding!importantto a property declaration bypasses the standard specificity calculation, giving that property priority over all normal declarations, including inline styles. Conflicting!importantrules are resolved by comparing their individual selector specificities. - Universal Selector (
*) and Combinators: The universal selector (*), descendant combinator (space), child combinator (>), and sibling combinators (+,~) have no specificity value (0-0-0-0). - Pseudo-Class Modifiers (
:is(),:not(),:has(),:where()): The:is(),:not(), and:has()pseudo-classes take on the specificity score of their most specific argument. Conversely,:where()always carries a specificity score of0-0-0-0, making it ideal for creating reset styles or easily overridable defaults.
Best Practices for Managing Specificity
Maintaining a low and flat specificity baseline prevents specificity wars and fragile CSS architectures:
- Favor single class selectors over deeply nested chains (e.g., use
.nav-iteminstead ofheader nav ul li a). - Reserve IDs for DOM selection in JavaScript and document anchoring rather than style declarations.
- Avoid relying on
!importantfor routine layout fixes, as it disrupts natural cascade inheritance and complicates future maintenance. - Adopt modular naming methodologies like BEM (Block, Element, Modifier) or utility-first frameworks to keep selector weights uniform and predictable across an entire codebase.