CSS Selector ↔ XPath Converter

Convert between CSS selectors and XPath expressions for the common patterns both languages share

CSSXPath

Examples:

//*[@id='nav']//*[contains(concat(' ', normalize-space(@class), ' '), ' active ')]
Test this in XPath Tester
Supports tag/#id/.class/[attr] selectors, the space/>/+/~ combinators, and :first-child, :last-child, :nth-child(n). Dynamic pseudo-classes (:hover, :not()) have no XPath equivalent and aren't supported. XPath → CSS only reverses patterns this converter itself produces — XPath using other axes (ancestor::, parent::) or functions like text() generally has no CSS equivalent at all, since CSS can't select by text content or move upward in the tree.

About this tool

The ToolNinja CSS Selector ↔ XPath Converter translates between the two selector languages for the substantial common ground they share: tag names, #id and .class selectors, [attribute] selectors (including ^=, $=, *=, and ~= operators), the descendant/child/adjacent-sibling/general-sibling combinators, and :first-child/:last-child/:nth-child(n). This common ground covers the overwhelming majority of real-world selectors, but the two languages aren't equivalent — XPath can select by text content, walk upward to parents and ancestors, and use arbitrary boolean logic in predicates, none of which CSS can express at all. Converting CSS → XPath always succeeds for a supported selector, since XPath is strictly more expressive. Converting XPath → CSS only works for XPath this converter's own output shape matches — arbitrary hand-written XPath using other axes or functions has no CSS equivalent and the tool says so clearly rather than guessing. Everything runs 100% in your browser — no selector or document content is ever sent anywhere, since there's no document involved at all, just the text conversion itself.

When to use it

  • →Converting a CSS selector into XPath for a tool or framework that only accepts XPath locators
  • →Converting an XPath locator back to CSS for Selenium/Playwright tests that prefer CSS selectors for performance
  • →Learning how a given CSS pattern (an attribute selector, a combinator) expresses in XPath syntax
  • →Quickly checking whether a selector you're writing has a direct equivalent in the other language

Tips

  • ◆CSS → XPath always succeeds for supported syntax, since XPath can express everything in this tool's CSS subset and more.
  • ◆XPath → CSS fails loudly with a specific reason when it hits something CSS can't express (an axis like ancestor::, a function like text()) — that's expected, not a bug, since CSS genuinely has no equivalent for those.
  • ◆Class selectors convert to a verbose-looking but standard XPath pattern (contains(concat(' ', normalize-space(@class), ' '), ' x ')) — that's the correct, robust way to match one class among several on an element in XPath 1.0, not an error.

Frequently asked questions

Why does my simple .foo class selector turn into such a long XPath expression?

XPath 1.0 has no direct 'has this class' primitive — @class='foo' would only match if foo were the element's only class. The contains(concat(' ', normalize-space(@class), ' '), ' foo ') pattern pads the class attribute with spaces and searches for ' foo ' specifically, which correctly matches regardless of how many other classes are present or in what order. It looks verbose, but it's the standard, correct way to do this in XPath 1.0.

Why can't every XPath expression convert back to CSS?

XPath is strictly more powerful than CSS selectors — it can select by text content, navigate upward to parent/ancestor nodes, use sibling-preceding axes, and apply arbitrary function logic in predicates. None of that has any CSS equivalent, because CSS selectors are fundamentally restricted to describing a downward path through the tree with simple attribute/class/structural conditions.

Does this handle :not(), :hover, or other dynamic pseudo-classes?

No — :not() would require negation logic this converter doesn't implement, and :hover, :focus, and similar dynamic pseudo-classes describe interaction state that has no meaning in a static document query language like XPath at all. These are intentionally out of scope.

Related tools

🥷 ToolNinja