Skip to content

CSS Specificity Calculator

Paste the selectors that are fighting over an element, one per line. Each gets its ID-class-element score and the list is ranked from most to least specific.

Runs in your browserFree, no sign-upLast reviewed

Settings

One selector per line. They are ranked from most to least specific.

Results update automatically as you type.

Most specific selector

Selector
#sidebar .widget a
Specificity
1-1-1
IDs
1
Classes, attributes, pseudo-classes
1
Elements and pseudo-elements
1
Result
Wins outright over the others

Ranked from most to least specific

SelectorSpecificityIDsClassesElements
#sidebar .widget a1-1-1111
.nav .nav__item a:hover0-3-1031
a.button0-1-1011
header nav ul li a0-0-5005
:where(.reset) a0-0-1001

Specificity is compared column by column from the left, not as a single number. One ID beats any number of classes, because the comparison never reaches the class column when the ID column already differs.

Quick answers

How is CSS specificity calculated?
Count three things in the selector: IDs; classes, attribute selectors and pseudo-classes; element names and pseudo-elements. Write the counts side by side, such as 1-2-1. Compare two selectors from the left count to the right and the first higher number wins. Combinators and the universal selector add nothing.
Does an ID always beat a class?
Yes, within normal selectors. #header scores 1-0-0 and a selector made of eleven classes scores 0-11-0, and the ID still wins because the ID count is compared first. Classes never add up to an ID.
What is the specificity of a class selector?
0-1-0. A single class such as .btn adds one to the middle count. Attribute selectors such as [type="text"] and pseudo-classes such as :hover add to the same count, so a:hover and a.button both score 0-1-1.
What happens when two selectors have the same specificity?
The declaration that comes later in the source wins. .card .title and .sidebar .title both score 0-2-0, so whichever rule appears last in the stylesheet, or in the later-loaded stylesheet, is applied.
What is the specificity of :where()?
Zero. :where() and everything inside its brackets contribute nothing, so :where(.reset) a scores 0-0-1, the same as a plain a. :is(), :not() and :has() are different: each takes the specificity of its most specific argument.
Does !important change specificity?
No. !important does not alter a selector's score. It moves the declaration into a separate, higher-priority tier of the cascade, where it beats every normal declaration. If two !important declarations compete, specificity is compared between those two.

About CSS Specificity Calculator

Specificity is three counts, written here as 1-2-1: the number of IDs, then the number of classes, attribute selectors and pseudo-classes, then the number of element names and pseudo-elements. The browser compares the counts from the left and stops at the first one that differs, so a single ID outranks any number of classes. This calculator works those counts out for every selector you enter, sorts the list and names the winner, or tells you when the top selectors tie and source order decides instead. It scores selectors only: inline styles, !important and cascade layers sit outside the three counts and are covered in the notes below.

CSS specificity chart for common selectors

Every value below is the output of the calculator above for that exact selector.

SelectorSpecificityWhat is counted
*0-0-0The universal selector counts for nothing
li0-0-1One element
ul li0-0-2Two elements
ul > li0-0-2Two elements; the child combinator adds nothing
li::before0-0-2One element and one pseudo-element
h1 + p::first-line0-0-3Two elements and one pseudo-element
.btn0-1-0One class
:root0-1-0One pseudo-class
a:hover0-1-1One pseudo-class and one element
input[type="text"]0-1-1One attribute selector and one element
li:nth-child(2n+1)0-1-1One pseudo-class and one element
.nav > li + li0-1-2One class and two elements
.btn.btn-primary0-2-0Two classes
#header1-0-0One ID
#header .nav a1-1-1One ID, one class, one element
ul#nav li.active a1-1-3One ID, one class, three elements
body #content .data img:hover1-2-2One ID, a class and a pseudo-class, two elements
#a #b2-0-0Two IDs

Worked comparisons: which selector wins

The sample list the page loads with

#sidebar .widget a / .nav .nav__item a:hover / header nav ul li a / a.button / :where(.reset) a

Ranked 1-1-1, 0-3-1, 0-1-1, 0-0-5, 0-0-1. #sidebar .widget a wins outright.

Two classes and a pseudo-class lose to one ID, and five element names lose to one class.

One ID against eleven classes

#header / .a.b.c.d.e.f.g.h.i.j.k

#header is 1-0-0 and the class chain is 0-11-0. #header is ranked first.

This is why the value is not read as the number 100: the middle count can exceed 9 without spilling into the ID count.

Two component selectors that tie

.card .title / .sidebar .title

Both 0-2-0. Result reports a tie and that the rule declared last in the stylesheet wins.

Tied rows stay in the order you entered them.

A negation that carries an ID

:not(#main) / :not(.active)

:not(#main) is 1-0-0 and :not(.active) is 0-1-0.

The :not() itself adds nothing; its argument sets the score.

:is() with a mixed list

:is(#main, .card) p

1-0-1

The whole :is() takes the score of #main, its most specific argument, even for elements that only match .card.

Specificity of :is(), :not(), :has() and :where()

The four pseudo-classes that take a selector list do not count as a pseudo-class themselves; what matters is the list inside the brackets. An ordinary functional pseudo-class such as :nth-child() is shown last for contrast.

Pseudo-classRuleExampleCalculator result
:is()Takes its most specific argument:is(#main, .card) p1-0-1
:not()Takes its most specific argument:not(.active)0-1-0
:has()Takes its most specific argument.card:has(> img)0-1-1
:where()Always zero:where(.reset) a0-0-1
:nth-child()Counts as one pseudo-classli:nth-child(2n+1)0-1-1

Wrapping base and reset styles in :where() keeps them at zero, so any single class can override them later.

What ranks above selector specificity

Specificity is only one step of the cascade, and it is not the first. Before the browser compares selectors it sorts declarations by origin and importance: !important declarations beat normal ones regardless of selector. Next comes the style attribute. A normal declaration written inline as style="..." beats every selector in a stylesheet, including one with several IDs, and only !important can override it from a stylesheet.

Cascade layers come next. When rules are placed in @layer blocks, a normal declaration in a later layer beats one in an earlier layer whatever their selectors score, and normal styles outside any layer beat all layered normal styles. Only when two declarations survive all of that in the same tier does specificity decide, and only when specificity ties does source order decide.

So if this calculator says your selector wins and the style still does not apply, look for an inline style, an !important on the other rule, or a layer ordering before you make the selector heavier.

Input this calculator does not handle

The tool counts with pattern matching and does not parse CSS fully. These cases give a wrong or meaningless score.

  • Comma-separated lists. A line such as .a, #b is scored as one selector (1-1-0). Put each selector on its own line.
  • Nested functional pseudo-classes. div:not(:has(.a)) or :is(.a, :not(#x)) are over-counted; only one level of brackets is resolved.
  • Attribute values containing a dot or a hash. a[href$=".pdf"] is reported as 0-2-1 when the correct value is 0-1-1, because .pdf is read as a class. Check these by hand.
  • Whole rules. Braces, declarations and at-rules such as @media are not stripped, so enter the selector text only.
  • Inline styles, !important and @layer. None of these can be entered; the ranking compares selectors alone.
  • Whether a selector matches. The tool does not see your HTML, so a winning score only matters if both selectors target the same element.

How to use it

  1. 1

    Type or paste into Selectors, one selector per line. Leave out the braces and declarations, and split a comma-separated list onto separate lines.

  2. 2

    Read the Most specific selector panel: it shows the winning selector, its Specificity value and the three counts behind it.

  3. 3

    Check Result. It says either that the top selector wins outright or that it is tied, in which case the rule declared last in the stylesheet applies.

  4. 4

    Scan the table Ranked from most to least specific to see where each of your other selectors falls and which column separates them.

  5. 5

    Edit a selector and compare again until the rule you want on top is on top without reaching for !important.

Frequently asked questions

Why is my CSS rule not being applied?
Most often a different rule sets the same property with a more specific selector, or with equal specificity later in the source. Copy both selectors from your browser's style inspector into the calculator. If yours ranks first and still loses, check for an inline style, !important or a cascade layer.
Why is specificity written as 1-2-1 and not 121?
Because the three counts never carry into each other. A selector with twelve classes is 0-12-0, which a three-digit number cannot show, and it still loses to 1-0-0. Some references write the same thing as (1,2,1) or add a leading count for inline styles, such as 0,1,2,1.
Do combinators such as > and + add specificity?
No. The descendant space, child (>), next-sibling (+) and subsequent-sibling (~) combinators change which elements match but not the score. .nav li and .nav > li are both 0-1-1.
Do pseudo-elements count the same as pseudo-classes?
No. Pseudo-elements such as ::before, ::after and ::first-line count with element names in the last column. Pseudo-classes such as :hover, :focus-visible and :first-child count with classes in the middle column. li::before is 0-0-2 while li:hover is 0-1-1.
Does the order of classes or the length of the selector matter?
Only the counts matter. .btn.primary and .primary.btn are both 0-2-0, and a long selector made of element names can lose to a short one with a class: header nav ul li a is 0-0-5 and a.button is 0-1-1.
How do I raise specificity without adding an ID?
Add a class or attribute that is already on the element, or repeat a class: .btn.btn is a valid selector that scores 0-2-0. Better still, lower the competing rule, for example by wrapping it in :where(), so the stylesheet does not escalate.
Should I avoid ID selectors in CSS?
For styling, usually. A rule keyed on an ID can only be overridden by another ID, an inline style, !important or a later cascade layer, which pushes later rules to escalate. Keeping most selectors at one or two classes makes source order predictable. IDs remain fine for anchors, labels and scripts.
Does the universal selector have any specificity?
No. The * selector scores 0-0-0, so any element, class or ID selector overrides it. That is why resets written with * are easy to override.

Looking for something else? Browse all css tools or see every tool.