The :has() pseudo-class matches an element when a relative selector inside the parentheses finds at least one match. Use it to style a parent from its children, or a previous sibling from what comes next.
Examples: .card:has(img) styles a card that contains an image; h1:has(+ p) styles an h1 only when the next sibling is a p.
Baseline Widely available.
Widely available across modern browsers since December 2023. Prefer progressive enhancement if you must support older engines.
Warning — limits inside :has().
You cannot nest :has() inside :has(), and pseudo-elements are not valid arguments. Specificity follows the most specific selector in the argument list (like :not()).
Syntax
CSS Syntax
syntax.css
:has(<relative-selector-list>)
Relative selectors may start with a combinator such as >, +, or ~.
Arguments
Selector Arguments
:has() takes a relative selector list — not property values. Common patterns:
:has(video, audio) is OR. For AND, chain :has(video):has(audio).
How It Works
Pass a list. Commas inside :has() mean any match is enough.
Style the container..media gets the outline when video or audio exists.
Skip empty cases. A media box with only text does not match.
Need both? Write :has(video):has(audio) instead.
Edge Cases & Pitfalls
Most issues are nesting limits, broad anchors, or confusing OR vs AND.
nest
No :has() inside :has()
Nested :has() is invalid. Rewrite the condition with combinators or chained :has() on the same anchor.
pseudo
No pseudo-elements inside
::before, ::after, and similar are not valid arguments for :has().
perf
Avoid broad anchors
Prefer a specific container over body:has(...), :root:has(...), or *:has(...) on large pages.
logic
Comma is OR; chaining is AND
:has(a, b) matches either. :has(a):has(b) requires both.
support
Unsupported browsers drop the rule
If :has() is not supported, the whole selector fails unless it sits in a forgiving list such as :is() / :where().
Compatibility
Browser Support
:has() is a browser CSS pseudo-class. Node.js, Deno, and Bun do not evaluate it.
✓ Baseline · Widely available
:has()
Chrome 105+, Edge 105+, Safari 15.4+, Firefox 121+, Opera 91+, Samsung Internet 20+ (widely available since Dec 2023).
95%Modern browsers
Google Chrome105+
Full support
Mozilla Firefox121+
Full support
Apple Safari15.4+
Full support
Microsoft Edge105+
Full support
Opera91+
Full support
Samsung Internet20+
Full support
:has()Widely supported
Bottom line: Safe for modern sites. Use progressive enhancement for older browsers. Not a server runtime API.
Remember
Key Takeaways
Role: style an element based on what it contains or what follows it.
Form:anchor:has(relative-selector)
Logic: comma = OR; chained :has() = AND.
Tip: keep anchors specific and conditions tight with > or +.
One line:A:has(B) styles A when relative selector B matches from A.
Frequently Asked Questions
It matches an element when a relative selector inside the parentheses finds at least one match. Use it to style a parent from its children, or a previous sibling from what follows.
Yes in practice. section:has(.featured) styles the section when it contains .featured. Use > for a direct child: section:has(> .featured).
Yes. h1:has(+ p) styles an h1 only when the next sibling is a p. That looks forward, then styles the earlier element.
:has(a, b) is OR — match if a or b is found. :has(a):has(b) is AND — both must match.
No. :has() cannot contain another :has(), and pseudo-elements are not valid inside :has().
No. It is a browser CSS pseudo-class. Node.js, Deno, and Bun do not apply it.