CSS Selector Validator
Verify whether a CSS selector is valid and compute its specificity (a,b,c). Lists detected classes, IDs and elements.
CSS selector validation: from syntax to specificity
A CSS selector is the pattern that picks DOM nodes for styling or scripting. Validation has two layers: syntax (does the selector parse?) and semantics (does it select what the author intended?). The W3C spec Selectors Level 4 standardizes the grammar; browsers implement it through WebKit (Safari), Blink (Chrome, Edge, Opera) and Gecko (Firefox). The runtime test is simple: document.querySelector(selector) throws a SyntaxError on invalid input, so any validator can wrap it in try/catch.
Selector types and combinators
- Element:
div,p,aโ by tag name. - Class:
.btn,.cardโ byclassattribute. - ID:
#main,#navโ byid, must be unique. - Attribute:
[type="text"],[href^="https"],[data-id]. - Pseudo-class:
:hover,:focus,:nth-child(2n+1),:not(.x),:has(> img). - Pseudo-element:
::before,::after,::placeholder,::selection. - Descendant:
a b(any depth) vs child:a > b(direct child only). - Adjacent sibling:
a + b(immediately after) vs general sibling:a ~ b(any later sibling).
Specificity calculation
When two rules target the same element, the higher specificity wins. The classic formula is (a, b, c) read as a three-digit number: a = number of IDs, b = classes + attribute selectors + pseudo-classes, c = elements + pseudo-elements. Inline styles add a fourth, higher field; !important overrides the whole calculation but should be a last resort.
#main .card a:hover โ (1, 2, 1) โ 121
.btn.primary โ (0, 2, 0) โ 020
ul li โ (0, 0, 2) โ 002
div > .item::before โ (0, 1, 2) โ 012
A common gotcha: :not(), :is() and :where() change the rules. :where() contributes zero specificity regardless of its argument โ extremely useful for resets that you want easy to override. :is() contributes the specificity of its most specific argument.
Browser support and :has()
Most CSS Selectors Level 4 features are stable across modern browsers, but a few are recent. :has() โ the "parent selector" โ landed in Chrome 105 (Aug 2022), Safari 15.4 (Mar 2022) and Firefox 121 (Dec 2023). Before relying on it in production, check usage telemetry (caniuse.com/css-has) and gate it behind @supports selector(:has(*)). :focus-visible, :focus-within, ::backdrop are now safe on all evergreen browsers.
Use cases beyond styling
- DOM query in JS:
document.querySelector,querySelectorAll,element.matches,element.closest. - E2E testing: Cypress (
cy.get('selector')), Playwright (page.locator('selector')), Puppeteer (page.$(selector)). - Web scraping: Cheerio (Node), BeautifulSoup with
select()(Python), Goquery (Go). - Legacy jQuery: the Sizzle engine extended CSS with non-standard pseudos (
:contains,:visible) that don't work inquerySelector.
Performance and anti-patterns
Browsers match selectors right-to-left, so the rightmost piece (the key selector) drives initial filtering. getElementById is the fastest lookup; querySelector('#id') is marginally slower because it parses the selector first. Long descendant chains like body > div > div > main > article > section > h2.title are fragile โ any markup change breaks them. The best practice for E2E tests is to use data attributes as stable hooks: [data-testid="submit-button"]. They decouple test code from styling and survive refactors.
FAQ
Can I test :has() in this validator? Yes, as long as you run it in a modern browser. The validator delegates to document.querySelector, so if your browser supports :has() the validator does too.
How do I select a parent element? Use :has(): div:has(> img) matches divs that contain a direct-child image. Before :has() shipped, the only option was JavaScript traversal (element.parentElement).
For E2E tests, should I prefer CSS or XPath? CSS. Playwright, Cypress and modern QA tooling treat CSS as first class; XPath is more powerful (full text search, ancestor axis) but verbose and slow. Reach for XPath only when CSS cannot express the query.
Does :scope work in querySelector? Yes. element.querySelectorAll(':scope > li') matches only direct-child li of element, avoiding the historical bug where the selector starts from the document.
Why does my selector with quotes fail? Probably nested quoting. '[data-x="a"]' is fine; "[data-x=\"a\"]" needs escaping. In CSS-in-JS templates, prefer single-quoted strings or backtick literals to avoid escape sequences.
Related Tools
CPF Validator
Validate Brazilian CPF numbers instantly using the official algorithm. Useful for testing document validation in applications. No data sent to servers.
Batch CPF Validator
Validate a list of CPFs (one per line) and see which are valid and which are not. No data sent to servers.
Batch CNPJ Validator
Validate a list of CNPJs (one per line) with a summary of valid, invalid and total. No data sent to servers.