@supports is the CSS equivalent of JavaScript feature detection — it lets you apply styles only when the browser supports a specific property or value, without a JavaScript polyfill or a class added by Modernizr. Combined with @supports selector() (for testing pseudo-class support) and @supports font-tech() (for font feature detection), it provides a complete feature-detection system inside CSS.
Problem: A WordPress theme or plugin uses CSS features that are not supported in all target browsers — Grid subgrid, :has(), color-mix() — and there is no systematic approach to providing fallbacks only where needed without duplicating the entire stylesheet.
Solution: Use CSS @supports to progressively enhance: wrap the modern feature in @supports (grid-template-columns: subgrid) { ... } and provide a Flexbox fallback outside the block. Use @supports not for the fallback path. Combine with @supports selector(:has(*)) for selector-based feature detection. This avoids JavaScript feature detection for pure CSS capabilities.
The examples below show basic property detection, selector detection, detecting new layout features with a fallback, using not and and operators, and a WordPress block stylesheet that progressively enhances with newer CSS features.
/* ── 1. Basic property detection ─────────────────────────────────────────── */
/* Fallback: floats for all browsers */
.columns { overflow: hidden; }
.columns > * { float: left; width: 50%; }
/* Enhancement: use grid when supported */
@supports (display: grid) {
.columns {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
overflow: visible;
}
.columns > * { float: none; width: auto; }
}
/* ── 2. Value detection ─────────────────────────────────────────────────── */
/* Fallback: HSL colour */
:root { --accent: hsl(200 80% 40%); }
/* Enhancement: OKLCH when supported */
@supports (color: oklch(50% .2 250)) {
:root { --accent: oklch(50% .2 250); }
}
/* ── 3. Selector detection ──────────────────────────────────────────────── */
/* :has() is not supported in older Firefox */
@supports selector(:has(> img)) {
.card:has(> img) {
display: grid;
grid-template-rows: auto 1fr;
}
}
/* ── 4. Logical operators: and, or, not ─────────────────────────────────── */
@supports (display: grid) and (gap: 1rem) {
/* Both must be true */
}
@supports (display: flex) or (display: grid) {
/* Either is fine */
}
@supports not (display: grid) {
/* Fallback for browsers without grid */
.layout { display: flex; flex-wrap: wrap; }
}
/* ── 5. Container query detection ────────────────────────────────────────── */
@supports (container-type: inline-size) {
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card { flex-direction: row; }
}
}
/* ── 6. WordPress block: progressive enhancement ────────────────────────── */
.wp-block-my-plugin-gallery {
display: flex;
flex-wrap: wrap;
gap: .5rem;
}
@supports (grid-template-rows: masonry) {
.wp-block-my-plugin-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry;
}
}
/* ── 7. font-tech() — detect variable font support ───────────────────────── */
@supports font-tech(variations) {
body {
font-family: 'MyVariableFont', sans-serif;
font-variation-settings: 'wght' 400, 'wdth' 100;
}
}
NOTE: @supports tests syntax support, not rendering correctness — a browser may parse display: grid without bugs, or it may have a buggy implementation; always test visual output in target browsers rather than relying solely on @supports as a proxy for correct behaviour.