Logical Combinators

These pseudo-classes take a selector list. :is(), :where() and :not() are Baseline widely available since July 2023, :has() since June 2026.

Logical combinators
Selector Matches Specificity
:is(h2, h3) a <a> in <h2> or <h3> Heaviest argument: 0-0-2
:where(ul, ol) li The same, weightless Argument adds 0: 0-0-1
li:not(.done, #x) <li> matching neither Heaviest argument: 1-0-1
.card:has(> img) .card with an <img> child Heaviest argument: 0-1-1

:is() and :where() are forgiving: an invalid argument is dropped instead of voiding the rule; :has() is not. :has() takes a relative selector, so h2:has(+ p) selects an <h2> followed by a paragraph: parent and previous-sibling selection at last. Avoid broad anchors such as body:has(.x) on fast-changing pages, since any change inside can restyle everything.

:has() styles parents from what they containHTMLLive
<!doctype html>
<style>
  body { font: 15px system-ui; }  p { margin: 0; }
  .card { border: 1px solid #b0bec5; border-left-width: 6px; padding: 4px 8px; margin: 4px 0; }
  .card:has(img) { display: flex; gap: 8px; border-left-color: #1565c0; }
  .card:not(:has(img)) { font-style: italic; }  form:has(:invalid) button { opacity: 0.4; }
</style>
<div class="card"><img width="28" height="28" alt="" style="background:#90caf9">
  <p>Card with an image: flex row and blue edge via :has(img)</p></div>
<div class="card"><p>Text-only card: italic via :not(:has(img))</p></div>
<form><input required placeholder="Required field"> <button>Send (dimmed)</button></form>
Browser output of Listing 4.20
Browser output of 20