Skip to content

A Powerful CSS Pseudo Class, Let's Take a Look at CSS :has()

8 min read


Rizki Maulana Citra

Written by / Rizki Maulana Citra

Introduction

Well, since CSS got upgraded to version 3 in 1998, A lot of things happened, and until now, CSS has already got many features that we can use to enhance our website to be more beautiful and interactive.

Web Developer will never leave their CSS behind because it's one of the cores of the technology behind the web, current CSS version let us use our creativity by crafting modern and accessible web applications.

But more features mean we need more time to think about how it works (again), although most modern web apps use CSS framework for managing their stylesheet, enhancing our fundamentals about the core of the CSS framework itself is necessary.

CSS has never been easy, not a few people understand how CSS works (including me), but there is a person out there ready to help you fall in love with CSS, check out his YouTube channel.

One of the problems is that CSS is not a programming language, so it's very hard to debug when something goes wrong, and you might think " CSS is quite easy, we change something, the UI reflected, what's the hard part?". That's what I thought when I was using Bootstrap, but the hard part is when you do all the stuff and some elements didn't quite fit with your expectation, like setting how to set this style to a specific element that contains this.

Well if you back to 1998 to do all those stuff, it's going to be challenging, but don't worry we now have the feature to do all those stuff.

It's a CSS pseudo-class that will handle all of those for you. Just imagine you want to target an element that contains something and also the element shouldn't contain something again, it's like a condition in a programming language.

TS
if(somethingOk && somethingOkToo) // do the stuff

Pretty cool right? now before we talk about this feature, let's talk a little bit about what CSS pseudo-class is.

What is CSS pseudo-class?

In CSS, A pseudo-class is a keyword added to a selector that specifies a special state of the selected element(s). For example, the pseudo-class :hover can be used to select a button when a user's pointer hovers over the button and this selected button can then be styled.

I'm not going to talk much about what pseudo-class is, but here's a simple explanation of it, we will have a button with a black background color, and the text color will be white, I'm also going to add some padding.

CSS
.button {
  padding: 0.75rem;
  background-color: black;
  color: white;
}

Result:

regular button

We have a nice button there, now let's add interaction, such as color change when the user hovers over the element with :hover pseudo-class.

CSS
.button {
  padding: 0.75rem;
  background-color: black;
  color: white;
}

.button:hover {
  background-color: red;
}

Now the button would look like this:

regular button with hover

After I added the :hover pseudo-class, the button will change it's color if I hover over the element, that's my simple explanation about the pseudo-class.

What is CSS :has() pseudo-class?

The functional :has() CSS pseudo-class represents an element if any of the relative selectors that are passed as an argument match at least one element when anchored against this element. This pseudo-class presents a way of selecting a parent element or a previous sibling element concerning a reference element by taking a forgiving relative selector list as an argument.

  • Q: Why it's called a functional?
  • A: Because this type of pseudo-class accepts parameters rather than just calling it like :hover

CSS :has() pseudo-class will accept forgiving relative element and will apply a style if the parameters are valid as specificity.

CSS :has() pseudo-class often described as "Parent Selector" because a lot of people use it to target the parent element but then they actually want to select a specific element that matches with the condition.

As such, you might want to chill and understand how :has() works, this might break your mental model about the CSS itseld, now to simplify how :has() works, take a look at this CSS snippet:

CSS
/* Matches <a> elements that contain an <span> child with a class colored */
a:has(span.colored) {
  /*...*/
}

/* Matches <a> elements that directly contain an <img> child */
a:has(> img) {
  /*...*/
}

/* Matches <section> elements that DON'T contain any heading elements: */
section:not(:has(h1, h2, h3, h4, h5, h6)) {
  /*...*/
}

CSS :has() examples

Let's take a look at these example of how the CSS pseudo-class :has() works

HTML
<main>
  <section>
    <h1>Punctuation</h1>
    <p>A simple punctuation will never arrive at a dragon's house tonight.</p>
  </section>

  <section>
    <h1 id="vergil">Punctuation</h1>
    <p>Hi Andrew can I borrow your pen?</p>
    <p>Lorem ipsum dolor sit amet constectum adpisicing elit.</p>
  </section>
</main>
CSS
section:has(h1#vergil) {
  color: blue;
}

The result is going to look like this:

css has example

So what is happening? as you can see, I'm targetting the section element that has an h1 element with an id of vergil, and then the whole section will have a text color of blue.

It's kinda confusing if you read this pseudo-class for the first time, but you should feel comfortable later.

Another example is when your website has a lot of images and some of the images will have a figcaption in them. This way you can use a different style to the image that has a figcaption by using CSS :has() pseudo-class.

HTML
<div class="container">
  <figure>
    <img src="/mr-crab-dank.jpg" alt="Mr. Crab" />
  </figure>
  <figure>
    <img src="/your-ms-puff.jpg" alt="Ms. Puff" />
    <figcaption>Ms. Puff waving at you</figcaption>
  </figure>
</div>

Also with a little bit of base styling with this CSS

CSS
* {
  margin: 0;
  padding: 0;
}

.container {
  padding-top: 0.875rem;
  width: 91%;
  margin: 0 auto;
  max-width: 524px;
  display: flex;
  flex-direction: column;
  gap: 0.765rem;
}

.container figure {
  width: 100%;
}

figure img {
  display: block;
  width: 50%;
  margin: 0 auto;
}

The result will look like this

Mr. Crab Ms. Puff

Now we have 2 images that contain Mr. Crab and Ms. Puff, but you as can see between the images, only Ms. Puff who have a descriptive figcaption.

Now let's style an image that contains figcaption to be different from the other images. I'm going to style it to have padding and box-shadow.

CSS
figure:has(img + figcaption) {
  padding: 0.75rem;
  box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.25);
}

And the result will be like this

Mr. Crab Ms. Puff edit

I simply add some padding with a box-shadow to the <figure> element, if the <figure> element has a child that is an <img> element but also has <figcaption> element that is a sibling of the <img /> element.

I hope I explained well with that sentence 😅

But the point is I can style the <figure> element but with a condition if the element has a certain element or is followed by another selector by using CSS :has() pseudo-class.

CSS :has() can also be used along with another pseudo-class, for example using :nth-child(n) to manage grid column. This can be useful if you don't want to use Media Queries that much.

CSS
/* This will apply if a container has 4 children or more */
.container:has(:nth-child(5)) {
  ...;
}

/* This will apply if a container has 4 children or more */
.container:has(:nth-child(10)) {
  ...;
}

The above snippet can be used to modify our grid column to be 1 when the container has 5 or more items, but when the container has 10 or more items, a different style can be applied, for example, styling a grid column to be 2.

Browser Compatibility

CSS Has browser compatibility

CSS pseudo-class :has() is now already supported on many browsers, including Chrome, Edge, and Firefox, but not for Internet Explorer.

Internet Explorer is already dead anyway so this is not a problem because so many people already use a modern web browser like Edge or Chrome.

But be careful, CSS :has is already supported on Firefox, but it is disabled by default, so you need to be aware of this one, as of today, there are a lot of people still using FireFox.

Support Fallback

As on firefox, CSS :has() pseudo-class is not enabled by default, you might need to think about the fallback, fortunately, we have CSS at-rule to handle if the browser support some rules or style.

CSS
@supports (selector(:has(p))) {
  /* Supported! */
}

This will seek if the browser supports the CSS :has() pseudo-class.

Closing

And that's it, folks, We're reaching the end of this post, thank you so much to read the blog till the last part. I really appreciate and I hope this post is useful for you.

If you have any questions or want to share your thought related to this post, you can comment on this post by using your GitHub account, see you later.