---
title: ":has() for parent and page state"
description: "Use it wherever a script adds a class to a parent because of what it contains or what state a child is in."
canonical: https://good-css.com/has-for-parent-and-page-state.md
last-updated: 2026-11-05
---

# `:has()` for parent and page state

Use it wherever a script adds a class to a parent because of what it contains or what state a child is in.

```css
article:has(img) { grid-column: span 2; }
h2:has(+ p) { margin-block-end: 0; }
form:has(:focus-visible) { background: var(--surface-raised); }

html { scrollbar-gutter: stable; }
html:has(dialog:modal) { overflow: hidden; }
```

Why it works:

- `:has()` matches an element by its descendants or following siblings, and it accepts state pseudo-classes. Content and form state can style any ancestor.
- On the root it works as a page-wide listener. The last rule locks page scroll while a modal dialog is open and lifts the lock when it closes, with nothing to clean up.

Rules:

- `:modal` matches `showModal()` only, so a non-modal dialog does not lock the page.
- Keep `scrollbar-gutter: stable` with the scroll lock. Without it the page shifts sideways when the scrollbar disappears. [The reset](https://good-css.com/the-reset.md) sets it.
- `:has()` cannot be nested inside `:has()`.

Support: Chrome 105, Firefox 121, Safari 15.4.

- Borrowed from: Jen Simmons, [Using :has() as a CSS Parent Selector and much more](https://webkit.org/blog/13096/css-has-pseudo-class/), 2022-08-18 · Josh W. Comeau, [The Undeniable Utility Of CSS :has](https://www.joshwcomeau.com/css/has/), 2024-09-09 · Chris Coyier, [Scroll-Locked Dialogs](https://blog.master.dev/scroll-locked-dialogs/), 2024-02-19
