Feature queries: @supports
New CSS properties do not arrive in all browsers at once. The @supports rule asks the browser: "Do you understand this property?" — and applies the styles only if the answer is "yes". Such a check is called a feature query.
The @supports rule
The parentheses hold a "property: value" pair. The styles inside the block apply only if the browser understands it:
<style>
.box { padding: 12px; background: gainsboro; }
@supports (display: grid) {
.box { background: palegreen; }
}
</style>
<div class="box">Green means the browser knows display: grid</div>It works like a media query, only the condition is not the window size but the browser's abilities.
not, and, or
Conditions can be reversed and combined. The fly-mode property does not exist, so only the second block applies:
<style>
.msg { display: none; padding: 12px; }
@supports (fly-mode: on) {
.yes { display: block; background: palegreen; }
}
@supports not (fly-mode: on) {
.no { display: block; background: mistyrose; }
}
</style>
<p class="msg yes">The browser can fly</p>
<p class="msg no">The browser does not know the fly-mode property</p>| Form | When it applies |
|---|---|
@supports (a: 1) | the browser understands a: 1 |
@supports not (a: 1) | it does not |
@supports (a: 1) and (b: 2) | it understands both |
@supports (a: 1) or (b: 2) | it understands at least one |
Checking a selector: selector()
The selector() function checks a selector instead of a property. This is how you find out whether, for example, :has() is supported:
<style>
.note { padding: 12px; background: gainsboro; }
@supports selector(:has(a)) {
.note { background: lightskyblue; }
}
</style>
<p class="note">Light blue means the browser understands :has()</p>Progressive enhancement
Progressive enhancement is an approach where the page is first made to work everywhere, and new features are added on top for the browsers that understand them. The base style is written as usual, and the enhancement goes inside @supports:
<style>
.panel { padding: 12px; background: steelblue; color: white; }
@supports (backdrop-filter: blur(4px)) {
.panel { background: rgb(70 130 180 / 60%); backdrop-filter: blur(4px); }
}
.scene { padding: 16px; background: linear-gradient(45deg, gold, tomato); }
</style>
<div class="scene">
<div class="panel">A panel with a blurred background, or just a blue one</div>
</div>An old browser shows a solid blue panel, a new one a semi-transparent blurred one. Both versions look fine.
When @supports is not needed
The browser skips declarations it does not understand. So for a single property two lines in a row are enough: first the fallback value, then the new one:
<style>
.title { color: steelblue; color: oklch(55% 0.15 260); }
</style>
<p class="title">An old browser takes steelblue, a new one oklch()</p>@supports is needed when other properties change together with the new one, like the panel background in the previous example.
Common mistake
A condition without parentheses or without a value. @supports display: grid and @supports (display) are errors, and the browser skips the whole block:
<style>
.a, .b { padding: 8px; margin-bottom: 4px; background: gainsboro; }
@supports (display) { .a { background: palegreen; } }
@supports (display: block) { .b { background: palegreen; } }
</style>
<p class="a">(display) — no value, did not work</p>
<p class="b">(display: block) — worked</p>Write the full "property: value" pair in parentheses.