CSS Parent Selector

The Problem

It is impractical to apply styling directly to a parent or element solely based on its existence. Instead, CSS classes need to be generated and switched based on the desired variation.

Look at this example.

Two versions of our card module are accessible: one with an image and one without an image. CSS can be employed to achieve a comparable outcome:

Example

/* A card with an image */
.card {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* A card without an image */
.card--plain {
  display: block;
  border-top: 3px solid #7c93e9;
}
<!-- Card with an image -->
<div class="card">
  <div class="card__image">
    <img src="https://placehold.co/400x300/1abc9c/ffffff?text=Sample+Image" alt="" />
  </div>
  <div class="card__content">
    <!-- Card content here -->
  </div>
</div>

<!-- Card without an image -->
<div class="card card--plain">
  <div class="card__content">
    <!-- Card content here -->
  </div>
</div>

Since the flex wrapper is not necessary, as demonstrated above, we have intentionally created a different class specifically for displaying a card without an image. Is there a way we could achieve this conditionally in CSS rather than relying on a separate variation class?

This is where CSS proves its usefulness by helping us ascertain if the .card component contains a .card__image.

To verify whether the card contains an image, for example, we can utilize the following:

Example

.card:has(.card__image) {
  display: flex;
  align-items: center;
}

Presenting CSS:Has a selector

The colon (:) selector, as defined in the CSS specification, determines whether a parent element contains a specific element or meets a certain condition, like checking if an input is currently focused.

Let's revisit the previous instance of a sample illustration.

Example

.card:has(.card__image) {
}

We ascertain the presence of the .card__image child element within the .card container. Refer to this illustration:

Essentially, the subsequent code is equivalent to the CSS provided earlier.

Is the card__image element present on the card?

Isn't that just fantastic? Within CSS, we are beginning to witness some logical patterns. It's an ideal opportunity to delve into CSS!

The Has Selector Concerns more than just the Parent

In addition to verifying if a parent contains a child, we can also ascertain if an element is succeeded by a sibling. Consider the following scenario:

Example

.card h2:has(+ p) {
}

This checks if a specific <p> element immediately follows the preceding <h2> element.

Alternatively, it can be utilized in combination with a form element to determine the presence of a focused input.

Example

/* If the form has a focused input, apply the following */
form:has(input:focused) {
  background-color: lightgrey;
}

Browsers Support

As of the current moment, CSS:has is compatible with Chrome Canary and Safari 15.4. It is important to consider if I am able to make use of this support.

Can It Be Applied as An Improvement?

It is indeed achievable. By utilizing the CSS @supports directive, we can validate in the following manner.

Example

@supports selector(:has(*)) {
  /* do something */
}

Now, adequate theoretical knowledge aside, let's delve into practical scenarios!

Uses for CSS:possesses a section header

Normally, while editing a section heading, I typically create two versions: one containing solely the title and the other including the title along with an anchor link.

If a hyperlink is either available or missing, I want to style it in a distinct manner.

Example

<section>
  <div class="section-header">
    <h2>Latest articles</h2>
    <a href="/articles/">See all</a>
  </div>
</section>

Identify the direct child link by selecting the element with the (> a) notation.

Example

.section-header {
  display: flex;
  justify-content: space-between;
}

/* If there is a link, add the following */
.section-header:has(> a) {
  align-items: center;
  border-bottom: 1px solid;
  padding-bottom: 0.5rem;
}

Example 1: Card Component

Let's revisit the initial card example for a moment. There are two options to choose from: one featuring an image and the other without it.

Example

.card:has(.card__image) {
  display: flex;
  align-items: center;
}

We can also implement unique formatting and check if the .card contains an image. For our purposes, this is the boundary's edge.

Example

.card:not(:has(.card__image)) {
  border-top: 3px solid #7c93e9;
}

Two CSS classes are necessary in the event that CSS :has is unavailable to handle the actions that have been performed.

Example

.card--default {
  display: flex;
  align-items: center;
}

.card--plain {
  border-top: 3px solid #7c93e9;
}

View Example 2 of the Demo Card Component.

This sample card displays a pair of distinct card action choices: one featuring a single element (the hyperlink) and the other containing multiple actions (save, share, and additional options).

We aim to activate the display: flex property when the card actions involve two separate wrappers for the activities. As shown in the example, the HTML structure provided is solely for illustrative purposes and may not be accurate.

Example

<div class="card">
    <div class="card__thumb><img src="https://placehold.co/400x300/3498db/ffffff?text=Sample+Image"/></div>
    <div class="card__content">
        <div class="card__actions">
            <div class="start">
                <a href="#">Like</a>
                <a href="#">Save</a>
            </div>
            <div class="end">
                <a href="#">More</a>
            </div>
        </div>
    </div>
</div>
.card__actions:has(.start, .end) {
  display: flex;
  justify-content: space-between;
}

Input Required

This code uses input(). Please provide values below: