CSS :has() Selector Guide
The revolutionary "parent selector" that CSS developers waited 20 years for.
Click any code block to copy to clipboard.
What is :has()?
The :has() selector is a CSS pseudo-class that selects an element if any of the selectors passed as parameters match at least one element. It is often called the "parent selector" because it allows you to style a parent based on its children.
Before :has(), there was no way to select a parent element based on what children it contained. You had to use JavaScript. Now, with :has(), you can write pure CSS that responds to DOM structure.
Browser Support (2024+)
Supported in all major browsers since late 2023. Safe to use in production.
Before :has()
Required JavaScript to style parents:
// Check if parent has image
if (card.querySelector('img')) {
card.classList.add('has-image');
}With :has()
Pure CSS, no JavaScript needed:
/* Style card that has an image */
.card:has(img) {
padding: 0;
}Basic Syntax
elementThe element to select (parent)
:has()The functional pseudo-class
selectorWhat to look for (child/descendant)
Select parent with specific child
Style a parent element based on what children it contains
/* Style figure that contains a figcaption */
figure:has(figcaption) {
border: 2px solid #e5e7eb;
padding: 1rem;
border-radius: 8px;
}
/* Style article that contains an image */
article:has(img) {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1rem;
}Direct child selector
Use > to select only direct children, not all descendants
/* Link that directly contains an image */
a:has(> img) {
display: block;
border-radius: 8px;
overflow: hidden;
}
/* List item with direct button child */
li:has(> button) {
display: flex;
justify-content: space-between;
align-items: center;
}Multiple conditions
Combine multiple :has() selectors for complex conditions
/* Card with both image AND badge */
.card:has(img):has(.badge) {
position: relative;
}
/* Form with both email and password fields */
form:has(input[type="email"]):has(input[type="password"]) {
border: 2px solid #6366f1;
}Form Validation Patterns
One of the most powerful use cases for :has() is form validation styling without any JavaScript.
Form validation styling
Change form appearance based on input validity
/* Form with any invalid input */
form:has(input:invalid) {
border-color: #ef4444;
background: #fef2f2;
}
/* Form with all valid inputs */
form:has(input:valid):not(:has(input:invalid)) {
border-color: #22c55e;
background: #f0fdf4;
}Required field label styling
Style labels for required inputs without JavaScript
/* Label followed by required input */
label:has(+ input:required)::after {
content: " *";
color: #ef4444;
}
/* Fieldset containing required fields */
fieldset:has(input:required) {
border-left: 3px solid #ef4444;
}Focus-within alternative
Style parent when any child has focus
/* Input wrapper with focused input */
.input-wrapper:has(input:focus) {
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3);
border-color: #6366f1;
}
/* Show helper text when input is focused */
.field:has(input:focus) .helper-text {
display: block;
opacity: 1;
}Advanced Patterns
Combining with :not()
Select elements that do NOT contain certain children
/* Card without an image - add padding */
.card:not(:has(img)) {
padding: 2rem;
}
/* Empty state for lists without items */
ul:not(:has(li)) {
display: grid;
place-items: center;
min-height: 200px;
}
ul:not(:has(li))::before {
content: "No items found";
color: #9ca3af;
}Quantity queries
Change layout based on number of children
/* 2 columns when 4+ items */
.grid:has(> :nth-child(4)) {
grid-template-columns: repeat(2, 1fr);
}
/* 3 columns when 7+ items */
.grid:has(> :nth-child(7)) {
grid-template-columns: repeat(3, 1fr);
}
/* Stack first item when 6+ items */
.grid:has(> :nth-child(6)) > :first-child {
grid-column: span 2;
}Sibling-based styling
Style siblings based on state of other siblings
/* Dim other items when one is hovered */
.list:has(.item:hover) .item:not(:hover) {
opacity: 0.5;
transform: scale(0.98);
}
/* Highlight related items */
.container:has(.primary:hover) .secondary {
border-color: #6366f1;
background: #eef2ff;
}Previous sibling selector
Style elements before another element (impossible before :has())
/* Style all items before the active one */
/* This was impossible in CSS before :has()! */
li:has(~ li.active) {
opacity: 0.5;
}
/* Style label when its input has error */
label:has(+ input.error) {
color: #ef4444;
}Live Interactive Demos
Interact with these demos to see :has() in action:
Form Validation
Checkbox Label Styling
Label styling changes when checkbox is checked
Card with/without Image
Card with Image
No padding on container
Card without Image
Has left border accent
Navigation Active State
Nav background changes when an item is active
Quantity-based Layout
Grid changes columns based on item count (1-3: 1col, 4-6: 2col, 7+: 3col)
Sibling Highlight Effect
- First item
- Second item
- Third item
- Fourth item
Hover any item to dim the others
Real-World Practical Examples
Responsive navigation
Change nav styling based on active state
/* Gradient background when item is active */
nav:has(.nav-item.active) {
background: linear-gradient(to right, #6366f1, #8b5cf6);
}
/* White text for non-active items when one is active */
nav:has(.nav-item.active) .nav-item:not(.active) {
color: white;
}Interactive checkbox labels
Style entire label area based on checkbox state
/* Selected option styling */
.option:has(input:checked) {
background: #dbeafe;
border-color: #3b82f6;
}
.option:has(input:checked) .option-title {
color: #1d4ed8;
font-weight: 600;
}
/* Disabled option styling */
.option:has(input:disabled) {
opacity: 0.5;
cursor: not-allowed;
}Modal backdrop control
Control backdrop when modal is open
/* Show backdrop when modal is present */
body:has(.modal.open) {
overflow: hidden;
}
body:has(.modal.open)::before {
content: "";
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 40;
}Dark mode toggle
Apply dark mode based on toggle state
/* When dark mode toggle is checked */
html:has(#dark-mode:checked) {
color-scheme: dark;
}
html:has(#dark-mode:checked) body {
background: #1f2937;
color: #f9fafb;
}Performance Considerations
When :has() Can Be Slow
The :has() selector requires the browser to check descendants, which can be expensive in certain scenarios:
- !Broad selectors:
*:has(img)checks every element - !Deep nesting:
div:has(div div div img)searches deeply - !Frequent DOM changes: Adding/removing elements triggers re-evaluation
Best Practices
Do
- Use specific selectors:
.card:has(img) - Prefer direct child:
:has(> img) - Combine with classes for clarity
- Test on low-end devices
Avoid
- Universal selector:
*:has(...) - Complex nested selectors
- Overusing on frequently updated DOM
- Relying on it for critical layout
Performance Comparison
/* Slower - checks all elements */
*:has(> .active) { ... }
/* Faster - scoped to .nav elements */
.nav:has(> .active) { ... }
/* Fastest - direct child only */
.nav:has(> .nav-item.active) { ... }Quick Reference
| Pattern | Description | Example |
|---|---|---|
A:has(B) | A that contains B anywhere | .card:has(img) |
A:has(> B) | A with direct child B | ul:has(> li.active) |
A:has(+ B) | A immediately followed by B | label:has(+ input:required) |
A:has(~ B) | A followed by B (any sibling) | li:has(~ li.active) |
A:not(:has(B)) | A that does NOT contain B | .card:not(:has(img)) |
A:has(B):has(C) | A that contains both B and C | form:has(input):has(button) |
A:has(B, C) | A that contains B or C | .container:has(img, video) |
Explore more CSS tools
CSS Grid Generator
Create grid layouts visually
CSS Flexbox Generator
Create flex layouts visually
CSS Gradient Generator
Create custom gradients
CSS Text Gradient
Gradient text effects
CSS Gradients Collection
275+ ready-to-use gradients
CSS Box Shadow Generator
Design multi-layer shadows
CSS Selection Generator
Style text highlight colors
CSS Scrollbar Generator
Style custom scrollbars
CSS Blend Mode Generator
Mix colors with blend modes
CSS Cursor Generator
Preview all cursor styles
CSS Loader Generator
Create loading animations
CSS Button Generator
Design buttons with hover effects
CSS Glow Generator
Create neon glow effects
Fluid Typography
Scale text across screen sizes
