A Use Case For A Parent Selector

Discover how the CSS :has() pseudo-class enables powerful parent selection for adaptive layouts, form validation, and responsive components.

The CSS Parent Selector Dream

For years, CSS developers dreamed of a selector that could style a parent element based on its children. This capability seemed just out of reach, requiring workarounds with JavaScript or complex HTML restructuring. The CSS :has() pseudo-class has changed all of that, bringing true parent selection to modern web development.

Whether you're building with Next.js, React, or vanilla HTML and CSS, :has() opens up entirely new possibilities for responsive, adaptive layouts without sacrificing performance or maintainability. This powerful selector works seamlessly with modern CSS techniques like CSS Grid to create truly dynamic components.

What You'll Learn

  • Basic :has() syntax and selector patterns
  • Practical form validation techniques
  • Adaptive card and layout patterns
  • Navigation menu indicators
  • Quantity-based styling approaches
  • Browser support and performance tips
Key Capabilities of :has()

Understanding the powerful features that make :has() a game-changer for CSS developers

True Parent Selection

Style parent elements based on their children, enabling adaptive layouts without JavaScript or complex HTML restructuring.

Previous Sibling Selection

Select elements based on what follows them, unlocking styling patterns that were previously impossible in CSS.

State-Based Styling

Style containers based on the validation state or interactive state of their child elements.

Quantity Queries

Apply styles based on how many children an element contains, creating truly adaptive grid layouts.

Understanding the :has() Pseudo-Class

The :has() pseudo-class represents an element that has at least one element matching the selector passed as an argument. Unlike traditional CSS selectors that traverse downward (from parent to child), :has() allows you to look upward and sideways, checking for the presence of descendants or siblings before applying styles.

Basic Syntax

The :has() selector accepts any selector combination as its argument, including compound selectors, pseudo-elements, and other pseudo-classes:

/* Style a card if it contains an image */
.card:has(img) {
 grid-template-columns: 1fr 2fr;
}

/* Style a form if it contains an invalid input */
form:has(input:invalid) {
 border-color: #ef4444;
}

/* Style a list item that has a submenu */
nav li:has(ul) > a::after {
 content: "▼";
}

The selector reads naturally: "Style this if it has that." This intuitive pattern makes your CSS more readable and maintainable.

Basic :has() Selectors
1/* Style containers with specific children */2.container:has(.featured) {3 padding: 2rem;4}5 6/* Multiple selector support */7nav li:has(a.active, button.active) {8 background-color: #f3f4f6;9}10 11/* Combining with :not() */12.card:not(:has(.sale-badge)) {13 opacity: 0.9;14}15 16/* Nested condition checking */17.article:has(.author-bio:has(img)) {18 display: grid;19 grid-template-columns: 80px 1fr;20}

Practical Use Cases for Parent Selection

Adaptive Card Layouts

One of the most common use cases for :has() is creating adaptive card layouts that change based on their content. Previously, you might have needed to add conditional classes server-side or use JavaScript. With :has(), this becomes a simple CSS rule that automatically adapts.

/* Cards with images get a side-by-side layout */
.card:has(img) {
 display: grid;
 grid-template-columns: 200px 1fr;
 gap: 1.5rem;
}

/* Cards with featured images get special styling */
.card:has(.featured-image) {
 position: relative;
 overflow: hidden;
}

Form Validation and Feedback

Forms benefit greatly from :has() because you can style the entire form or specific form groups based on the validation state of their inputs. This approach works with HTML5's built-in validation attributes, creating comprehensive visual feedback using only CSS without relying on JavaScript error logging for every validation scenario:

/* Style the form when any field is invalid */
form:has(input:invalid) {
 border-color: #dc2626;
 background-color: #fef2f2;
}

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

/* Highlight completed sections */
fieldset:has(input:valid) {
 border-color: #22c55e;
 background-color: #f0fdf4;
}

This CSS-first approach reduces the JavaScript needed for form handling while providing excellent user experience.

Navigation Menus with Indicators

Creating navigation menus that indicate which items have dropdowns or submenus is another excellent use case:

/* Add arrow indicator to menu items with submenus */
nav li:has(ul) > a::after {
 content: "";
 display: inline-block;
 width: 0;
 height: 0;
 margin-left: 0.5rem;
 border-left: 4px solid transparent;
 border-right: 4px solid transparent;
 border-top: 4px solid currentColor;
}

/* Style menu items with active submenus */
nav li:has(.active) > a {
 color: #3b82f6;
 font-weight: 600;
}

This pattern is useful for dynamic navigation where submenus might change based on user permissions or content structure.

Quantity-Based Styling

The :has() selector enables powerful quantity queries, styling elements based on how many children they contain:

/* Style lists with 4 or more items */
ul:has(> li:nth-last-child(n+4)) {
 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* Add spacing when there are many items */
.grid:has(> .item:nth-child(n+6)) {
 gap: 2rem;
}

/* Highlight items in a long list */
.list-item:has(~ .list-item) {
 border-bottom: 1px solid #e5e7eb;
}

These quantity queries create truly adaptive layouts that respond to content structure, making your CSS Grid Layout implementations more flexible.

Previous Sibling Selection

Before :has(), selecting previous siblings required workarounds. Now you can style elements based on what follows them:

/* Add separator between breadcrumb items */
.breadcrumb-item:has(~ .breadcrumb-item)::after {
 content: "/";
 margin: 0 0.75rem;
 color: #9ca3af;
}

/* Highlight selected item and its neighbors */
.list-item:has(+ .list-item.active) {
 opacity: 0.7;
}

.list-item.active {
 font-weight: 700;
 color: #3b82f6;
}

This unlocks creative navigation patterns, timeline components, and responsive list styling using techniques that complement your position value implementations.

Best Practices for :has() Performance

Selector Efficiency

While :has() is powerful, it's important to use it efficiently. Complex :has() selectors with many conditions can impact rendering performance, especially with deeply nested structures:

/* Good: Specific and efficient */
.card:has(.featured) {
 border-color: #3b82f6;
}

/* Avoid when possible: Complex nested selector */
.container:has(.wrapper:has(.inner:has(.content))) {
 /* This can be expensive */
}

Combining with Modern CSS

The :has() pseudo-class works beautifully with CSS Grid, Flexbox, and container queries:

@container (min-width: 400px) {
 .card:has(img) {
 grid-template-columns: 1fr 1fr;
 }
}

/* Combine with :hover for interactive feedback */
.card:has(:hover) {
 box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
}

For optimal performance in your web development projects, use :has() judiciously and test across target browsers.

Browser Support

100%

Modern Browser Support

4

Major Browsers Supported

2024

Full Support Achieved

Conclusion

The CSS :has() pseudo-class represents a significant advancement in what CSS can accomplish without JavaScript. From adaptive card layouts to form validation feedback, from navigation indicators to quantity-based styling, :has() simplifies many common styling challenges encountered in modern web development.

Its full support across modern browsers makes it safe to use in production, and its graceful degradation means you can adopt it confidently. As you build applications with frameworks like Next.js or React, :has() offers a powerful tool for creating responsive, content-aware components using only CSS--complementing techniques like CSS Grid Layout and advanced position value strategies.

Start experimenting with :has() in your projects--you'll likely find it solves styling problems you've been working around for years.

Related Resources

Frequently Asked Questions

Ready to Modernize Your Web Development?

Our team specializes in building performant, responsive web applications using the latest CSS features and modern frameworks.