CSS Specificity Calculator
Every CSS rule you write carries a specificity weight - a score the browser uses to decide which style wins when two rules target the same element. That score is expressed as three numbers: (ID, Class, Element). A higher number in an earlier column always wins, regardless of how large the later columns are. So (1,0,0) beats (0,99,99) every time.
Paste any selector below to instantly see its breakdown. Each part of the selector - IDs, classes, attributes, pseudo-classes, element types - is colour-coded and annotated so you can see exactly what's contributing to the total. Use the compare panel to rank multiple selectors against each other and find out which one the browser will give priority to.
CSS Specificity Calculator
!important with higher specificity).
Token Breakdown
Compare Selectors
How specificity scoring works
The browser scores every selector using three independent counters, written as (A, B, C).
- A counts ID selectors (
#id). - B counts class selectors (
.class), attribute selectors ([type="text"]), and pseudo-classes (:hover,:focus, etc.). - C counts element type selectors (
div,a) and pseudo-elements (::before,::after).
Comparison always starts from the left. A selector with (1,0,0) beats anything with (0,x,x) regardless of x. If A is equal, move to B; if B is also equal, compare C. Only when all three match does source order in the stylesheet decide the winner.
| Selector type | Contributes to | Example |
|---|---|---|
| #id | A (+1,0,0) | #header |
| .class | B (+0,1,0) | .nav-link |
| [attr] | B (+0,1,0) | [type="submit"] |
| :pseudo-class | B (+0,1,0) | :hover, :nth-child(2) |
| element | C (+0,0,1) | div, a |
| ::pseudo-element | C (+0,0,1) | ::before, ::placeholder |
| * (universal) | Nothing (0,0,0) | * |
| Combinators | Nothing (0,0,0) | >, +, ~, space |
Common gotchas and edge cases
Several modern pseudo-classes have specificity behaviour that surprises even experienced developers:
:is(#id, .class) scores (1,0,0), not (0,1,0).:not(#id) contributes (1,0,0).!important rules conflict, specificity is used as a tiebreaker between them.!important.S adds its own specificity in newer browsers. This tool scores the pseudo-class itself at (0,1,0).How this tool parses selectors
The parser is a hand-written tokeniser that walks the selector character by character, recognising IDs, classes, attribute selectors, element types, pseudo-classes, and pseudo-elements. It tracks parenthesis depth to correctly handle nested function selectors like :is(), :not(), :has(), and :where().
For :is(), :not(), and :has(), the parser recursively evaluates each comma-separated argument inside the parentheses and adopts the highest scoring one - matching the behaviour specified in CSS Selectors Level 4. :where() is special-cased to always return (0,0,0). Combinators (>, +, ~) and the universal selector (*) are explicitly ignored. Comma-separated selector lists are evaluated as separate selectors and the highest-specificity branch is used.
Tips for writing better selectors
#id creates (1,0,0), making it nearly impossible to override without another ID or !important. Prefer classes for styling..card .title .text scores (0,3,0) and tightly couples your CSS to your HTML structure. .card-text at (0,1,0) is easier to maintain and override.:where() gives them zero specificity, so any author style - no matter how simple - can override them without friction.h1 a, h2 a, h3 a scoring (0,0,2), write :is(h1, h2, h3) a - same specificity, much less repetition.!important with higher specificity - a spiral that's hard to unwind. Fix the root specificity problem instead.@layer rule (modern browsers) lets you control which layer wins before specificity is even compared - a powerful alternative to fighting specificity directly.Frequently Asked Questions
What is CSS specificity?
CSS specificity is the scoring system browsers use to decide which CSS rule wins when two or more rules target the same element and set the same property.
Every selector carries a weight expressed as three numbers - (A, B, C) - representing the count of ID selectors, class/attribute/pseudo-class selectors, and element/pseudo-element selectors respectively.
When rules conflict, the browser compares these scores from left to right, and the rule with the higher score takes effect. If scores are equal, the rule that appears later in the stylesheet wins.
How is CSS specificity calculated?
Specificity is calculated by counting three types of selector component separately. Each ID selector (#id) adds 1 to the first column (A). Each class (.class), attribute selector ([attr]), or pseudo-class (:hover) adds 1 to the second column (B). Each element type (div, a) or pseudo-element (::before) adds 1 to the third column (C).
For example, the selector #nav .link:hover span scores (1,2,1) - one ID, one class, one pseudo-class, and one element. Comparison always starts at column A. A selector scoring (1,0,0) beats any selector scoring (0,x,x) regardless of how large x is. The columns are not added together into a single number; they are compared independently from left to right.
Does (0,1,0) always beat (0,0,10)?
Yes. The three columns are compared independently, not summed. A single class selector scoring (0,1,0) will always beat ten element selectors scoring (0,0,10), because the B column (1) is greater than the B column (0) - and once a column is decided, the remaining columns are irrelevant. This is a common misconception: there is no number of lower-column selectors that can "add up" to outrank a higher column.
What specificity does !important have?
!important sits entirely outside the normal specificity system. A declaration marked !important will override any normal declaration targeting the same property on the same element, regardless of specificity scores.
When two !important declarations conflict with each other, specificity is then used as a tiebreaker between them - the !important rule with the higher specificity wins.
Because of this, !important should be used as a last resort; once introduced, the only way to override it is with another !important, which can quickly become difficult to manage.
Do inline styles override CSS rules?
Yes. Inline styles - written directly in the HTML style attribute - are treated as a fourth implicit column, effectively scoring (1,0,0,0), which is higher than any selector-based specificity.
The only way to override an inline style from a stylesheet is to use !important.
This is one reason inline styles are generally discouraged for component styling: they make it very hard to override or theme elements from external stylesheets.
What is the specificity of :is(), :not(), and :has()?
All three pseudo-classes adopt the specificity of their most specific argument, not the pseudo-class itself.
So :is(#id, .class) scores (1,0,0) because #id is the most specific argument.
:not(.inactive) scores (0,1,0) because .inactive is a class.
:has(> img) scores (0,0,1) because img is an element selector.
This means you need to be careful about what you pass into these pseudo-classes - a single high-specificity argument can significantly raise the score of the whole selector.
What is the specificity of :where()?
:where() always contributes a specificity of (0,0,0), regardless of what selectors are inside it.
This makes it extremely useful for writing base or reset styles that you want to be easy to override - any author style, no matter how simple, will win over a rule scoped inside :where().
It is commonly used in CSS resets and design system foundations precisely because it adds no specificity weight while still allowing complex selector matching.
Why does my CSS not apply even though my selector looks more specific?
There are a few common causes. First, check for !important on the competing rule - it overrides specificity entirely. Second, check for inline styles on the element, which also beat selector-based specificity. Third, verify the specificity scores are actually what you expect - a single ID in the competing selector will beat any number of classes in yours. Fourth, if specificity scores are genuinely equal, the rule that appears later in the stylesheet (or in a later-loaded stylesheet) wins. Finally, check that your selector actually matches the element - a selector that does not match cannot apply at all.
Should I use ID selectors in CSS?
Generally no, and most CSS methodologies actively discourage it. An ID selector scores (1,0,0), which is so high that it is nearly impossible to override without another ID or !important. IDs are also unique per page, which means ID-based styles cannot be reused across components. Classes are almost always the better choice for styling - they are reusable, composable, and sit at a specificity level that is easy to manage. Reserve IDs for JavaScript hooks and accessibility references where their uniqueness is genuinely useful.
What is a CSS cascade layer and how does it relate to specificity?
The CSS @layer at-rule (supported in all modern browsers) lets you organise styles into named layers with an explicit priority order. Crucially, layer order is resolved before specificity - a rule in a higher-priority layer wins over a rule in a lower-priority layer even if the lower layer's selector is more specific.
This gives you a powerful tool to manage cascade conflicts without resorting to high-specificity selectors or !important. For example, you can put reset styles in a low-priority layer and component styles in a high-priority layer, guaranteeing that components always win regardless of specificity.
What does the universal selector * contribute to specificity?
Nothing. The universal selector * contributes a specificity of (0,0,0). The same is true for combinators - descendant (space), child (>), adjacent sibling (+), and general sibling (~). They affect which elements a selector matches but have no bearing on its specificity score. So * > div scores (0,0,1), the same as div on its own.
