CSS Specificity Calculator Compare Selectors — See the (A,B,C) Score & Which Rule Wins
Enter any CSS selector and see its exact specificity score broken down into (A, B, C) — where A counts ID selectors, B counts class/attribute/pseudo-class selectors, and C counts element type and pseudo-element selectors. Compare multiple selectors simultaneously to see which rule wins in a cascade conflict. Every part of the selector is colour-coded: IDs in purple, classes and attributes in amber, elements in green. Handles :not(), :is(), :has(), :where() (0 specificity), !important indicator, and 20+ preset examples.
Enter Selectors — See the (A,B,C) Score & Which Wins
Add CSS selectors below. Every part is colour-coded and explained. Compare multiple selectors to find the winner.
CSS specificity quick reference
#header(1,0,0)#nav #item(2,0,0).menu-item(0,1,0)[type="text"](0,1,0):hover(0,1,0):where()(0,0,0)div(0,0,1)p a(0,0,2)::before(0,0,1)*(0,0,0)!importantBeats allRate this tool
CSS Specificity Explained — How Browsers Decide Which Rule Wins
CSS specificity is the algorithm browsers use to determine which CSS rule applies to an element when two or more rules target the same element with conflicting declarations. It is one of the three components of the CSS cascade — alongside cascade origin (user agent, author, user stylesheets) and cascade order (source position). Understanding specificity is essential for writing maintainable CSS that behaves predictably.
The (A,B,C) specificity tuple
Specificity is represented as a three-number tuple (A, B, C). Each number counts a different type of selector component:
| Column | Counts | Selector types | Example | Score |
|---|---|---|---|---|
| A | ID selectors | #id | #header | (1,0,0) |
| B | Class selectors | .class | .nav-item | (0,1,0) |
| B | Attribute selectors | [attr] | [type="text"] | (0,1,0) |
| B | Pseudo-classes | :pseudo | :hover, :nth-child(2) | (0,1,0) |
| C | Element selectors | element | div, p, a | (0,0,1) |
| C | Pseudo-elements | ::pseudo | ::before, ::after | (0,0,1) |
| – | Universal, combinators | * + > ~ space | * div > p | (0,0,0) |
Comparison is done column by column from left (A) to right (C). The first column with a higher number wins. If all three are equal, the last rule in the CSS source order wins.
How to read a specificity score
The selector ul#nav li.active a breaks down as: ul = +1 element (C), #nav = +1 ID (A), li = +1 element (C), .active = +1 class (B), a = +1 element (C). Total: (1, 1, 3). To beat this with another selector, you need a higher A value, or the same A with a higher B value, or the same A and B with a higher C value. One ID (1,0,0) beats (0,99,99) — columns are never carried over.
CSS Specificity Examples — Real Selector Scores
Paste any of these into the calculator above to see the colour-coded breakdown.
| Selector | Score | Explanation |
|---|---|---|
| * | (0,0,0) | Universal selector — no specificity |
| p | (0,0,1) | 1 element |
| div p | (0,0,2) | 2 elements |
| .menu-item | (0,1,0) | 1 class |
| p.intro | (0,1,1) | 1 class + 1 element |
| ul li.active a | (0,1,3) | 1 class + 3 elements |
| #header | (1,0,0) | 1 ID — beats any B or C combination |
| #header .nav | (1,1,0) | 1 ID + 1 class |
| #nav ul#menu li | (2,0,2) | 2 IDs + 2 elements |
| a:hover | (0,1,1) | 1 pseudo-class + 1 element |
| input[type="text"] | (0,1,1) | 1 attribute + 1 element |
| li:nth-child(2n+1) | (0,1,1) | 1 pseudo-class + 1 element |
| p::before | (0,0,2) | 1 pseudo-element + 1 element |
| :not(.active) | (0,1,0) | :not() takes argument's specificity (.active = class) |
| :is(#id, .class) | (1,0,0) | :is() takes most specific argument (#id = ID) |
| :where(#id, .class) | (0,0,0) | :where() always 0 specificity |
| div.card.featured.active | (0,3,1) | 3 classes + 1 element |
| #main .content p.intro a | (1,2,2) | 1 ID + 2 classes + 2 elements |
!important, Inline Styles, :where(), :is(), :not() — Special Specificity Rules
!important — the cascade override
!important does not have a specificity score — it is a cascade override. A declaration marked !important beats any normal (non-important) declaration regardless of specificity. When two !important declarations conflict, normal specificity rules apply between them. Using !important makes CSS harder to maintain because future overrides require yet another !important. The modern alternative is CSS Cascade Layers (@layer).
Inline styles — the implicit selector
The style attribute directly on an HTML element has the highest specificity of any selector in the author origin. It is sometimes represented as (1,0,0,0) in a four-column model. Inline styles beat any selector-based rule in your stylesheet without !important. This is why frameworks like React and Vue emit inline styles carefully — they are nearly impossible to override from a stylesheet.
:where() — always zero specificity
:where() accepts a selector list but its specificity contribution is always 0. This is deliberately designed to allow low-specificity base styles that can be easily overridden. For example, :where(article) p has specificity (0,0,1) — only the p element counts. The selector inside :where() is completely ignored for specificity purposes. This makes :where() ideal for CSS reset rules and design system foundations.
:not(), :is(), :has() — take argument specificity
These functional pseudo-classes pass through the specificity of their arguments. :not() has 0 specificity itself but counts its argument: :not(#id) contributes (1,0,0). :is() takes the specificity of its most specific argument: :is(h1, h2, h3) contributes (0,0,1) but :is(#main, .content) contributes (1,0,0) because #main is the most specific. :has() works the same way as :is() for specificity.
Combinators have 0 specificity
The combinators — descendant (space), child (>), adjacent sibling (+), and general sibling (~) — contribute nothing to specificity. div > p has specificity (0,0,2), the same as div p. The combinator type affects which elements match but not how strongly the rule wins.
CSS Specificity Wars — How to Fix Specificity Problems
document.getElementById), not for styling. In CSS, use classes exclusively. If you need to override a library or third-party CSS that uses IDs, use :is(#id) .my-class to borrow the ID's specificity while keeping the class-based structure.@layer lets you define named layers with an explicit order. Rules in later layers win over rules in earlier layers regardless of specificity. This solves the "I need to override a framework but it has high specificity" problem without resorting to !important. Declare: @layer reset, base, components, utilities; then place rules in each layer. Utilities always win over components regardless of specificity..block__element--modifier — encourages using only class selectors for all styling, keeping specificity consistently at (0,1,0) throughout the codebase. When all rules have the same specificity, cascade order (source position) becomes the tiebreaker, which is predictable and easy to reason about. This is why most modern CSS frameworks and design systems use BEM or similar flat-class approaches.LazyTools vs Other CSS Specificity Calculators
| Feature | LazyTools | specificity.keegan.st | Polypane | cssbootcamp |
|---|---|---|---|---|
| Compare multiple selectors | ✅ Side-by-side + winner | ✅ Multiple rows | ❌ One at a time | ❌ One at a time |
| Colour-coded breakdown | ✅ ID/class/element colours | ✅ Yes | ⚠ Partial | ❌ No |
| :where(), :is(), :has() | ✅ All handled | ✅ Yes | ✅ Yes | ⚠ Partial |
| !important indicator | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Bar graph visualization | ✅ Per selector | ❌ No | ❌ No | ❌ No |
| Preset examples | ✅ 20+ clickable presets | ❌ No | ❌ No | ❌ No |
| Winner ranking | ✅ Yes | ❌ No | ❌ No | ❌ No |
| No account required | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
CSS Specificity FAQ
The algorithm browsers use to decide which CSS rule applies when multiple rules conflict. Calculated as (A,B,C): A = ID selectors, B = class/attribute/pseudo-class, C = element/pseudo-element. Higher values win. Paste any selector above to see its score instantly.
Count A: number of # ID selectors. Count B: number of .class, [attribute] and :pseudo-class selectors (not :where()). Count C: number of element type and ::pseudo-element selectors. Universal (*) and combinators (+, >, ~) contribute 0. Enter your selector above for the exact breakdown.
Yes. !important overrides all specificity levels for that property within the same cascade origin. When two !important declarations conflict, normal specificity rules apply between them. Avoid !important — use CSS Cascade Layers (@layer) instead for structured override control.
(1,0,0). One ID beats any number of classes combined — (1,0,0) always beats (0,99,99). This is why overusing IDs in CSS causes specificity problems. Best practice: use IDs only for JavaScript, not for styling.
:where() always has (0,0,0) specificity — its contents are ignored. :is() takes the specificity of its most specific argument. Use :where() when you want overridable base styles. Use :is() as a convenient shorthand that preserves specificity.
The last rule in source order wins. CSS reads top to bottom — when specificity is tied, later declarations override earlier ones. This is the foundational cascading principle.
No. * has (0,0,0). Combinators (+, >, ~, space) also have 0. :where() has 0. This means div * p has the same specificity as div p — both are (0,0,2).
:hover, :focus, :nth-child(), :first-child all add (0,1,0). Exception: :where() adds (0,0,0). :not(), :is(), :has() pass through their argument's specificity — :not(#id) contributes (1,0,0).
:not() itself has 0 specificity, but its argument counts. :not(p) adds (0,0,1). :not(.active) adds (0,1,0). :not(#main) adds (1,0,0). The :not() wrapper contributes nothing — only its contents matter.
Enter any CSS selector to see its (A,B,C) score with colour-coded breakdown. Add multiple selectors to compare and find the winner. 20+ presets. Handles :not(), :is(), :where(), !important. Free, browser-side, no account.
Best options: 1) Restructure CSS to avoid high-specificity selectors. 2) Add a class to raise specificity. 3) Use a more specific selector. 4) Move rule later in source order. 5) Use CSS @layer for structured specificity management. 6) !important as last resort.
Inline styles have (1,0,0,0) specificity — they beat any selector-based rule without !important. Avoid inline styles in complex CSS systems as they are nearly impossible to override from a stylesheet.
Almost always a specificity conflict. Compare your selector against others in this calculator. Use browser DevTools: Inspect element → Styles panel — crossed-out declarations are being overridden by the highlighted rule above them.
@layer allows you to create named, ordered layers of CSS. Later layers beat earlier layers regardless of specificity. This solves specificity wars without !important. Declare: @layer reset, base, components, utilities; then place rules in each layer.
(0,1,1) means 0 ID selectors, 1 class/attribute/pseudo-class, 1 element type. Example: p.intro = 0 IDs, 1 class (.intro), 1 element (p). To beat it you need at least (0,1,2), (0,2,0), or (1,0,0).
(0,0,1) always beats (0,0,0). Comparison is column by column. (0,0,0) is the universal selector and combinators. (0,0,1) is any element selector like p or div. Any element selector beats the universal selector.