ShaharAmir
← Back to Blog
CSS2 min read

CSS :has() Changes Everything

The parent selector we've wanted for 20 years

S
Shahar Amir

CSS :has() Changes Everything

For 20 years, we couldn't style parents based on children. Now we can.

The Problem (Before :has)

Style a card differently if it has an image? Impossible in CSS.

javascript
1234
// Had to use JavaScript
if (card.querySelector('img')) {
card.classList.add('has-image');
}

The Solution: :has()

css
12345678910
/* Style card when it contains an image */
.card:has(img) {
display: grid;
grid-template-columns: 200px 1fr;
}
/* No image? Different layout */
.card:not(:has(img)) {
padding: 1rem;
}

Basic Syntax

css
12345678
/* Parent that has a child */
.parent:has(.child) { }
/* Parent that has a direct child */
.parent:has(> .child) { }
/* Parent that has a checked input */
form:has(input:checked) { }

Practical Examples

Form Validation

css
123456789
/* Highlight field group with error */
.field:has(input:invalid) {
border-left: 3px solid red;
}
/* Show success when valid */
.field:has(input:valid:not(:placeholder-shown)) {
border-left: 3px solid green;
}

Image Cards

css
12345678910
/* Card with image: side-by-side layout */
.card:has(img) {
display: flex;
gap: 1rem;
}
.card:has(img) img {
width: 40%;
object-fit: cover;
}

Navigation State

css
1234
/* Highlight nav item with active link */
nav li:has(a[aria-current="page"]) {
background: var(--accent);
}

Dark Mode Toggle

css
12345
/* If toggle is checked, dark mode */
body:has(#dark-mode:checked) {
background: #1a1a1a;
color: white;
}

Required Field Labels

css
12345
/* Add asterisk to labels of required fields */
label:has(+ input:required)::after {
content: " *";
color: red;
}

Advanced: Quantity Queries

css
12345678910
/* Grid when 3+ items */
.container:has(:nth-child(3)) {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
/* Single column when less than 3 */
.container:not(:has(:nth-child(3))) {
max-width: 500px;
}

Combining with Other Selectors

css
12345678
/* Parent that has both */
.card:has(img):has(h2) { }
/* Parent that has either */
.card:has(img, video) { }
/* Sibling of element that has */
.card:has(img) + .card { }

Browser Support

All modern browsers. Use with progressive enhancement:

css
1234567891011
/* Fallback */
.card {
display: block;
}
/* Enhanced experience */
@supports selector(:has(*)) {
.card:has(img) {
display: grid;
}
}

The Power

:has() is essentially a parent selector AND more. You can select any element based on what's inside it or around it. CSS finally caught up to what we needed.

#selectors#modern-css#parent

Stay Updated 📬

Get the latest tips and tutorials delivered to your inbox. No spam, unsubscribe anytime.