Selector matching with specificity calculation
The selector matching algorithm determines whether a selector matches a given element and calculates the selector’s specificity for cascade resolution.Matching algorithm
Selectors are matched from right to left for performance optimization:Start with the rightmost selector
Begin matching with the rightmost simple selector (the “key selector”). This is the selector that directly matches the target element.For example, in
div.container > p.highlight, start by checking if the element is a p with class highlight.Check the key selector
If the key selector doesn’t match the element, immediately reject the entire selector. This early rejection is crucial for performance.Why right-to-left? Most selectors won’t match most elements. Starting from the right allows quick rejection without traversing the DOM tree.
Process combinators and remaining selectors
If the key selector matches, work leftward through combinators:
- Descendant combinator (
): Check ancestors - Child combinator (
>): Check parent - Adjacent sibling combinator (
+): Check previous sibling - General sibling combinator (
~): Check all previous siblings
Right-to-left matching is counterintuitive but significantly faster than left-to-right. The key selector acts as a filter, eliminating most elements before expensive DOM traversal occurs.
Specificity calculation
When multiple rules match an element, specificity determines which declarations take precedence. Specificity is calculated as a three-part value: (a, b, c).Specificity is often written as a three-digit number (e.g., 1-2-3) but it’s actually three separate counters. A selector with specificity (0, 1, 0) always beats (0, 0, 100).
Specificity components
Specificity components
Specificity consists of three counters:
- a: Number of ID selectors
- b: Number of class selectors, attribute selectors, and pseudo-classes
- c: Number of type selectors and pseudo-elements
!important overrides everything.Calculation rules
Calculation rules
Count each type of selector:
| Selector | a | b | c | Specificity |
|---|---|---|---|---|
* | 0 | 0 | 0 | (0, 0, 0) |
p | 0 | 0 | 1 | (0, 0, 1) |
.class | 0 | 1 | 0 | (0, 1, 0) |
#id | 1 | 0 | 0 | (1, 0, 0) |
p.class | 0 | 1 | 1 | (0, 1, 1) |
div p | 0 | 0 | 2 | (0, 0, 2) |
#id .class p | 1 | 1 | 1 | (1, 1, 1) |
[type="text"] | 0 | 1 | 0 | (0, 1, 0) |
:hover | 0 | 1 | 0 | (0, 1, 0) |
::before | 0 | 0 | 1 | (0, 0, 1) |
Comparison algorithm
Comparison algorithm
To compare two specificities:
- Compare the a values. Higher wins.
- If a values are equal, compare b values. Higher wins.
- If b values are equal, compare c values. Higher wins.
- If all values are equal, the later rule (in source order) wins.
- (0, 1, 0) beats (0, 0, 100) — one class beats 100 type selectors
- (1, 0, 0) beats (0, 100, 100) — one ID beats 100 classes and 100 types
- (0, 2, 1) beats (0, 1, 5) — two classes beat one class, regardless of types
Cascade resolution
The cascade is the algorithm that combines declarations from different sources and resolves conflicts when multiple rules set the same property on an element.Cascade algorithm steps
Filter declarations
Collect all declarations that apply to the element based on selector matching. Include declarations from:
- User agent stylesheets (browser defaults)
- User stylesheets
- Author stylesheets (your CSS)
- Inline styles
Sort by origin and importance
Declarations are sorted by origin in this order (highest priority first):
- Transition declarations
- Important user agent declarations (
!importantin browser defaults) - Important user declarations
- Important author declarations
- Animation declarations
- Normal author declarations
- Normal user declarations
- Normal user agent declarations
Sort by specificity
Within the same origin and importance level, declarations are sorted by selector specificity. Higher specificity wins.
Sort by source order
If origin, importance, and specificity are all equal, the declaration that appears later in the source code wins.
The cascade is why CSS is called “Cascading” Style Sheets. Multiple stylesheets cascade together, with conflicts resolved according to the cascade algorithm.
Important cascade behaviors
!important reverses the cascade
!important reverses the cascade
Normal declarations follow the order: user agent → user → author.Important declarations reverse this: author
!important → user !important → user agent !important.This means a user’s !important declaration beats an author’s !important declaration, allowing users to override site styles for accessibility.Animations and transitions have special priority
Animations and transitions have special priority
CSS transitions have the highest priority (they temporarily override even
!important).CSS animations sit between !important declarations and normal declarations.This allows smooth transitions even when styles change dynamically.Inline styles have high specificity
Inline styles have high specificity
Inline styles (e.g.,
<div style="color: red">) are treated as having specificity (1, 0, 0, 0), higher than any selector-based rule.Only !important declarations can override inline styles.Inheritance
Some CSS properties are inherited by default, meaning child elements automatically receive values from their parent unless explicitly overridden.Inherited vs. non-inherited properties
- Inherited properties
- Non-inherited properties
- Controlling inheritance
These properties automatically cascade to descendants:Typography:
colorfont-familyfont-sizefont-weightfont-styleline-heightletter-spacingtext-aligntext-indenttext-transformwhite-space
list-stylelist-style-typelist-style-position
visibilitycursorquotes
Inheritance implementation
You now have a complete understanding of CSS selector matching, specificity calculation, cascade resolution, and style inheritance. These mechanisms work together to determine the final computed styles for each element in the document.