Understanding the :has() Pseudo-Class in CSS

Written by Software Engineer

March 29, 2025
Understanding the :has() Pseudo-Class in CSS

The :has() pseudo-class is a relational selector that lets you style an element based on its child or contents. Unlike traditional CSS selectors, which mainly target child or sibling elements, :has() can apply styles to a parent based on what’s inside it.

This feature has been in discussion for years, often referred to as the missing "parent selector." While it was initially limited to Safari, it's now widely supported in modern browsers like Chrome and Edge, making it practical for real-world use.

Has caniuse info

Basic Syntax


The :has() selector follows a simple pattern:

element:has(selector) {
  /* Styles applied to the element if it contains the selector */
}

For example, consider the following HTML:

<div class="box">
    <p>This div has no image.</p>
</div>

<div class="box">
    <p>This div contains an image.</p>
    <img src="https://via.placeholder.com/150" alt="Sample Image">
</div>

If you want to style a <div> only when it contains an <img>, you can write:

div:has(img) {
  border: 2px solid blue;
  background-color: lightblue;
}

This applies a border and background only if the <div> has an <img> inside it. If the image is removed, the styles won’t apply.

Basic css has selector example

Chaining :has() for More Powerful Selectors


One of the most powerful aspects of :has() is that it can be chained with multiple conditions, allowing for more advanced and precise styling. This means you can apply styles only when multiple conditions are met.

Let's say we have a card component, and we want to style it differently only if it contains both an image and a button. Without :has(), we’d likely need JavaScript, but now we can achieve this with pure CSS.

Consider the following HTML:

<div class="card">
    <p>This card has no image or button.</p>
</div>

<div class="card">
    <p>This card has an image but no button.</p>
    <img src="https://via.placeholder.com/150" alt="Sample Image">
</div>

<div class="card">
    <p>This card contains both an image and a button.</p>
    <img src="https://via.placeholder.com/150" alt="Sample Image">
    <button>Click Me</button>
</div>

You can then chain the :has() selector like this:

.card {
    width: 300px;
    padding: 20px;
    margin: 20px;
    border: 2px solid gray;
    text-align: center;
    background-color: white;
}

/* Style cards that contain both an image and a button */
.card:has(img):has(button) {
    border-color: green;
    background-color: lightgreen;
}

The .card is only styled if it contains both an image (<img>) and a button (<button>). If a .card has just an image or just a button, it remains unstyled. However, when both conditions are met, the card’s border turns green, and the background changes to light green.

Css has chaining

Before :has(), the only way to achieve this behavior was through JavaScript. Here’s what it would have looked like:

document.querySelectorAll('.card').forEach(card => {
    const hasImage = card.querySelector('img') !== null;
    const hasButton = card.querySelector('button') !== null;

    if (hasImage && hasButton) {
        card.style.border = '2px solid green';
        card.style.backgroundColor = 'lightgreen';
    }
});

The CSS approach is simpler and eliminates the need to loop through elements and modify styles dynamically.

90%

💸 90% OFF YOUR FIRST MONTH WITH ALL VERPEX SHARED WEB HOSTING PLANS

with the discount code

MOVEME

Save Now

Combining :has() with Other CSS Selectors (:not(), :is(), and :where())


The :has() pseudo-class becomes even more powerful when combined with other advanced CSS selectors like :not(), :is(), and :where(). These allow you to create complex yet readable styling rules that would otherwise require JavaScript.

1. Using :has() with :not() to Exclude Elements

The :not() pseudo-class is used to exclude certain elements from being selected. When combined with :has(), it allows you to style elements only when they do not contain specific children.

Let's say we have a list of cards, but some of them don’t contain any content. We can use :not(:has(*)) to hide only the empty ones.

<div class="card">
    <p>This first card has content.</p>
</div>

<div class="card">
    <!-- This second card is empty -->
</div>

<div class="card">
    <p>This third card has content.</p>
</div>

Here is what the CSS code will look like:

.card {
    width: 300px;
    padding: 20px;
    margin: 20px;
    border: 2px solid gray;
    text-align: center;
    background-color: white;
}

/* Hide empty cards */
.card:not(:has(*)) {
    display: none;
}

The first and third .card elements contain content, so they remain visible. The second .card is empty, so it gets hidden.

Css has with not selectors

2. Using :has() with :is() for More Flexible Targeting

The :is() pseudo-class simplifies complex selectors by grouping multiple conditions. When used with :has(), it allows for more dynamic parent selection.

For example, if we want to style a <section> only if it contains either an image or a video, we can do this:

<section class="content">
    <p>This section has no media.</p>
</section>

<section class="content">
    <img src="https://via.placeholder.com/150" alt="Sample Image">
    <p>This section has an image.</p>
</section>

<section class="content">
    <video controls>
        <source src="sample.mp4" type="video/mp4">
    </video>
    <p>This section has a video.</p>
</section>

This is what the CSS code will look like:

.content {
    width: 400px;
    padding: 20px;
    margin: 20px;
    border: 2px solid gray;
    text-align: center;
    background-color: white;
}

/* Style sections that contain either an image OR a video */
.content:has(:is(img, video)) {
    border-color: purple;
    background-color: lavender;
}

The first <section> does not contain an image or video, so it stays unstyled. The second and third <section> elements contain an image or video, so they get a purple border and lavender background.

Css has with is example

3. Using :has() with :where() for Improved Readability

The :where() pseudo-class works similarly to :is() but has no specificity, making it useful for defining default styles that are easy to override.

For example, if we want to highlight any form field group that contains an invalid input, a required field, or a textarea, we can write:

<div class="form-group">
    <label>Name:</label>
    <input type="text">
</div>

<div class="form-group">
    <label>Email:</label>
    <input type="email" required>
</div>

<div class="form-group">
    <label>Message:</label>
    <textarea></textarea>
</div>

Here is the CSS code:

.form-group {
    padding: 10px;
    margin: 10px 0;
    border: 2px solid gray;
}

/* Highlight form groups containing a required field or a textarea */
.form-group:has(:where(input:required, textarea)) {
    border-color: red;
    background-color: #ffe5e5;
}

The first .form-group has a normal input (unstyled). The second .form-group has a required input, so it gets highlighted. The third .form-group contains a textarea, so it gets the same styling.

Css has with where example

By combining :has() with :not(), :is(), and :where(), we can dynamically style elements based on their contents without using JavaScript.

How :has() Affects Performance


Most CSS selectors work top-down, meaning the browser processes the parent element first and then applies styles to its children. However, :has() requires the browser to look inside an element, check if it contains a specific child, and then apply styles to the parent. This extra lookup can slow down performance when applied to a large number of elements.

For instance, if you use :has() on a widely applied selector, like all <div> elements on a page, the browser has to check every single <div> to see if it meets the condition. This increases rendering time and can cause layout shifts, especially in dynamic content where elements frequently update.

Here are some best practices for optimizing :has() usage:

1. Scope It to Specific Containers: Instead of applying :has() to all elements of a type (e.g., all <div> elements on a page), restrict it to a smaller section of your layout. This reduces the number of elements the browser needs to process and speeds up rendering.

2. Avoid Using :has() on Frequently Updated Elements: If :has() is applied to elements that frequently change—such as live comment sections, infinite scrolling lists, or real-time dashboards—the browser will constantly recalculate styles, leading to performance drops. Instead, consider using JavaScript for dynamic changes in these cases.

3. Minimize Its Use in Large Lists or Tables: Applying :has() to every row in a large table or every item in a list forces the browser to check each element, which can be slow. Use it only in necessary sections instead of applying it globally.

4. Be Mindful of Browser Support: While :has() is now supported in Chrome, Edge, and Safari, it is still not available in Firefox. If Firefox compatibility is important, ensure you have fallback styles or use JavaScript to replicate the effect where necessary.

20%

💸 EXTRA 20% OFF ALL VERPEX RESELLER HOSTING PLANS

with the discount code

AWESOME

Save Now

Wrapping Up


The :has() pseudo-class brings powerful new capabilities to CSS, making it easier to style elements based on their children without JavaScript. However, it should be used thoughtfully to avoid performance issues, especially in large or dynamic layouts.

By scoping it properly and considering browser support, you can take full advantage of :has() to write cleaner, more flexible CSS. Now it's time to experiment and integrate it into your projects!

Frequently Asked Questions

Can I integrate CSS preprocessors like SASS or LESS with my CSS hosting?

Yes, most CSS hosting services support preprocessors like SASS or LESS. They allow for more advanced CSS functionalities, enabling you to write cleaner and more maintainable code.

What security measures are essential for CSS hosting?

Key security measures for CSS hosting include SSL/TLS encryption, regular software updates, firewalls, and protection against DDoS attacks. These features safeguard your CSS files and the overall integrity of your website.

What is the impact of server location on CSS hosting performance?

Server location significantly affects the loading speed of CSS files. Hosting your CSS closer to your user base ensures faster download times, reducing website latency and improving user experience.

How does CSS hosting handle scalability for high-traffic websites?

CSS hosting typically employs scalable infrastructure, like cloud hosting, which can dynamically allocate resources to handle increased traffic. This ensures your site remains fast and reliable, even during traffic surges.

Discount

🚀 90% OFF YOUR FIRST MONTH WITH ALL VERPEX CLOUD WEB HOSTING PLANS

with the discount code

MOVEME

Save Now
Jivo Live Chat