CSS Feature Detection with @supports (No Modernizr)

CSS feature detection without JavaScript

Detecting CSS feature support required JavaScript: either Modernizr, inline CSS.supports() checks, or testing computed styles. @supports lets CSS apply styles conditionally based on whether the browser supports a given property and value.

Old way JS required
// JS: detect support, add classif (CSS.supports('container-type', 'inline-size')) {  document.documentElement.classList.add('has-container-queries');}
Modern
3 lines
@supports (display: grid)   {  .layout   {    display: grid;  }}
Widely available Since 2020 96% global usage

This feature is well established and works across many devices and browser versions. It has been available across browsers since 2020.

Safe to use without fallbacks.

28+
22+
9+
12+
@supports switches layout — no JS required
Item 1
Item 2
Item 3
Item 4
@supports (display: grid) — layout switches automatically

Detection and fallback in one place

@supports keeps feature detection in CSS where the styles live. No JS, no class toggling, no DOM mutation to apply a CSS branch.

Supports not, and, or

@supports not (), @supports (a) and (b), and @supports (a) or (b) cover all combinatorial cases for progressive enhancement.

Selector detection too

@supports selector(:has()) tests whether a CSS selector is supported, which is useful for detecting newer pseudo-classes before using them.

Old Approach
JS + class toggling
Modernizr or CSS.supports() then DOM mutation
Modern Approach
@supports
Conditional CSS blocks, no JavaScript
Variants
not / and / or
Combine conditions with logical operators

How it works

Progressive enhancement with JavaScript meant detecting support, adding a class to the root element, then writing separate CSS for that class. This split the feature logic across two files and required JS to run before styles applied correctly. That's a fragile dependency.

@supports is a CSS conditional block. Styles inside only apply when the browser supports the tested declaration. @supports not provides a fallback path. @supports selector(:has()) tests selector support. Because the detection is in CSS, no JavaScript is needed and styles are self-contained.

ESC