The CSS :has() Selector: The Parent Selector is Finally Here

Published on: by Dr. Talib

For decades, developers have wished for a "parent selector" in CSS—a way to style a parent element based on the children it contains. With the recent, widespread adoption of the :has() pseudo-class, this is now a reality. This powerful feature fundamentally changes how we can structure our CSS and allows us to solve complex styling challenges without needing JavaScript.


What is the `:has()` Selector?

The Concept: The :has() relational pseudo-class allows you to select an element if it "has" certain other elements as descendants. The selector you are targeting is the one *before* the `:has()`, and the condition is what goes inside the parentheses.

The Basic Syntax:

/* Select any 'figure' element that has a 'figcaption' inside it */
figure:has(figcaption) {
  /* Apply styles to the 'figure' element itself */
  border: 2px solid green;
}

In this example, only figures that contain a caption will get a green border. A figure with just an image and no caption will be unaffected. This was impossible with CSS alone until now.

Practical Use Case 1: Styling Cards with Images

Imagine a grid of cards. Some cards contain an image, and some do not. You want to apply a different layout (e.g., use Flexbox) only to the cards that contain an image.

The HTML and CSS Solution:

<style>
  .card {
    border: 1px solid #ccc;
    padding: 1rem;
    margin-bottom: 1rem;
  }
  
  /* THIS IS THE MAGIC: Select a .card that has an .card-image inside it */
  .card:has(.card-image) {
    display: flex;
    align-items: center;
    gap: 1rem;
  }
</style>

<!-- This card WILL get the flex layout -->
<div class="card">
  <img class="card-image" src="https://via.placeholder.com/100" alt="Placeholder">
  <div class="card-content">I have an image.</div>
</div>

<!-- This card will NOT get the flex layout -->
<div class="card">
  <div class="card-content">I do not have an image.</div>
</div>

Try it Yourself: Paste this into the HTML Viewer. Without adding any extra classes to the HTML, the CSS is smart enough to apply the flex layout only to the first card. This keeps your HTML clean and your styling logic entirely within the CSS.

Practical Use Case 2: Form Validation Styling

You can style a form field's container based on the state of the input inside it. For example, you can change the color of a label if its associated input is invalid.

Styling a Parent `div` based on an invalid `input`:

<style>
  .form-field { margin-bottom: 1rem; }
  label { display: block; }
  
  /* Select any .form-field that has an invalid input inside it */
  .form-field:has(input:invalid) {
    background-color: #ffe8e8; /* Highlight the whole field in light red */
    border-left: 4px solid red;
    padding: 0.5rem;
  }
</style>

<div class="form-field">
  <label for="email">Enter a valid email:</label>
  <input type="email" id="email" name="email" required>
</div>

Try it Yourself: In the live editor, start typing an invalid email (like "test@"). The entire parent `div` will get a red highlight, providing much clearer feedback to the user, all without a single line of JavaScript.

Browser Support and The Future

As of mid-2024, the :has() selector is supported in all major modern browsers (Chrome, Firefox, Safari, Edge). While you should always check caniuse.com for the very latest data, it is now considered safe to use for most projects.

The ability to style based on descendants represents one of the biggest shifts in CSS thinking in years. It allows for more robust, resilient, and context-aware component styling than ever before.

A New Way of Thinking in CSS

The :has() selector encourages us to write more declarative CSS that responds to the state and content of our HTML, reducing our reliance on JavaScript for styling hooks and conditional classes. As you become more comfortable with it, you'll find it opening up new and elegant solutions to problems that were once notoriously difficult to solve with CSS alone.