Overview
Specificity decides which rule wins within a cascade layer. Scores below are (IDs, classes/attributes/pseudo-classes, types/pseudo-elements); compare left to right. Layers and !important sit above specificity, see css-cascade-layers.
Basic and attribute selectors
Prefer classes over IDs; the specificity column shows why.
| Selector | Example | Specificity |
|---|---|---|
| Universal | * | (0,0,0) |
| Type | p | (0,0,1) |
| Class | .btn | (0,1,0) |
| ID | #header | (1,0,0) |
| Attribute present | [disabled] | (0,1,0) |
| Attribute equals | [type="email"] | (0,1,0) |
| Starts with / ends with | [href^="https"], [src$=".svg"] | (0,1,0) |
| Contains | [class*="card"] | (0,1,0) |
| Case-insensitive value | [type="email" i] | (0,1,0) |
Combinators
Use the child combinator when the structure is fixed.
| Combinator | Example | Matches |
|---|---|---|
| Descendant | nav a | Any a inside nav. |
| Child | ul > li | Direct children only. |
| Adjacent sibling | h2 + p | The p right after an h2. |
| General sibling | h2 ~ p | Every later p sibling of an h2. |
Logical pseudo-classes
Use :where() for low-specificity defaults and :is() for grouped selectors.
| Selector | Example | Specificity |
|---|---|---|
:is() | :is(h1, h2) + p | Highest in the list. |
:where() | :where(h1, h2) + p | Always (0,0,0); use for resets and base styles. |
:not() | li:not(.skip) | Highest in the list. |
:has() | article:has(img) | Highest in the list; selects the parent. |
State and structural pseudo-classes (:hover, :nth-child()) score (0,1,0); see css-pseudo-classes.
Gotchas
Check these when a rule wins or loses unexpectedly.
| Gotcha | Fix |
|---|---|
IDs and !important are hard to override. | Style with classes and cascade layers. |
:not(.a.b) matches elements missing either class. | Use :not(.a):not(.b) for “neither”. |
:is(.a, #b) scores like the ID. | Put low-specificity alternatives in :where(). |
| Unlayered rules beat layered rules whatever the specificity. | Order @layer declarations deliberately. |
Attribute values are case-sensitive, except for a few HTML attributes such as type. | Add the i flag when unsure. |
:has() reached all major browsers only in December 2023 (Firefox 121). | Check your support target before relying on it. |