ToolNimba

๐ŸŽฏ CSS Specificity Calculator (a, b, c) Selector Weight Score

Shihab Mia By Shihab Mia ยท Updated 2026-07-05

Specificity
0, 0, 0
Weight 0 - type a selector above.
0
a - IDs
0
b - classes / attrs / pseudo-classes
0
c - elements / pseudo-elements
What counted
  • Token breakdown appears here.
Try an example

Higher tuples win. Compare a first, then b, then c (like comparing the digits of a number). The universal selector (*) and combinators (>, +, ~, space) add nothing.

This CSS specificity calculator parses any selector into its (a, b, c) score so you can see exactly which rule the browser applies. Paste a selector and it counts a as ID selectors, b as classes, attributes and pseudo-classes, and c as element names and pseudo-elements. You get the weight, a token-by-token breakdown of what counted, and a plain explanation of how that score is compared against competing rules to break a styling tie.

What is the CSS Specificity Calculator?

Every CSS selector carries a specificity value, written as three numbers (a, b, c). The first number a is the count of ID selectors (#header). The second number b is the count of class selectors (.nav), attribute selectors ([type="text"]) and pseudo-classes (:hover). The third number c is the count of type selectors, that is element names (div, li), plus pseudo-elements (::before). The universal selector (*) and combinators (the descendant space, the child combinator >, the adjacent sibling combinator + and the general sibling combinator ~) add nothing to any column. This three-part model replaced the old four-part (a, b, c, d) notation once inline styles were separated out of the selector score, but the comparison logic is identical.

When two rules target the same property on the same element, the browser compares their specificity column by column, left to right, like comparing the digits of a number. It looks at a first: a higher a always wins, no matter what b and c are. Only when a is tied does b decide, and only when both a and b are tied does c decide. So one ID (1, 0, 0) beats any number of classes, and one class (0, 1, 0) beats any number of element names. If specificity is completely equal, the rule that appears later in the source order wins, which is why the order of your stylesheets and rules matters.

The functional pseudo-classes behave specially and trip up even experienced developers. The :not(), :is() and :has() functions do not add specificity themselves; instead they take the specificity of the most specific selector in their argument list. So :is(#id, .class) contributes (1, 0, 0) because the ID is the most specific argument. The :where() function is the deliberate opposite: it and everything inside it always count as zero, which makes it ideal for low-specificity defaults and design-system base styles that are trivially easy to override later.

Specificity is only one layer of the wider cascade. Before specificity is even consulted, the browser sorts declarations by origin and importance: user-agent styles, then normal author styles, then author styles marked !important, then user !important, in a defined precedence order. Cascade layers (@layer) also sit above specificity, so a declaration in a later layer beats one in an earlier layer regardless of selector weight. Only when declarations share the same origin, importance and layer does the (a, b, c) score decide the outcome.

Inline style attributes and the !important flag sit outside the (a, b, c) score entirely. An inline style="" attribute effectively outranks any selector-based rule of the same importance, and an !important declaration jumps above all normal declarations. This is why chasing wins by piling on more ID and class selectors leads to brittle stylesheets. Modern best practice is to keep specificity flat and low, lean on source order and cascade layers for control, and reserve !important for genuine utility overrides rather than everyday conflicts.

Knowing the score also guides refactoring. When a component style is hard to override, this calculator shows you which token is inflating the weight, an ID, a stacked class chain, or a nested descendant selector, so you can flatten it. Teams building design systems often target a ceiling such as a single class (0, 1, 0) for component styles and wrap resets in :where() to guarantee they never fight application code.

When to use it

  • Working out why a CSS rule you wrote is being ignored and another rule is winning instead.
  • Comparing two competing selectors to see which one the browser will apply to an element.
  • Refactoring a bloated stylesheet to lower specificity so overrides become predictable and easy.
  • Teaching or learning the cascade by experimenting with how each part of a selector scores.
  • Auditing a design system to keep component styles at a flat, low specificity using :where().
  • Deciding whether a conflict needs a more specific selector, source-order change, or a cascade layer.

How to use the CSS Specificity Calculator

  1. Type or paste a CSS selector into the input box.
  2. Read the (a, b, c) tuple shown at the top: a = IDs, b = classes / attributes / pseudo-classes, c = elements / pseudo-elements.
  3. Check the "What counted" list to see exactly which token added to which column.
  4. Click an example chip to load a sample selector and watch the score change in real time.
  5. Compare the tuple against another selector left to right (a, then b, then c) to see which rule wins.
  6. If the scores tie, remember the rule written later in the source order takes precedence.

Formula & method

Specificity = (a, b, c) where a = number of ID selectors, b = number of class, attribute and pseudo-class selectors, c = number of type (element) selectors and pseudo-elements. Compare two selectors column by column from a to c; the first column that differs decides the winner, and an exact tie is broken by source order. The universal selector and combinators score 0, :where() and its contents score 0, and :not() / :is() / :has() inherit the score of their most specific argument.
CSS Specificity: the (a, b, c) scoreCompared left to right. Column a beats b beats c. Ties break on source order.a = IDs#headerhighest weight1b = classes.nav [type] :hoverclasses, attrs, pseudo-classes0c = elementsdiv li ::beforetypes, pseudo-elements0>>Example: #nav ul li.active a:hover = (1, 2, 3)* and combinators (> + ~ space) and :where() score 0

Worked examples

Score the selector #nav ul li.active a:hover

  1. #nav is an ID selector, so a = 1
  2. .active is a class and :hover is a pseudo-class, so b = 2
  3. ul, li and a are element selectors, so c = 3
  4. The descendant spaces between them add nothing
  5. Combine the columns into the tuple (a, b, c)

Result: Specificity = (1, 2, 3)

Compare .btn.primary against #cta on the same button

  1. .btn.primary has no ID and two classes, so it scores (0, 2, 0)
  2. #cta has one ID and nothing else, so it scores (1, 0, 0)
  3. Compare column a first: 0 for the classes versus 1 for the ID
  4. A higher a wins outright, the b column never gets a vote

Result: #cta (1, 0, 0) wins over .btn.primary (0, 2, 0)

Score li:not(.first, #x) using the most specific argument rule

  1. li is an element selector, contributing c = 1
  2. :not() itself adds nothing, but it takes its most specific argument
  3. Inside :not() the arguments are .first (0, 1, 0) and #x (1, 0, 0)
  4. #x is the most specific, so :not() contributes (1, 0, 0)
  5. Add the element li to get the final score

Result: Specificity = (1, 0, 1)

How common selector parts contribute to the (a, b, c) score

Selector partExampleColumnAdds
ID selector#headera1 to a
Class selector.menub1 to b
Attribute selector[type="text"]b1 to b
Pseudo-class:hoverb1 to b
Type (element) selectordivc1 to c
Pseudo-element::beforec1 to c
Universal selector*none0
Combinators> + ~ (space)none0
:where() and its contents:where(.a, #b)none0

Worked specificity scores for example selectors

SelectorabcTuple
*000(0, 0, 0)
li001(0, 0, 1)
ul li a003(0, 0, 3)
.active010(0, 1, 0)
a:hover011(0, 1, 1)
input[type="text"]011(0, 1, 1)
:is(.a, div)010(0, 1, 0)
:where(.a, #b)000(0, 0, 0)
#nav ul li.active a:hover123(1, 2, 3)
p::first-line002(0, 0, 2)

Cascade precedence, from lowest to highest priority

RankLayer of the cascadeBeats everything below it
1 (lowest)User-agent (browser default) stylesBaseline only
2Normal author styles, ranked by @layer then specificityYes
3Inline style="" attribute (normal)Yes
4Author !important declarationsYes
5Inline style="" with !importantYes
6 (highest)User and user-agent !important, plus CSS transitionsYes

Common mistakes to avoid

  • Thinking more selectors always means higher specificity. It is the columns that matter, not the total length. A single ID at (1, 0, 0) beats a long chain of ten classes at (0, 10, 0), because column a is compared before column b.
  • Treating the tuple as a single base-10 number. Specificity is compared per column, not added into one figure. Eleven classes do not roll over into an ID. (0, 11, 0) is still beaten by (1, 0, 0), even though 11 looks bigger than 1.
  • Forgetting that !important and inline styles sit outside the score. The (a, b, c) calculation only ranks normal selectors. An inline style attribute outranks them all, and an !important declaration outranks everything except another !important, so neither shows up in the tuple.
  • Assuming :not() and :is() add to the score. These functional pseudo-classes contribute nothing on their own. They simply borrow the specificity of the most specific selector inside their brackets, which is easy to miss.
  • Counting the universal selector or combinators. The * selector and the >, +, ~ and descendant (space) combinators never change specificity. Only the simple selectors they join together are counted.
  • Ignoring cascade layers and source order. A rule can lose even with higher specificity. A declaration in a later @layer, or a later rule of equal specificity in source order, wins first. Specificity only decides ties within the same layer and origin.

Glossary

Specificity
The weight a browser assigns to a selector, written (a, b, c), used to decide which rule wins when several target the same element and property.
Selector
The pattern before a CSS rule that chooses which elements the declarations apply to, for example #nav ul li.active.
Cascade
The full set of rules (origin, importance, cascade layer, specificity, source order) the browser follows to resolve conflicting declarations.
Pseudo-class
A keyword starting with one colon, like :hover or :first-child, that targets an element in a particular state. Counts toward b.
Pseudo-element
A keyword starting with two colons, like ::before or ::first-line, that styles a part of an element. Counts toward c.
Combinator
A character that relates two selectors, such as the descendant space, child >, adjacent sibling + or general sibling ~. Adds no specificity.
!important
A flag appended to a declaration that lifts it above all normal declarations in the cascade, sitting outside the (a, b, c) score entirely.
Cascade layer
A named group of styles created with @layer whose priority is decided by layer order before specificity is even compared.

Frequently asked questions

What is CSS specificity?

Specificity is the weighting system browsers use to decide which CSS rule applies when more than one targets the same element and property. It is expressed as three numbers (a, b, c) counting IDs, then classes / attributes / pseudo-classes, then elements / pseudo-elements. The higher value, compared column by column, wins.

How is the (a, b, c) specificity score calculated?

Count the ID selectors for a, the class, attribute and pseudo-class selectors for b, and the type (element) selectors and pseudo-elements for c. The universal selector and combinators add nothing. This calculator parses your selector and reports each column automatically.

Which selector wins if two rules conflict?

Compare the tuples column by column from left to right. Whichever has the larger a wins; if a ties, the larger b wins; if a and b tie, the larger c wins. If all three columns are equal, the rule written later in the source order wins.

Does !important affect specificity?

No. The !important flag is not part of the (a, b, c) score. It sits above normal specificity in the cascade: an !important declaration beats any normal declaration regardless of selector weight, and only another !important of equal or higher origin priority can override it.

How do :not(), :is() and :has() count toward specificity?

These functional pseudo-classes add nothing themselves. They take on the specificity of the most specific selector inside their argument list. So :not(#id) contributes (1, 0, 0), while :is(.a, div) contributes (0, 1, 0) because the class is more specific than the element.

What is the specificity of :where()?

The :where() pseudo-class always has zero specificity, and so does everything inside it. That makes :where(.a, #b) score (0, 0, 0). It is designed for low-specificity defaults, resets and design-system base styles that any later rule can override without a fight.

Why does my class rule lose to an ID rule even with several classes?

Because column a (IDs) is always compared before column b (classes). A single ID scores (1, 0, 0), which beats any number of classes such as (0, 5, 0). To win, you need either a higher a value or to lower the competing rule below an ID.

What specificity does an inline style have?

Inline styles set with the style attribute are not scored as (a, b, c) at all. In the cascade they rank above all normal selector-based rules of the same importance, so a normal inline style beats a normal rule even if that rule uses IDs. Only an !important declaration can override a normal inline style.

Do cascade layers change how specificity works?

Cascade layers created with @layer are compared before specificity. A declaration in a later layer beats one in an earlier layer no matter the selector weight, and unlayered normal styles win over layered ones. Specificity only decides conflicts within the same layer and origin.

How can I lower specificity to make overrides easier?

Prefer single-class selectors, avoid chaining IDs and long descendant chains, and wrap resets or defaults in :where() so they score zero. Use cascade layers and source order for control instead of stacking selectors, and reserve !important for genuine utility overrides.