CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*)

When feature detecting support for :has(), use :has(+ *) instead of :has(*)


This content originally appeared on Bram.us and was authored by Bramus!

# Feature detecting :has()

To feature detect browser support for the CSS :has() selector, you can use @supports(selector(…)). When doing so, it is important to include a valid selector as its argument. As I’ve tweeted before, you must pass a selector such as * into :has()> when used in a feature query.

/* ❌ This will always evaluate to false */
@supports selector(:has()) {
  …
}

/* ✅ This will evaluate to true in browsers that support :has() */
@supports selector(:has(*)) {
  …
}

💁‍♂️ Initially this selector requirement was only the case in Safari, but this has since been adjusted at the spec level making it a requirement in all other browsers.

I use this technique to conditionally show a warning in many of my :has() demos.

/* Style warning block */
.no-support {
  margin: 1em 0;
  padding: 1em;
  border: 1px solid #ccc;
  background-color: #ff00002b;
  display: block;
}

/* Hide warning block in case :has() support is detected */
@supports selector(:has(*)) {
  .no-support {
    display: none;
  }
}

If your browser has no support, you get to see a warning message telling you about it. This is the case in Firefox which, at the time of writing, does not support :has() out of the box just yet.

Screenshot of Firefox showing one of my :has() demos. As it lacks support for :has(), visitors get to see a warning message telling them the demo will not work correctly.

~

# The Problem with :has(*)

While the approach above does allow you to feature detect :has(), it is not 100% closing. The culprit here is Firefox, which currently has experimental support behind a feature flag.

When flipping the layout.css.has-selector.enabled flag on, Firefox will correctly claim support when using @supports selector(:has(*)). This, however, does not account for the fact that this experimental implementation does not support relative selector parsing (yet).

As my demo above uses :has() with relative selectors, I do want a warning to be shown even if the flag is flipped on. The solution here is to actually use a relative selector, such as + *, as the argument to :has(). Like so:

/* Hide warning block in case :has() support – including relative selectors – is detected */
@supports selector(:has(+ *)) {
  .no-support {
    display: none;
  }
}

~

# Full Code

The full code used in my demo looks like this. It can detect both levels of support and also allows showing a certain box in case support is claimed.

<div class="no-support" data-support="css-has-basic"><p>🚨 Your browser does not support CSS <code>:has()</code>, so this demo will not work correctly.</p></div>
<div class="no-support" data-support="css-has-relative"><p>🚨 Your browser does not support relative selectors in CSS <code>:has()</code>, so this demo will not work correctly.</p></div>
.no-support,
.has-support {
  margin: 1em 0;
  padding: 1em;
  border: 1px solid #ccc;
}

.no-support {
  background-color: #ff00002b;
  display: block;
}
.has-support {
  background-color: #00ff002b;
  display: none;
}

@supports selector(:has(*)) {
  .no-support[data-support="css-has-basic"] {
    display: none;
  }
  .has-support[data-support="css-has-basic"] {
    display: block;
  }
}

@supports selector(:has(+ *)) {
  .no-support[data-support="css-has-relative"] {
    display: none;
  }
  .has-support[data-support="css-has-relative"] {
    display: block;
  }
}

~

# TL;DR

If you’re feature detecting :has() with @supports you must pass a selector into :has(). This can be * but if your code relies on relative selectors used inside :has(), use @supports selector(:has(+ *)) instead. This must be done to filter out Firefox visitors who have flipped on the experimental :has() support which currently lacks support for relative selectors.

~

# Spread the word

To help spread the contents of this post, feel free to retweet its announcement tweet:

~

🔥 Like what you see? Want to stay in the loop? Here's how:


This content originally appeared on Bram.us and was authored by Bramus!


Print Share Comment Cite Upload Translate Updates
APA

Bramus! | Sciencx (2023-01-04T20:20:17+00:00) CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*). Retrieved from https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/

MLA
" » CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*)." Bramus! | Sciencx - Wednesday January 4, 2023, https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/
HARVARD
Bramus! | Sciencx Wednesday January 4, 2023 » CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*)., viewed ,<https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/>
VANCOUVER
Bramus! | Sciencx - » CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/
CHICAGO
" » CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*)." Bramus! | Sciencx - Accessed . https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/
IEEE
" » CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*)." Bramus! | Sciencx [Online]. Available: https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/. [Accessed: ]
rf:citation
» CSS :has() feature detection with @supports(selector(…)): You want :has(+ *), not :has(*) | Bramus! | Sciencx | https://www.scien.cx/2023/01/04/css-has-feature-detection-with-supportsselector-you-want-has-not-has/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.