CSS Selector & Specificity Lab
Compare selector weights and inspect which sample elements match. Explore :is(), :where(), :has(), :not() and one level of CSS nesting.
Separate selectors with commas, including across lines. Do not paste CSS declarations. Ctrl/Cmd + Enter analyzes.
With a parent, & takes the parent list’s highest weight. Selectors without & become descendants.
Sample elements are parsed into an inert template and never displayed or executed. No sample URLs are fetched.
Enable JavaScript to calculate weights and inspect matches.
Selector results
ID · Class / attribute / pseudo-class · Type / pseudo-element
Understand selector weight and sample matching
- Enter a comma-separated selector list and a small HTML fragment.
- Leave the parent blank for ordinary selectors, or add a resolved parent list to explore one nesting level.
- Analyze, compare the three specificity columns from left to right, and expand matched element labels.
- Copy the JSON report when you need a record of the exact resolved selectors and matches.
How are functional selectors counted?
:is(), :not() and :has() contribute
the highest specificity in their argument list. :where() and
everything inside it contribute zero. For example, :is(.card, #featured) > h3 has weight 1 · 0 · 1 even when
the ID branch does not match. :nth-child(2n of .card, #featured) adds one pseudo-class plus
the highest argument weight, giving 1 · 1 · 0.
What does nesting support?
Supply one fully resolved parent list. Each & is resolved through :is(parent-list); the list’s maximum weight is counted at
each occurrence unless it is inside :where(). Without an
explicit &, the child becomes a descendant of the parent. With parent .card, #featured, child & > h3 has weight
1 · 0 · 1. Nested stylesheets, multiple parent levels, Sass suffix
concatenation and pseudo-element parents are outside this lab’s scope.
Does the strongest selector always win?
No. Origins, importance, cascade layers, inline declarations, scope proximity, source order, inheritance, animations and transitions can change the result. This lab does not load stylesheets, inspect computed styles or predict a winning declaration. The three columns are compared lexicographically; they are not a decimal score.
Matching scope and limits
Matching uses your browser’s querySelectorAll on an inert HTML fragment. A fragment has no html/body document wrapper; :root has no matching document element. Browser selector support varies. If any resolved selector is rejected by the browser, the entire analysis is cleared instead of showing an unvalidated weight. :dir() accepts only ltr or rtl. Unknown or unsupported pseudo-selectors are rejected. Shadow DOM, namespaces, :scope, column combinators and custom selector extensions are not supported. Pseudo-elements and live hover, focus, visited, target and custom-element definition states are counted for specificity but are not evaluated as sample matches. Script, style, image and other sample elements stay inert, so no user code runs and no resources load.
Use at most 24 selectors, 4,000 selector characters, 1,000 parent characters, 256 parsed selector nodes and 12 parser nesting levels. Resolved selectors are capped at 12,000 characters. Sample HTML is limited to 30,000 characters, 500 elements and 32 nesting levels. At most 12 matched element labels per selector appear in the report; the count still includes all matches. Calculations run in a worker with a 2.5-second deadline; fragment matching runs on the page within these small bounds.
References
Read MDN’s specificity guide , the CSS nesting selector reference , and the CSSTools specificity implementation .