When CSS @supports Is Useful
Browsers already ignore declarations they do not understand. That means a new CSS value often needs no feature query at all:
.button {
background: #3366ff;
background: oklch(62% 0.18 250);
}
An older browser keeps the hex colour. A newer one replaces it with oklch(). I use @supports when the fallback needs more than this natural cascade.
Put the working baseline first
.card {
display: block;
}
@supports (display: grid) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
The condition tests a property-value pair. It tells you whether the browser parses that declaration, not whether its implementation is bug-free or appropriate for your particular layout.
Newer optional behaviour is a good fit:
@supports (field-sizing: content) {
textarea {
field-sizing: content;
}
}
The textarea remains usable without the enhancement.
Selectors can be queried too
@supports selector(:has(*)) {
.field:has(input:focus) {
outline: 2px solid currentColor;
}
}
Use and, or, and not when a fallback genuinely depends on a combination:
@supports (display: grid) and (gap: 1rem) {
.layout {
display: grid;
gap: 1rem;
}
}
@supports not (display: grid) {
.layout {
display: flex;
}
}
I usually keep the fallback outside the not block. It is easier to find, and it remains the default when a browser does not understand feature queries themselves.
Do not put the page behind a test
@supports (new-feature: value) {
body {
display: block;
}
}
If essential content exists only inside a support condition, progressive enhancement has gone missing. Start with a usable page and query the polish.
For example, keyword-size interpolation can enhance an otherwise ordinary panel:
.panel {
height: auto;
}
@supports (interpolate-size: allow-keywords) {
.panel {
interpolate-size: allow-keywords;
transition: height 250ms;
}
}
My check is simple: if ignoring one declaration leaves an acceptable result, let the cascade handle it. If the browser needs a different group of rules, @supports makes that decision explicit.