CSS :has() works in Chrome 105+, Edge 105+, Firefox 121+, Safari 15.4+, Opera 91+, and Samsung Internet 20+. Learn :has() browser support and quirks.

Prince Dewani
May 1, 2026
CSS :has() is a relational pseudo-class in CSS Selectors Level 4 that selects an element when any selector inside its parens matches a child or sibling. It works in Chrome 105+, Edge 105+, Firefox 121+, Safari 15.4 on macOS and iOS, Opera 91+, Samsung Internet 20+, and Android Browser 147+, while Internet Explorer never added support.
This guide covers what :has() is, the browsers that support it, the key features, how to check support, and the known issues.
CSS :has() is a relational pseudo-class defined in the CSS Selectors Level 4 specification from the W3C. It accepts a relative selector list as its argument and matches the anchor element when any of those selectors find at least one matching child or sibling. It is the first true parent selector in CSS.
CSS :has() works in every modern desktop and mobile browser. Chrome 105, Edge 105, Firefox 121, Safari 15.4 on macOS and iOS, Opera 91, and Samsung Internet 20 all enable it by default, while Internet Explorer never received support.
Chrome supports CSS :has() by default from Chrome 105 on Windows, macOS, Linux, ChromeOS, and Android. Chrome 101 to 104 had :has() disabled by default behind the Experimental Web Platform features flag in chrome://flags, and Chrome 4 to 100 did not support it at all.
Microsoft Edge supports CSS :has() by default from Edge 105 on Windows, macOS, and Linux. Edge 12 to 104 did not support :has() at all. Legacy EdgeHTML versions never received the relational pseudo-class, so users on stale Edge builds should switch to a current Chromium-based Edge release.
Firefox supports CSS :has() by default from Firefox 121 on Windows, macOS, Linux, and Android. Firefox 103 to 120 had :has() disabled by default behind the layout.css.has-selector.enabled preference in about:config, and Firefox 2 to 102 did not support it. Firefox for Android picks up support from Firefox 121.
Safari supports CSS :has() by default from Safari 15.4 on macOS and from iOS 15.4 on iPhone and iPad. Safari 3.1 to 15.3 on macOS and Safari on iOS 3.2 to 15.3 do not support :has(). The WebKit team shipped the first production-quality :has() implementation, ahead of Chromium and Firefox.
Opera supports CSS :has() by default from Opera 91 on desktop and from Opera Mobile 80 on Android. Opera 9 to 90 did not support :has(). Opera follows the Chromium release cadence, so it inherits the same Blink :has() implementation that Chrome uses.
Samsung Internet supports CSS :has() by default from Samsung Internet 20 on Galaxy phones and tablets. Earlier Samsung Internet builds, from version 4 through 19.0, did not support :has(). The browser shares the underlying Chromium engine with Chrome on Android.
Chrome for Android supports CSS :has() by default from Chrome 105, and the modern Android Browser ships :has() from version 147 onward. WebView on Android 5.0 and later inherits the Chromium engine, so apps that embed WebView pick up :has() once the system WebView updates past Chromium 105.
Internet Explorer 5.5 through 11 do not support CSS :has() in any version. The Trident engine never received the relational pseudo-class. Users on Windows who need :has() should switch to Microsoft Edge 105 or later or to Chrome 105 or later.
Note: CSS :has() breaks across browsers when shipped without a fallback. Test it on real browsers and OS with TestMu AI. Try TestMu AI free!
CSS :has() opens up DOM-aware styling that previously needed JavaScript. The selector keeps a small surface area but unlocks several patterns CSS could never express on its own.
The cleanest check is the @supports at-rule with a selector() condition. Wrap the :has() rules so the browser only applies them when the engine recognizes the selector. Pair it with @supports not to ship a fallback for older browsers.
/* Feature-detect :has() before applying it. The block runs only when the
browser parses :has(); older browsers skip it and use the fallback below. */
@supports selector(:has(*)) {
.card:has(img) {
padding: 1rem;
border: 1px solid #ddd;
}
form:has(:invalid) {
outline: 2px solid red;
}
}
/* Fallback for browsers without :has() support. */
@supports not selector(:has(*)) {
.card {
padding: 1rem;
}
}For runtime checks, the CSS.supports JavaScript API parses the same selector string. Paste this snippet into the DevTools console of any browser to see whether the engine recognizes :has().
// Run in any browser DevTools console to check :has() support at runtime.
// CSS.supports parses the selector string and returns true when the engine
// recognizes it.
const supportsHas = CSS.supports('selector(:has(*))');
console.log(`:has() supported in this browser: ${supportsHas}`);
// Logs true on Chrome 105+, Edge 105+, Firefox 121+, Safari 15.4+,
// Opera 91+, and Samsung Internet 20+. Logs false everywhere else.If CSS.supports returns false, the browser silently ignores any rule that contains :has(). Use the @supports not block above to set a baseline style for those users instead of leaving the layout broken.
CSS :has() is now Baseline 2023, but the way browsers parse and resolve it still has rough edges that hit production sites. Plan around these before you replace JavaScript-driven class toggling with :has().
In my experience, the silent failure mode that bites teams the most is forgetting the @supports wrapper: a :has() rule outside an @supports block can break the cascade on Internet Explorer and on stale Android WebView builds. Always feature-detect before shipping :has() to a multi-browser audience.
All CSS :has() version numbers and platform notes in this guide come from these primary sources:
Did you find this page helpful?
More Related Hubs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance