See the (A, B, C) specificity of any selector, part by part, and which rule wins
Runs in your browser. Nothing you type is sent anywhere. Checked against sources on 17 Oct 2026.CSS specificity is a score of three numbers, (A, B, C): A counts ID selectors, B counts classes, attribute selectors and pseudo-classes, and C counts type selectors and pseudo-elements. The browser compares A first, so one ID at (1, 0, 0) beats any number of classes, and a tie goes to the rule written last.
Type or paste one selector per line, or paste whole CSS rules.
Read the (A, B, C) score of each selector: IDs, then classes, attributes and pseudo-classes, then types and pseudo-elements.
Each part of the selector is coloured by the column it adds to, including inside :is(), :not() and :has().
The selector marked as the winner is the one the browser applies when they target the same element.
When two rules set the same property on the same element, the browser applies the one with the higher specificity. Paste your selectors, one per line or as whole CSS rules, and each gets its score with every part coloured by the column it adds to.
How the score is counted. The rules are those of Selectors Level 4, section 15:
| Part of the selector | Adds to | Example |
|---|---|---|
| ID | A | #nav |
| Class, attribute, pseudo-class | B | .item, [href], :hover |
| Type, pseudo-element | C | li, ::before, :first-line |
Universal, combinators, :where() |
nothing | *, >, +, ~ |
The functional pseudo-classes. :is(), :not() and :has() take the score of the most specific selector inside them, so :is(.a, #b) counts as an ID even when .a is what matched. :nth-child(2n of .item) counts one pseudo-class plus the selector after "of". :where() always counts zero, which makes it the tool for writing defaults that are easy to override. From the Shadow DOM rules, :host(.x) and ::slotted(.x) add their argument to their own count.
Comparing. A is compared first, then B, then C. Ten classes, (0, 10, 0), still lose to one ID, (1, 0, 0). When the scores are equal, the rule written later wins; the winner is marked on that basis. Inline styles and !important sit above specificity altogether and are not part of the score.
Checked against browsers' own tests. The counting is tested on the specification's 14 worked examples, the has-, is- and not-specificity test pages that every browser runs from web-platform-tests, and 360 selectors generated as syntax trees whose scores were summed separately. The nesting selector & takes its parent's score and is flagged rather than guessed. Nothing you paste leaves the browser.
Count three things in the selector: IDs give A, classes, attribute selectors and pseudo-classes give B, and type selectors and pseudo-elements give C. The universal selector and combinators count nothing. So ul#nav li.active a scores (1, 1, 3): one ID, one class and three element names.
Yes. Browsers compare the A column first and only look at B when A is tied, so (1, 0, 0) beats (0, 10, 0) and even (0, 99, 0). Classes never add up into an ID. Rather than adding IDs to win, lower the other rule with :where() or restructure the selectors.
A selector with :is() gets the score of the most specific selector in its list, whichever one matched, so :is(.a, #b) counts as an ID. :where() always scores zero, whatever it contains. :not() and :has() follow the :is() rule, taking their most specific argument.
Usually another rule targeting the same element has a higher specificity, or the same specificity and appears later in the stylesheet. Paste both selectors here to see the scores. Other causes are !important on the other rule, an inline style attribute, a cascade layer, or a selector that does not match at all.
No. Pseudo-classes such as :hover or :first-child count in B, like a class. Pseudo-elements such as ::before or ::marker count in C, like an element name. The old one-colon forms :before, :after, :first-line and :first-letter are pseudo-elements and are counted as such.
Neither is part of the (A, B, C) score. A style attribute beats every selector in a stylesheet, and an !important declaration beats normal declarations whatever their specificity. Between two !important declarations, or two normal ones, the selector specificity shown here decides, then the order in the source.
Rates and rules change; check the source before you rely on a figure.
Still stuck? Ask about CSS Specificity Calculator and anybody who knows can answer. No account needed.
On the related tools: Readability Checker – Flesch reading ease and grade level, with what to change · JSONPath Evaluator – Evaluate a JSONPath expression and get the matching values and their paths.