Discover the Benefits of the Upcoming CSS :has() Selector

On December 20th, 2021, Safari Technology Preview 137 was released, adding unflagged support for the :has() selector. An unflagged feature doesn’t require the user to manually enable it themselves.

The long-requested tool is most often thought of as a “parent selector” as its most commonly anticipated use case is to select a parent element that contains qualified children.

For example, maybe you are styling links, but need to treat links that contain heading tags differently, you could do the following:

a:has(h1, h2, h3, h4, h5, h6) { … }

If you don’t need to add styling to those heading links and would like to just target anchors that don’t contain a heading, you can combine :not and :has with the following:

a:not(:has(h1, h2, h3, h4, h5, h6)) { … }

Other uses are selecting elements inside a matching container, similar to checking siblings.

You could target h2s inside a container that doesn’t contain an h1:

div:not(:has(h1)) h2 { … }

You could also target the items that only have a specific element immediately following them. We’ve been used to achieving “only paragraph tags immediately after h1s” with:

h1 + p { … }

While that targets the paragraph tag, until now, we could not target the preceding h1. With :has, we can:

h1:has(+ p) { … }

Style Labels for Invalid Inputs

In the event you put your input elements inside the label elements for your forms, you can use :has to style the labels for inputs that do not pass validation:

label:has( input:invalid ) { color: red }

If you put your inputs after your labels and use the “for” and “id” attributes to associate your labels to your inputs, you can do the following:

Label:has(+ input:invalid) { color: red };

Some Notes on Specificity

:has() is a forgiving selector.

In the event one of your rules in your rule-set is invalid, it does not invalidate the whole rule-set.

a:has(h1, h2, $heading:h3) { … }

$heading:h3 is an invalid selector, but the h1 and h2 rules will still be valid.

The specificity of any argument is that of the most specific argument.

If the rule-set has multiple rules, the rule with the highest specificity passes that along to its other rules.

p:has( #foo, .bar ) {
    color: red;
}

p.bar {
    color: blue;
}

Even though it appears both targets for p.bar have the same specificity, and typically, the later rule making it blue would be used, the higher specificity of p#foo in the first rule gets applied to the first p.bar, resulting in the paragraph being red.

While this is still a very new selector only available in Safari’s Technology Preview version, getting familiar with its possibilities will enable you to take full advantage when it’s widely supported. For more web development assistance, contact the experts at Hall.

See how Hall can help increase your demand.