More Real World Uses For CSS :has()

Discover production-ready applications of the CSS :has() pseudo-class for styling based on descendants, siblings, and states without JavaScript dependencies.

What Makes :has() Different

The CSS :has() pseudo-class represents a fundamental shift in how we approach styling. Dubbed the "parent selector" CSS never had, :has() enables styling based on descendants, siblings, and states--capabilities that previously required JavaScript workarounds or awkward HTML structure modifications. With 92% global browser support as of late 2024, :has() has crossed the threshold from experimental feature to production-ready tool.

This guide explores practical, production-ready applications of :has() that solve real-world frontend challenges, from form validation feedback to accessible focus management and intelligent layout adjustments. By leveraging these techniques in your web development projects, you can reduce JavaScript dependencies while creating more maintainable stylesheets.

Browser Support Landscape

Browser support for :has() has reached significant milestones across all major engines:

  • Safari 15.4 (March 2022)
  • Chrome and Edge 105 (August 2022)
  • Firefox 121 (December 2023)

This cross-browser availability, combined with the feature's progressive enhancement nature, makes :has() safe to use in production today. For browsers that don't support :has(), the CSS is simply ignored--no errors, no broken layouts. This compatibility makes :has() an excellent choice for modern CSS development where progressive enhancement is a core principle.

Key :has() Capabilities

Transform your CSS with these powerful patterns

Parent-Based Styling

Style containers based on their children without JavaScript, eliminating the need for class toggling patterns.

Sibling Selectors

Style elements based on what follows them using :has() with adjacent and general sibling combinators.

State Detection

Detect focus, hover, and checked states on children to style parent containers accordingly.

Form Validation

Create visual validation feedback using CSS alone, leveraging :invalid and :placeholder-shown selectors.

Table Enhancements

Implement intelligent striping and conditional formatting based on row content and cell values.

Accessibility Improvements

Improve keyboard navigation with focus state propagation to parent containers.

Styling Based on Descendant Presence

Container-Level State Without JavaScript

One of the most powerful applications of :has() eliminates the common pattern of toggling parent class names via JavaScript. Consider a card component that should display differently when it contains a featured item. Previously, this required JavaScript to detect the featured item and add a class to the parent. With :has(), the relationship is declarative in CSS alone.

/* Style the card when it contains a featured tag */
.card:has(.featured-tag) {
 border-color: #ff6b35;
 box-shadow: 0 4px 12px rgba(255, 107, 53, 0.25);
}

/* Highlight cards with images */
.article-card:has(img) {
 padding-left: 0;
}

This approach eliminates JavaScript dependencies for visual state management, reducing bundle size and improving performance. The styling logic lives where it belongs--in the stylesheet--closely coupled with the visual rules it affects.

When building modern web applications, this pattern proves particularly valuable for component libraries where visual states should be determined by content rather than imperative JavaScript class manipulation. Related techniques like CSS selectors can further enhance your styling capabilities.

Empty State Detection

Detecting and styling empty containers is another common requirement. While the :empty pseudo-class exists, it only matches elements with no children at all--not whitespace, not comments. The :has() pseudo-class provides more flexible empty-like detection patterns.

/* Style sections with no content sections */
.section:has(> .content-block:empty) {
 display: none;
}

/* Highlight form sections missing required fields */
.form-section:has(.required:not(:filled)) {
 border-left: 3px solid #e74c3c;
 padding-left: 1rem;
}

These patterns work seamlessly with component libraries where conditional rendering creates empty states that need styling attention. Combining :has() with CSS Modules creates maintainable styling systems for complex applications.

Sibling-Based Styling Patterns

Preceding Sibling Detection

The :has() pseudo-class enables styling based on following siblings, a capability that transforms how we approach layout and content relationships. Traditional sibling selectors only affect elements that come after the reference element. With :has(), we can style elements based on what follows them.

/* Style images that are followed by captions */
figure:has(+ figcaption) {
 margin-bottom: 0.5rem;
}

/* Highlight headings that precede certain content types */
h2:has(+ .warning-message) {
 color: #e74c3c;
}

This capability proves particularly valuable for responsive design, where content relationships should drive layout decisions rather than arbitrary breakpoints. When combined with modern CSS Grid layouts, :has() creates adaptive interfaces that respond to content context naturally.

Contextual Styling Based on Adjacent Content

Consider a documentation site where certain code blocks need special formatting when they follow conceptual explanations. Rather than adding classes manually or relying on fragile nth-child calculations, :has() establishes semantic relationships through content patterns.

/* Provide extra context for examples that follow explanations */
.explanation:has(+ .example) {
 margin-bottom: 0.5rem;
}

/* Style warning blocks that follow error descriptions */
.error-description:has(+ .warning) {
 background-color: #fff5f5;
}

For developers working with CSS security patterns, understanding sibling selectors helps create more robust styling systems that avoid common pitfalls.

State-Based Styling Without JavaScript

Focus State Propagation

Accessibility-focused development often requires styling parent containers when their interactive children receive focus. The :has() pseudo-class elegantly solves this challenge, enabling visual emphasis on containing elements during keyboard navigation.

/* Highlight the card when any interactive element inside has focus */
.card:has(*:focus) {
 outline: 3px solid #4a90d9;
 outline-offset: 2px;
}

/* Dim cards that don't have focus */
.grid-item:not(:has(*:focus)) {
 opacity: 0.6;
}

/* Show additional controls when input inside has focus */
.input-group:has(input:focus) .helper-text {
 display: block;
}

This pattern dramatically improves keyboard navigation experiences, particularly in complex interfaces with multiple interactive elements. Users navigating via keyboard can easily identify which card or section contains their current focus.

For web accessibility compliance, this approach provides visual feedback that helps users understand their position within complex layouts without relying on screen readers alone. Explore more CSS techniques that enhance user experience through visual design.

Checked State Detection

Form elements with checked states (checkboxes, radio buttons, switches) can drive styling of their parent containers. This enables intuitive patterns like expanding sections when associated checkboxes are checked, without JavaScript event handlers.

/* Expand card when toggle is checked */
.toggle-card:has(input[type="checkbox"]:checked) {
 max-height: 500px;
 overflow: visible;
}

/* Change border color for selected options */
.option-card:has(input[type="radio"]:checked) {
 border-color: #3498db;
 background-color: #f0f9ff;
}

This approach transforms checkbox-based interfaces from simple binary toggles into full-featured interactive components driven entirely by CSS. When building custom form solutions, these patterns reduce the need for client-side state management while providing smooth user experiences. Similar principles apply when styling custom list counters for enhanced content presentation.

Form Validation Visual Feedback

Input State Detection

Modern form validation often relies on JavaScript to apply error states and feedback. The :has() pseudo-class enables visual validation states through CSS alone, leveraging HTML5 validation pseudo-classes like :invalid, :user-invalid, and :placeholder-shown.

/* Highlight labels for invalid inputs */
.input-group:has(input:invalid:not(:focus)) label {
 color: #e74c3c;
}

/* Show error message when input is invalid */
.form-field:has(input:invalid) .error-message {
 display: block;
}

/* Style container when email format is incorrect */
.email-field:has(input[type="email"]:not(:placeholder-shown):invalid) {
 border-color: #e74c3c;
}

These patterns provide immediate, consistent feedback to users without JavaScript dependencies. The browser's built-in validation API combined with :has() creates a powerful validation system entirely in CSS.

Conditional Form Layouts

Complex forms often need to rearrange themselves based on user selections. The :has() pseudo-class enables dynamic layout adjustments without JavaScript state management.

/* Show phone field only when "Call me" is selected */
.phone-field {
 display: none;
}
.contact-preference:has(#contact-call:checked) + .phone-field {
 display: block;
}

/* Expand shipping address when "Ship to different address" is checked */
.shipping-address {
 display: none;
}
.shipping-options:has(#ship-different:checked) ~ .shipping-address {
 display: block;
}

This approach is particularly valuable for progressive form enhancement, where base functionality works for all users while modern browsers receive enhanced interactive experiences. Discover additional CSS tricks that simplify complex styling challenges.

Table and Data Display Enhancements

Intelligent Row Striping

Traditional zebra striping uses nth-child selectors, which apply uniformly regardless of content. The :has() pseudo-class enables content-aware striping that adapts to filtered or hidden rows.

/* Alternate row styling based on visible rows */
tbody tr:has(+ tr:not([hidden])) {
 background-color: #f8f9fa;
}

/* Highlight rows containing specific content */
table.data-table tr:has(.status-error) {
 background-color: #fff5f5;
}

table.data-table tr:has(.status-success) {
 background-color: #f0fff4;
}

/* Style cells based on neighboring cell content */
td:has(+ .highlight) {
 font-weight: 600;
}

Column and Cell Styling

While CSS lacks a true column selector, :has() provides creative workarounds for column-aware styling when combined with specific table structures and selector patterns.

/* Highlight cells in rows where a specific column has certain content */
tr:has(td:nth-child(4):empty) .actions-column {
 visibility: hidden;
}

/* Style summary rows differently from data rows */
table tr:has(.row-total) {
 font-weight: 600;
 border-top: 2px solid #dee2e6;
}

These patterns are essential for admin dashboard development, where data-rich interfaces require intelligent visual organization that responds to content context. Learn how to get actual parent dimensions for responsive layouts that adapt to dynamic content.

Navigation and Menu Patterns

Mega Menu Enhancement

Complex navigation menus benefit from :has() for detecting active states and managing nested menu visibility. This creates more responsive, context-aware navigation experiences.

/* Show dropdown when hovering menu item with dropdown */
.nav-item:has(.dropdown-menu) .dropdown-menu {
 display: none;
}

.nav-item:has(.dropdown-menu):hover .dropdown-menu {
 display: block;
}

/* Highlight nav items linking to active page */
.nav-link:has(~ .current-page-indicator) {
 color: #3498db;
 font-weight: 600;
}

Mobile Menu States

Responsive navigation often requires JavaScript to toggle mobile menu visibility. With :has() and checkbox-based toggles, mobile menus can function without JavaScript.

/* Checkbox-based mobile menu toggle */
.menu-toggle {
 display: none;
}

@media (max-width: 768px) {
 .mobile-menu {
 display: none;
 }

 .menu-toggle:checked ~ .mobile-menu {
 display: block;
 }
}

For responsive web design, these techniques reduce JavaScript dependencies while providing smooth, accessible navigation experiences across all device sizes. Explore related solutions for hamburger menu implementations and native JavaScript routing patterns.

Progressive Enhancement and Performance

Feature Detection Strategy

The @supports at-rule enables graceful degradation for :has() features. Browsers that don't support :has() simply ignore the enhanced styles, falling back to baseline experiences.

/* Base styles for all browsers */
.card {
 padding: 1rem;
}

/* Enhanced styles for :has()-supporting browsers */
@supports selector(card:has(.featured)) {
 .card:has(.featured) {
 padding: 1.5rem;
 border-left: 4px solid #3498db;
 }
}

This approach ensures baseline functionality while providing enhanced experiences for modern browsers. The key is treating :has() features as improvements rather than requirements.

Performance Considerations

Browser rendering engines optimize :has() selectors, but certain patterns impact performance more than others. Simple descendant selectors within :has() perform well, while complex nested selectors or :has() chains can trigger style recalculation across larger portions of the document tree.

/* Performant: simple descendant check */
.card:has(.highlight) { /* efficient */ }

/* Less performant: complex nested selectors */
.container:has(.sidebar .widget .featured) { /* may impact rendering */ }

/* Best practice: keep selectors simple */
.card:has(.featured-tag) { /* optimal */ }

Modern browsers have significantly optimized :has() performance, making it suitable for most production use cases. However, avoiding overly complex selector chains remains good practice, particularly for frequently animated or interactive elements.

When building high-performance web applications, these patterns help balance rich interactivity with responsive rendering across all user devices. Check out how to use math.random in JavaScript for dynamic interactions that complement CSS-only solutions.

Conclusion

The CSS :has() pseudo-class represents a fundamental capability expansion for stylesheet authoring. By enabling styling based on descendants, siblings, and states, :has() eliminates countless JavaScript workarounds while improving performance and maintainability. With 92% browser support and progressive enhancement built into its design, :has() is ready for production use across modern web applications.

The patterns explored in this guide--from form validation feedback to accessibility improvements--demonstrate that :has() isn't merely a syntactic convenience but a genuine capability expansion that enables cleaner code, better performance, and more maintainable stylesheets. As browser implementations continue to mature and developer familiarity grows, :has() will likely become as fundamental to CSS as flexbox and grid.

Our team leverages these modern CSS capabilities when building custom web solutions, creating interfaces that are performant, accessible, and maintainable. By reducing JavaScript dependencies for visual state management, we deliver faster loading times and smoother user experiences across all browsers. Explore more advanced CSS selectors and styling techniques to further enhance your development toolkit.

Frequently Asked Questions

What browsers support CSS :has()?

CSS :has() is supported in Safari 15.4+, Chrome/Edge 105+, and Firefox 121+. This represents approximately 92% global browser support as of late 2024.

Does :has() work with all CSS selectors?

Yes, :has() accepts any valid CSS selector as its argument, including class selectors, attribute selectors, pseudo-classes, and combinators like descendant (space), child (>), and sibling (+, ~) selectors.

How does :has() impact performance?

Modern browsers have significantly optimized :has() performance. Simple descendant checks within :has() are efficient. However, overly complex nested selectors or multiple :has() chains can increase style recalculation time.

Can I use :has() for form validation?

Absolutely! :has() combined with :invalid, :valid, and :placeholder-shown pseudo-classes enables visual form validation feedback entirely in CSS without JavaScript.

What's the difference between :has() and :empty?

The :empty pseudo-class only matches elements with absolutely no children (not even whitespace). :has() provides more flexible detection, such as checking for elements with specific classes or attributes.

Ready to Modernize Your Web Development?

Our team builds high-performance websites using modern CSS techniques like :has() to create seamless, responsive user experiences.

Sources

  1. MDN Web Docs: :has() - Official CSS specification documentation
  2. Can I Use: CSS :has() - Browser support statistics
  3. CSS-Tricks: More Real-World Uses for :has() - Developer community examples
  4. Josh W. Comeau: The Undeniable Utility Of CSS :has - Production implementation examples
  5. LogRocket: The different ways to use CSS :has() - Modern styling techniques
  6. LambdaTest: Complete Guide to CSS :has() Selector - Comprehensive overview