CSS Specificity Calculator
Note: The three columns never carry. A hundred classes at 0-100-0 still lose to one id at 1-0-0 β which is the whole argument for keeping ids out of stylesheets, so that source order stays the thing that decides.
When two rules have identical specificity, the later one wins. That is why a stylesheet's order matters, and why an override that works locally can break when a bundler changes the concatenation order.
The modern pseudo-classes changed the rules in ways worth knowing. :where() always contributes zero β everything inside it is free, which makes it the right way to write default styles that anything can override without a fight. :is() and :not() take the specificity of their most specific argument, so :is(#header, p) is worth a full id even when it matched the paragraph. :has() does the same, and since it usually contains a class or an id, it is rarely as cheap as it looks.
The practical advice that follows: keep everything in the middle column. A stylesheet built from single classes has flat, predictable specificity where source order decides, and source order is something you control. Ids in CSS and !important are both ways of winning an argument you should not be having β and once !important is in a codebase, the only thing that overrides it is another !important, which is how a stylesheet becomes unmaintainable.
Inline styles sit above all of this at 1-0-0-0, which is why component libraries that write inline styles are so hard to theme.
Frequently Asked Questions
Can enough classes beat an id?
No. The columns are compared independently and never carry, so a hundred classes at 0-100-0 still lose to a single id at 1-0-0. That is the reason to keep ids out of stylesheets entirely β once one is there, overriding it needs another.
What is the difference between :is() and :where()?
They match identically; only the specificity differs. :where() always contributes zero, so its contents are free to override. :is() takes the specificity of its most specific argument, which means one id inside it makes the whole selector cost an id.
When is !important acceptable?
Utility classes whose entire job is to win, and overriding third-party CSS you cannot edit. Everywhere else it removes your ability to override later, and the only answer to an !important is another one β which is how a stylesheet stops being maintainable.
Why does my rule lose when both selectors look the same?
Equal specificity is broken by source order, so the later rule wins. If the two live in different files, whatever order your bundler concatenates them in decides β which is why an override can work locally and fail after a build.
How specific is an inline style?
Higher than any selector β effectively a fourth column at 1-0-0-0. Only !important in a stylesheet beats it. This is why components that write inline styles are difficult to theme without resorting to !important yourself.

