Two CSS rules sometimes target the same element with conflicting instructions. Before even looking at the order of the code, the browser compares the weight of each selector: that is specificity, a precise calculation that decides which of the two rules wins.
Not understanding that calculation often leads to stacking ids or !important declarations just to force a style through, a habit that makes a stylesheet increasingly hard to maintain over time.
Definition
Specificity is a score assigned to every CSS selector based on the components it contains. The higher that score, the more weight a rule carries against a competing rule that targets the same element and the same property.
The calculation, in three categories
The score is calculated by counting three categories of components in the selector: the number of ids, the number of classes, attributes, and pseudo-classes, then the number of element types and pseudo-elements. These three numbers are then compared left to right, like digits in a three column system.
/* 0 id, 1 class, 0 element: specificity (0, 1, 0) */
.button {
color: blue;
}
/* 0 id, 2 classes, 0 element: specificity (0, 2, 0) */
.button.active {
color: green;
}
/* 1 id, 0 class, 0 element: specificity (1, 0, 0) */
#main-action {
color: red;
}
/* result: #main-action wins over the other two rules, no matter their order */In this example, #main-action wins consistently, even if the other two rules appear later in the file, because a single id carries more weight than any number of stacked classes.
More worked examples
| Selector | id | classes/attributes | elements |
|---|---|---|---|
p | 0 | 0 | 1 |
.card | 0 | 1 | 0 |
.card.featured | 0 | 2 | 0 |
nav ul li a | 0 | 0 | 4 |
#header .logo | 1 | 1 | 0 |
The Universal selector and combinators such as the space or the greater-than sign add nothing to the score: only components that actually target an element count in the calculation.
Why stacking classes beats stacking ids
Since the ID selector carries enormous weight in this calculation, a team that gets into the habit of styling through ids quickly gets stuck: the smallest exception requires another id, or worse, an !important. Sticking to the Class selector, even when stacking several on the same selector, keeps specificity in a low and predictable range, which keeps the whole stylesheet easy to override cleanly. Our HTML and CSS course emphasizes this discipline from the very first exercises, to build the habit of coding with low specificity before you even have to think about it.
Eyeballing specificity quickly
To estimate a selector's specificity at a glance, it is enough to count its components in order: each id adds a digit in the first position, each class, attribute, or pseudo-class in the second, each element type in the third. Once that quick reading becomes automatic, it removes the need to pull out a specificity calculator for every conflict and makes it immediately obvious which rule will win against another in the Cascade.
Frequently asked questions
:hover count in specificity?Yes, a pseudo-class counts exactly like a class in the calculation, while a pseudo-element like ::before counts in the same category as an element type.
:is() or :not() be calculated?Yes, but their calculation follows a particular rule: the specificity retained is that of the strongest selector passed as an argument, not a simple sum of every selector in the list.
No, never. The calculation compares categories left to right without ever adding them together: the id category always wins over the class category, no matter how many classes are stacked.