What is the CSS :has() Selector?
The CSS :has() pseudo-class is a functional selector that takes a relative selector list as its argument. It represents an element if any of the selectors passed as an argument match at least one element when anchored against this element.
In simpler terms: :has() lets you style an element based on its children or previous siblings--something that wasn't possible in CSS for decades. This opens entirely new possibilities for responsive layouts, state management, and conditional styling without JavaScript.
Historically, CSS selectors have worked in a "top-down" fashion. You could select a child based on its parent, but not a parent based on its child. The :has() pseudo-class flips this paradigm, working in a "bottom-up" fashion that enables entirely new styling patterns. This shift is particularly valuable for modern front-end development projects that prioritize performance and clean code.
Browser Support
The :has() selector reached Baseline status in December 2023, meaning it works reliably across all major browsers:
- Safari 15.4+ (March 2022)
- Chrome/Edge 105+ (August 2022)
- Firefox 121+ (December 2023)
Current global browser support stands at approximately 92% according to Can I Use, making it safe to use in production with progressive enhancement for edge cases.
Syntax and Basic Usage
The syntax follows the pattern element:has(selector). Inside the parentheses, you can use any valid CSS selector, including combinators for descendants, children, and siblings.
Basic Examples
/* Select a card that contains an image */
.card:has(img) {
border: 2px solid #3b82f6;
}
/* Select a section with a featured article */
section:has(.featured) {
background-color: #f0f9ff;
}
/* Select a list item that has a link */
li:has(a) {
padding: 0.5rem;
}
/* Select a form with required fields */
form:has([required]) {
border: 1px solid #e5e7eb;
}
The flexibility of the selector inside :has() makes it incredibly powerful for a wide range of use cases, from simple content detection to complex conditional styling. Combined with our responsive design services, this enables sophisticated layouts that adapt to content without JavaScript.
Key Use Cases for :has()
1. Parent Element Selection
Parent selection was long considered the "holy grail" of CSS selectors. The :has() pseudo-class finally delivers this capability, allowing you to style a parent element based on its children.
Practical applications:
- Highlighting cards that contain images
- Adding special spacing to containers with specific content types
- Styling form sections that contain errors
- Adding visual indicators to lists with certain item types
/* Add border to cards containing images */
.card:has(img) {
border: 2px solid #3b82f6;
}
/* Style article cards that have a video */
.article-card:has(video) {
background: linear-gradient(to bottom, #f0f9ff, #fff);
}
/* Highlight form groups with errors */
.form-group:has(.error) label {
color: #dc2626;
}
2. Previous Sibling Selection
Before :has(), CSS could only select elements that came after other elements. Using sibling combinators inside :has() enables selecting elements based on what precedes them.
/* Style a heading that has a paragraph immediately following it */
h1:has(+ p) {
margin-bottom: 0;
}
/* Style list items that follow a special item */
li.special:has(~ li) {
border-top: 1px solid #e5e7eb;
}
3. State-Based Styling Without JavaScript
One of the most powerful applications is creating state-based styles without JavaScript. Combine :has() with form pseudo-classes for sophisticated conditional styling, as demonstrated by industry experts.
/* Highlight a field group when any field has focus */
.field-group:has(:focus-within) {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Style a card based on checkbox state */
.pricing-card:has(input:checked) {
border-color: #3b82f6;
transform: scale(1.02);
}
This capability reduces the need for JavaScript event handlers and class toggling, resulting in cleaner codebases and improved performance--key benefits for any modern web application.
Performance Considerations
While :has() is powerful, it does have performance implications that developers should understand. The :has() pseudo-class creates a "retroactive" style application--when the matched element changes, the browser must re-evaluate all ancestors and preceding siblings.
Best Practices for Performance
-
Keep selectors simple: More complex selectors inside :has() require more processing. Use simple element or class selectors when possible, as recommended by performance experts.
-
Avoid deeply nested selectors: The deeper the selector chain inside :has(), the more expensive the style calculation becomes.
-
Use specific selectors first: If combining :has() with other selectors, place the most specific conditions outside the :has() call when possible.
-
Consider the scope: Applying :has() at the component level rather than the document level reduces performance impact.
/* Good - simple selector */
.card:has(img) { }
/* Avoid when possible - complex selector */
.card:has(.content .image-container img.special) { }
/* Better alternative if possible */
.card:has(.special-image) img { }
When to Use :has() vs JavaScript
Use :has() when:
- Styling depends purely on DOM structure
- State is tied to form validation
- Visual feedback responds to user focus
- Layout needs adjustment based on content type
Use JavaScript when:
- Styles depend on external data
- Business logic drives the decision
- Animations need precise timing control
- State must persist across sessions
For custom web development projects, :has() offers an elegant solution when styling decisions can be made purely through CSS selectors.
Best Practices
Progressive Enhancement
Since browser support is excellent but not universal, use @supports for graceful degradation. Many :has() use cases are "nice-to-have" enhancements that don't break functionality when absent.
/* Base styles for all browsers */
.card {
padding: 1rem;
}
/* Enhanced styles for supporting browsers */
@supports selector(:has(*)) {
.card:has(img) {
padding: 1.5rem;
}
}
Semantic HTML Still Matters
The :has() pseudo-class should complement, not replace, semantic HTML structure. Use :has() to enhance presentation based on meaningful content relationships, not to compensate for poor markup. This principle aligns with our approach to clean, maintainable code for long-term project success.
Combine with Modern CSS Features
The :has() selector works beautifully with other modern CSS features like container queries, custom properties, and logical properties:
@container (min-width: 400px) {
.card:has(img) {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
}
}
When used alongside responsive design frameworks and CSS custom properties, :has() enables sophisticated adaptive designs that respond to both content and viewport conditions.
Common Patterns and Examples
Form Validation Feedback
/* Highlight the entire form group when a field is invalid */
.form-group:has(input:invalid:not(:focus-without)) {
border-left: 3px solid #dc2626;
}
/* Show success state when all required fields are filled */
.form-section:has(input:required:valid) {
background-color: #f0fdf4;
}
Interactive Cards and Lists
/* Add hover effect to card when it contains a link */
.card:has(a:hover) {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Highlight items that have been favorited */
.list-item:has(.favorite-btn[data-active="true"]) {
background-color: #fef3c7;
}
Navigation and Menu Styling
/* Style nav items that have dropdowns */
.nav-item:has(.dropdown) > a::after {
content: " ▼";
font-size: 0.7em;
}
/* Highlight the current section in a sidebar */
.sidebar-link:has(~ .active) {
background-color: #f3f4f6;
}
These patterns demonstrate how :has() simplifies common UI requirements that previously required JavaScript event handling, reducing bundle size and improving maintainability for single-page applications and beyond.
Key benefits for modern web development
No JavaScript Required
Create sophisticated state-based styling without writing a single line of JavaScript
Parent Selection
Finally style parent elements based on their children--a long-requested CSS feature
Previous Sibling Selection
Style elements based on what precedes them, enabling entirely new layout patterns
Excellent Browser Support
92%+ global browser support with Baseline status since December 2023
Performance Benefits
Leverage the browser's native styling engine for faster, more efficient updates
Progressive Enhancement
Use @supports for graceful degradation on older browsers
Frequently Asked Questions
Sources
- MDN Web Docs - :has() Selector - Official CSS specification reference
- Josh W. Comeau - The Undeniable Utility Of CSS :has - Comprehensive practical guide with real-world examples
- LogRocket - The different ways to use CSS :has(), with examples - Multiple use cases with code examples
- Can I Use - CSS :has - Browser support data and compatibility information