← Back to DevBytes

Mastering CSS Has Selector: Tips and Best Practices

What is the CSS :has() Selector?

The :has() pseudo-class is a relational selector that allows you to style an element based on the existence, state, or position of its descendants or siblings. Often referred to as the “parent selector”, it actually goes much further: you can select an element not only when it contains a specific child, but also when it is followed or preceded by a particular element, when its children match certain states, or even when it contains a certain amount of elements.

Introduced in the CSS Selectors Level 4 specification, :has() is now supported across all modern browsers (Chrome, Firefox, Safari, and Edge). It brings conditional logic directly into your stylesheets—no JavaScript required.

Why :has() Matters for Modern Web Development

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Before :has(), developers often resorted to JavaScript or complex CSS hacks to style a parent based on a child’s state. For example, highlighting a form when a checkbox is checked, or changing a card’s appearance when it contains an image, required toggling classes with JS. This added complexity, hurt performance, and mixed concerns.

With :has(), you can express such relationships natively in CSS. The result is cleaner, more maintainable code, fewer DOM manipulations, and better separation of concerns. It empowers you to build dynamic, context-aware components while keeping your HTML and JavaScript simpler.

How to Use the :has() Selector

Basic Syntax and Simple Examples

The syntax is straightforward: element:has(selector). The selector inside :has() can be any valid CSS selector—descendant, child, attribute, pseudo-class, or even sibling combinators.

/* Select a div that contains any image */
div:has(img) {
  border: 2px solid #3498db;
}

/* Select a section that contains a heading of level 2 */
section:has(h2) {
  background: #f9f9f9;
}

/* Select a form that has a checked checkbox */
form:has(input[type="checkbox"]:checked) {
  box-shadow: 0 0 8px rgba(0,128,0,0.5);
}

Parent-Child Relationships

The most common use case is selecting a parent based on its children. This works with any descendant, not just direct children.

/* Highlight a card that contains a "sale" badge */
.card:has(.badge--sale) {
  border-color: #e74c3c;
}

/* Style a navigation item that contains a dropdown menu */
li:has(ul) {
  position: relative;
  cursor: pointer;
}

Combining with Other Selectors

You can combine :has() with other pseudo-classes, attributes, and even use it as part of a compound selector. This enables highly specific styling without extra classes.

/* Select a label that is immediately followed by an invalid input */
label:has(+ input:invalid) {
  color: red;
  font-weight: bold;
}

/* Select a button that is the only child of its parent */
div:has(button:only-child) {
  display: flex;
  justify-content: center;
}

/* Select a figure that contains a figcaption and an image */
figure:has(figcaption, img) {
  border: 1px solid #ddd;
}

Complex Conditional Logic

:has() accepts a selector list, so you can combine multiple conditions. It also works with relative selectors, letting you target elements based on their siblings or even preceding elements.

/* Select a .container that is followed by a .sidebar */
.container:has(+ .sidebar) {
  margin-right: 250px;
}

/* Select an input that is preceded by a label with required class */
input:has(label.required + *) {
  border-left: 3px solid orange;
}

/* Select a list that has more than 5 items (using quantity queries) */
ul:has(li:nth-child(6)) {
  column-count: 2;
}

Best Practices for Using :has()

Conclusion

The :has() selector fundamentally changes how we think about relationships in CSS. It brings the missing piece of conditional styling, allowing you to build smarter, more interactive interfaces with pure stylesheets. By following best practices—keeping selectors readable, avoiding forbidden nesting, and leveraging progressive enhancement—you can integrate :has() seamlessly into your workflow. Embrace this powerful tool and enjoy writing CSS that truly reflects the structure and state of your components.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles