Master Code
On The Go

Learn. Practice. Build.

Home › Code Snippets › CSS Snippets › :has() Selector

CSS Snippet

CSS :has() Selector

The parent selector we waited 20 years for. Style a parent based on its children — with no JavaScript.

Last updated: October 2026

Quick answer: :has() matches an element if it contains a descendant that matches a selector. For example, form:has(input:invalid) styles the form when any input is invalid. It replaces a lot of JavaScript validation, styling, and state management code.

🎬 Interactive Live Demo

Interact with the form, check/uncheck the checkbox, and toggle the tabs. The parent styles update automatically using :has() — no JavaScript.

💡 Every state change above is driven purely by :has() in CSS. No JavaScript logic for styling.

1

The Basic Syntax

/* Match any element that contains an img */
.card:has(img) {
    /* ... */
}

/* Match a form that contains an invalid input */
form:has(input:invalid) {
    border-color: red;
}

/* Match an article with an h2 immediately followed by a p */
article:has(h2 + p) {
    /* ... */
}

The pattern is: parent:has(descendant-selector). If the parent contains a matching descendant, the rule applies to the parent.

2

Form Validation Without JavaScript

The classic use case — style the form based on input state:

/* Red border when any input is invalid (and has been typed in) */
.field:has(input:invalid:not(:placeholder-shown)) {
    border-color: #ef4444;
}

/* Green border when all inputs are valid */
.field:has(input:valid):not(:has(input:invalid)) {
    border-color: #10b981;
}

/* Highlight the field when its input is focused */
.field:has(input:focus) {
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}

This is exactly what our live demo does. In most cases, you can replace an entire validation script with three lines of CSS.

3

Conditional Card Layouts

Style cards differently based on their content:

/* Cards with an image get a top-padding of 0 (for edge-to-edge image) */
.card:has(img) {
    padding-top: 0;
}

/* Cards without an image get a centered layout */
.card:not(:has(img)) {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 200px;
}

/* Cards with a "new" badge get a highlighted border */
.card:has(.badge-new) {
    border: 2px solid #10b981;
}
4

Navigation and Layout Magic

Style parent containers based on active states:

/* Highlight the whole nav section if any link is active */
.nav-section:has(.nav-link.active) {
    background: #f59e0b;
}

/* Add a sidebar-adjacent margin only if a sidebar exists */
.main:has(.sidebar) {
    margin-left: 250px;
}

/* Style the whole layout when the user is logged in */
body:has(.user-avatar) .header {
    background: #1e40af;
}
5

Combining with :not() and Other Selectors

/* Items that have a subtitle but no description */
.item:has(h2):not(:has(p)) {
    /* ... */
}

/* Grid with more than 4 items gets a different layout */
.grid:has(> :nth-child(5)) {
    grid-template-columns: repeat(4, 1fr);
}

/* Match any element that has a hovered child */
.menu-item:has(:hover) {
    background: rgba(255, 255, 255, 0.1);
}

The :has(:nth-child(5)) pattern is powerful — it lets you change a layout based on how many children there are, all in CSS.

🎯 :has() Use Cases Cheatsheet

Goal Selector
Style form with invalid input form:has(input:invalid)
Style field with focused input .field:has(input:focus)
Highlight card with an image .card:has(img)
Style parent with active child .menu:has(.active)
Layout with many items .grid:has(> :nth-child(10))
Exclude items with a specific child .item:not(:has(.badge))

⚠️ Common Mistakes

❓ Frequently Asked Questions

What does the CSS :has() selector do?

The :has() selector matches an element if it contains a specific descendant or matches a specific condition. It is the long-awaited 'parent selector' that lets you style a parent based on its children.

Is :has() supported in all browsers?

Yes. As of 2024, :has() is supported in all modern browsers: Chrome, Safari, Firefox, and Edge. It has been supported in Safari since 2022 and in Firefox and Chrome since 2023.

Can I use :has() to style a form based on input state?

Yes. form:has(input:invalid) styles the form when an input is invalid. form:has(input:focus) styles it when any input is focused. This replaces a lot of JavaScript validation code.

What is the difference between :has() and :not()?

The :not() selector excludes elements that match a condition. The :has() selector includes elements that contain a descendant matching a condition. They serve opposite purposes and can be combined.

Does :has() work with nested elements?

Yes. :has() supports complex selectors inside it, including combinators. For example, article:has(h2 + p) matches articles where an h2 is immediately followed by a p.

🎯 What's Next?

← All CSS Snippets