CSS Selector Tester
Test a CSS selector against your own HTML, see every element it matches and get its specificity score.
Matched with the browser's own querySelectorAll; specificity follows
W3C Selectors Level 4.
3 matching elements
Specificity (0, 1, 2)
0 IDs, 1 class/attribute/pseudo-class, 2 type/pseudo-elements.
- #1
<a>html > body > ul > li:nth-of-type(1) > a<a href="/home">Home</a> - #2
<a>html > body > ul > li:nth-of-type(2) > a<a href="/docs" data-nav="docs">Docs</a> - #3
<a>html > body > ul > li:nth-of-type(3) > a<a href="/pricing">Pricing</a>
Nothing matched this selector.
Specificity rules (Selectors Level 4)
#id- adds 1 to A, the ID column
.class[attr]:hover- adds 1 to B
div::before- adds 1 to C, type and pseudo-element
*- adds nothing at all
>+~- combinators add nothing
:not():is():has()- take their strongest argument
:where()- always zero, whatever is inside
a, b- each selector in a list is scored on its own
How it works
Type a CSS selector and paste the markup you want to check: the page parses your HTML with
DOMParser and runs the selector through the browser's own
querySelectorAll, so the matches you see are exactly the ones your stylesheet or
your document.querySelectorAll call will find. Every hit is listed with its
position, a unique nth-of-type path you can paste back into your code, and the
element's own HTML. An invalid or unsupported selector shows the engine's real
SyntaxError instead of quietly returning nothing.
Alongside the matches you get the specificity of the selector as the
(A, B, C) triple defined by W3C Selectors Level 4: A counts ID selectors,
B counts classes, attribute selectors and pseudo-classes, C counts type selectors and
pseudo-elements. The universal selector * and the combinators count for
nothing, :where() is always zero, and :not(), :is()
and :has() take the specificity of their strongest argument. A selector list is
scored one item at a time, because that is what the cascade does.
Your markup is parsed as a standalone document, so the parser adds the usual
<html>, <head> and <body> wrappers
around a fragment, and a selector such as * or :root will match
those too. Nothing is uploaded: parsing, matching and scoring all happen in your browser, so
it is safe to paste markup from a private or production page.
Frequently asked questions
How do I test a CSS selector against my own HTML?
Paste your markup into the HTML box and type a selector in the field above it. The page parses the markup with DOMParser and runs your selector through the browser's own querySelectorAll, so the list of matches updates as you type and is exactly what your stylesheet or your JavaScript will find. Each hit shows its tag, a unique nth-of-type path you can paste back into your code, and the element's own HTML.
How is CSS specificity calculated?
W3C Selectors Level 4 scores a selector as the triple (A, B, C): A counts ID selectors, B counts classes, attribute selectors and pseudo-classes, and C counts type selectors and pseudo-elements. Combinators such as >, + and ~ and the universal selector * add nothing at all. The columns are compared left to right, so a single ID always beats any number of classes, and the triple is never carried over like a decimal number.
Do :not(), :is() and :where() change the specificity?
Yes, and this tool handles all three. :not(), :is() and :has() are transparent: they contribute the specificity of their strongest argument, so div:not(.a, #b) scores (1, 0, 1) because #b outranks .a. :where() is the opposite and always contributes zero, whatever you put inside it, which is why it is the right wrapper for low-priority defaults. :nth-child(n of S) counts as one pseudo-class plus the specificity of S.
Is my HTML uploaded anywhere?
No. Parsing, matching and the specificity scoring all happen in your browser with DOMParser and querySelectorAll, and nothing is ever sent to a server. There is no signup and no account, so it is safe to paste markup from a private, internal or production page. One thing to note: your markup is parsed as a standalone document, so the parser adds the usual html, head and body wrappers around a fragment, and a selector like * or :root will match those too.