How CSS Specificity Works and Is Calculated: The Complete (a,b,c,d) Guide

CSS specificity is calculated using a four-component tuple (a, b, c, d) where inline styles (a) outrank ID selectors (b), which outrank class/attribute/pseudo-class selectors (c), which outrank type/pseudo-element selectors (d), with browsers comparing these values left-to-right to resolve conflicts and !important declarations overriding normal calculations.

According to the h5bp/Front-end-Developer-Interview-Questions repository, understanding selector specificity is essential for front-end developers. The interview questions documented in src/questions/css-questions.md examine exactly how browsers resolve conflicting style declarations when multiple selectors target the same element.

The Specificity Calculation Model

The browser assigns every CSS selector a weight represented as a four-segment tuple (a, b, c, d). Each segment counts a specific category of selector component, with higher-precedence categories completely outweighing lower ones regardless of quantity.

Segment A: Inline Styles

The a component counts inline styles declared directly on an element via the style attribute. Each instance contributes 1 to the a value, producing a tuple of (1,0,0,0).

  • Example: <p style="color: red;"> sets a = 1.

Segment B: ID Selectors

The b component counts ID selectors such as #header or #main. Each ID contributes 1 to the b value, producing a base tuple of (0,1,0,0).

  • Example: #intro { color: red; } sets b = 1.

Segment C: Class, Attribute, and Pseudo-Class Selectors

The c component counts class selectors (.button), attribute selectors ([type="text"]), and pseudo-classes (:hover, :focus, :nth-child()). Each contributes 1 to the c value.

  • Example: .active:hover results in (0,0,2,0) (two c-segment selectors).

Segment D: Type and Pseudo-Element Selectors

The d component counts HTML element selectors (div, p, h1) and pseudo-elements (::before, ::after). Each contributes 1 to the d value.

  • Example: h1::before results in (0,0,0,2) (one type + one pseudo-element).

Selectors That Do Not Add Specificity Weight

As documented in src/questions/css-questions.md, several selector types contribute zero to the specificity calculation:

  • Universal selector (*)
  • Combinators (>, +, ~, and the whitespace descendant combinator)
  • Negation pseudo-class (:not()) — though selectors passed as arguments inside :not() do contribute their respective weights

How Specificity Comparison Works

When multiple rules match the same element, the browser compares their (a, b, c, d) tuples from left to right:

  1. The higher a value wins immediately.
  2. If a is tied, the higher b value wins.
  3. If b is tied, the higher c value wins.
  4. If c is tied, the higher d value wins.

If all tuple values are identical, the declaration that appears later in the stylesheet wins due to source order precedence.

The !important Exception

An !important declaration overrides normal specificity calculations entirely. However, if two conflicting declarations both use !important, the browser falls back to the standard (a, b, c, d) comparison to determine which important declaration applies.

/* Specificity: (0,0,0,1) */
a { color: purple; }

/* Specificity: (0,0,1,1) + !important */
a.special { color: teal !important; }

In this example from the source documentation, a.special wins due to !important, even if another rule had higher specificity but lacked the flag.

Practical Specificity Examples

Consider the following markup and styles based on examples in src/questions/css-questions.md:

<style>
  /* Specificity: (0,0,0,1) */
  p { color: blue; }

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

  /* Specificity: (0,1,0,0) */
  #intro { color: red; }
</style>

<p id="intro" class="highlight" style="color: orange;">Demo</p>

The text renders orange because the inline style has specificity (1,0,0,0), which outranks the ID selector (0,1,0,0), regardless of the ID's high precedence relative to classes and elements.

Summary

  • CSS specificity uses a four-component tuple (a, b, c, d) to hierarchically weight inline styles, IDs, classes/attributes/pseudo-classes, and type/pseudo-elements respectively.
  • Universal selectors, combinators, and the :not() pseudo-class itself contribute zero specificity.
  • Browsers compare tuples left-to-right; the first non-tied higher value wins.
  • When specificity values are equal, the last declared rule wins.
  • !important declarations trump normal specificity, but specificity is recalculated normally when both competing declarations use the flag.

Frequently Asked Questions

What is the (a,b,c,d) specificity tuple?

The (a,b,c,d) tuple is a four-digit representation where a counts inline styles, b counts ID selectors, c counts class/attribute/pseudo-class selectors, and d counts type/pseudo-element selectors. The browser compares these values from left to right, with each position outweighing any combination of subsequent positions.

Does the universal selector (*) affect specificity?

No. The universal selector (*) contributes zero to all four specificity components. Similarly, combinators like >, +, and ~ add no weight; only the selectors they connect contribute to the calculation, as specified in the h5bp interview questions.

How does !important interact with specificity?

!important declarations exist outside normal specificity calculations and automatically override non-important declarations regardless of selector weight. However, if two declarations both include !important, the browser reverts to calculating specificity using the standard (a,b,c,d) rules to determine which important declaration applies.

Can multiple class selectors beat a single ID selector?

No. Due to the left-to-right tuple comparison, even numerous class selectors (yielding a high c value) cannot overcome a single ID selector (which sets the b value). For example, (0,1,0,0) (one ID) wins over (0,0,255,0) (255 classes) because the b value is compared before the c value.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →