Exploring the Power of CSS :has() for Parent-Child Hover Interactions

Exploring the Power of CSS :has() for Parent-Child Hover Interactions
Photo by Pankaj Patel / Unsplash

When you start diving deeper into CSS, you’ll often come across more advanced selectors that allow you to manipulate elements in creative ways. One of those selectors is the :has() pseudo-class, a relatively new addition to CSS that brings exciting capabilities to how we style elements.

For beginners, understanding how CSS selectors work can sometimes feel like learning a new language. Don’t worry—:has() opens up possibilities that are both intuitive and powerful. It allows you to style an element based on the presence or state of its descendants, and in this article, we’ll focus on how it can be used in a parent-child hover interaction.

💡
For you that want to quickly see the demo, see here.

How Hover Interactions Usually Work

Typically, when we think about hover effects in CSS, we imagine hovering over an element and changing its appearance. This is done with the :hover pseudo-class:

.child:hover {
    background-color: lightblue;
}

The code above is simple: when you hover over a .child element, its background color changes to light blue. But what if we want to create a more interactive behavior between the parent and all its children? What if we want to change not just the hovered child but the appearance of other children that are not being hovered?

Enter the :has() Selector

With :has(), we can do something that wasn’t possible in CSS until now—style a parent element based on what’s happening with its children. This opens up a whole new level of control.

Let’s walk through an example where we apply hover effects to both the hovered child and the other children in a container:

.parent:has(.child:hover) .child:not(:hover) {
    background-color: lightcoral;
}

What’s Happening Here?

Let’s break this down so it’s easier to digest:

  • .parent:has(.child:hover): This targets the .parent element, but only when it has a .child element inside it that is being hovered.
  • .child:not(:hover): This targets all the .child elements inside the .parent that are not being hovered. Essentially, when one child is hovered, this rule will apply to the rest of the children.

This allows us to create a scenario where hovering over one child will affect the other children.

Example of How It Works

Imagine you have three child elements inside a parent container. Each one starts with a light gray background. When you hover over one of the children, that child changes to light blue, while the others that are not hovered over change to light coral. This creates a dynamic, interactive experience where all elements inside the parent container respond to the hover action.

Here’s what the complete CSS might look like:

.parent {
    display: flex;
    gap: 10px;
}

.child {
    padding: 10px;
    background-color: lightgray;
    transition: background-color 0.3s ease;
}

.child:hover {
    background-color: lightblue;
}

.parent:has(.child:hover) .child:not(:hover) {
    background-color: lightcoral;
}

In this case, the children inside the .parent container react to the hover event based on their state. The :has() selector is crucial because it allows the parent to check if any child is being hovered, and then apply the necessary styles to the rest.

Why This Matters

The introduction of :has() is a game-changer for web developers, both beginners and experienced alike. It gives you the ability to manipulate relationships between elements without needing JavaScript. Before :has(), if you wanted to achieve this type of behavior, you’d typically have to rely on JavaScript to detect when a child element was hovered and then apply styles accordingly.

Now, CSS alone can handle these interactions, which not only makes your code simpler but also more efficient and easier to maintain.

Expanding the Use of :has()

Beyond hover interactions, the :has() pseudo-class can be used for many other cases. For instance, you can use it to style form inputs based on whether they are empty or filled, or to create dynamic layouts that adapt based on the presence of certain elements within a container.

/* Example of using :has() to style a form when inputs are filled */
.form:has(input:valid) {
    border: 2px solid green;
}

In this example, the :has() selector applies a green border to a form only when one of its input fields is valid. This means you can create reactive layouts purely with CSS, making your forms more responsive and intuitive to user actions.

Browser Support

Before you start using :has() everywhere, it’s worth noting that not all browsers support it yet. Currently, it works in most modern browsers, but you should always check for compatibility to ensure it fits with your target audience. As of now, Chrome, Edge, and Safari have support, while Firefox support is still in development.

💡

Finally

The :has() pseudo-class opens up a lot of new possibilities for creating more dynamic and responsive designs with CSS. In our hover interaction example, it enables us to style child elements based on the actions taken on sibling elements—all without writing any JavaScript.

As you get more comfortable with CSS, you’ll discover how useful :has() can be in various scenarios. Whether you're building complex layouts, enhancing form interactions, or creating engaging hover effects, :has() is a powerful tool in your CSS toolkit. So go ahead and experiment with it in your projects—there's a lot to explore!

Support Us

Subscribe to Buka Corner

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe