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):

  1. Inline Styles (1-0-0-0): Styles attached directly to an HTML element via the style attribute carry the highest default specificity. For example, <div style="color: red;"> overrides styles defined in external or internal stylesheets unless explicitly overridden by !important.
  2. 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.
  3. 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).
  4. 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 !important Rule: Adding !important to a property declaration bypasses the standard specificity calculation, giving that property priority over all normal declarations, including inline styles. Conflicting !important rules 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 of 0-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-item instead of header nav ul li a).
  • Reserve IDs for DOM selection in JavaScript and document anchoring rather than style declarations.
  • Avoid relying on !important for 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.