:has() — The Parent Selector
:has() is the most powerful CSS selector ever added. It selects elements based on their CHILDREN or SIBLINGS — something impossible before. Style a card differently if it contains an image, or style a form based on whether an input is valid.
:has() Use Cases
- div:has(img) — Select divs that CONTAIN an image
- form:has(:invalid) — Style form when any input is invalid
- .card:has(.badge) — Style cards that have a badge element
- h2:has(+ p) — Select h2 that is immediately followed by a p (sibling combinator)
- :has() as parent selector — .card:has(:hover) styles the card when ANY child is hovered
- Conditional layouts — .grid:has(> :nth-child(4)) only applies when grid has 4+ items
- Browser support — Supported in all modern browsers since 2023. Safe to use in production
:has() Code
/* Card has image → horizontal layout */
.card:has(img) {
display: flex;
gap: 16px;
}
.card:not(:has(img)) {
/* Card without image → vertical layout */
}
/* Form validation: style form based on input states */
form:has(:invalid) .submit-btn {
opacity: 0.5;
pointer-events: none;
}
/* Highlight parent on child hover */
.card:has(.title:hover) {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
/* Conditional grid columns */
.grid:has(> :nth-child(4)) {
grid-template-columns: repeat(4, 1fr); /* 4+ items: 4 columns */
}
.grid:not(:has(> :nth-child(4))) {
grid-template-columns: repeat(3, 1fr); /* Fewer: 3 columns */
}Tip
:has() can check siblings too: h2:has(+ p) selects any h2 followed by a p. This enables context-aware styles — headings with descriptions can have different spacing than standalone headings. Previously impossible without JS.
Higher specificity wins when multiple rules target the same element
Common Mistake
Using :has() in universal selectors like *:has(img). :has() checks child/sibling elements, which is expensive. Always scope it to specific blocks like .card:has(img) for better performance on large DOMs.
Practice Task
Use :has() for adaptive cards: (1) .card:has(img) gets flex horizontal layout, .card:not(:has(img)) stays vertical, (2) form:has(:invalid) .submit-btn gets opacity: 0.5, (3) .card:has(.title:hover) highlights the whole card.
Quick Quiz
Key Takeaways
- :has() is the most powerful CSS selector ever added.
- div:has(img) — Select divs that CONTAIN an image
- form:has(:invalid) — Style form when any input is invalid
- .card:has(.badge) — Style cards that have a badge element