New CSS features can improve a component while older browsers still need a readable base style. A feature query with @supports lets the stylesheet apply an enhancement only when the browser understands the tested declaration.
The fallback-first pattern keeps the normal rule outside the feature query, then places the newer declaration in a later @supports block. That order matters because unsupported browsers ignore the whole block, while supported browsers read the later rule and let it override the fallback through normal source order.
Feature queries test syntax support, not every rendering bug or design requirement. Check the exact property-value pair with CSS.supports(), verify the computed style after the page renders, and force the query to fail in a local test copy before relying on the fallback path.
Use the exact syntax the enhancement depends on, such as (aspect-ratio: 1 / 1) or selector(.card:has(.badge)). A broad property check can pass even when the specific value or selector form you need is not accepted.
<article class="feature-card"> <div class="feature-card__media" aria-hidden="true"></div> <div class="feature-card__body"> <p class="feature-card__eyebrow">Workshop</p> <h2>Layout fallback</h2> <p> The media block has a fixed fallback height before the aspect-ratio enhancement is applied. </p> </div> </article>
.feature-card { display: flex; align-items: flex-start; gap: 1rem; max-inline-size: 36rem; padding: 1rem; border: 1px solid #cbd5e1; border-radius: 1rem; background: #ffffff; color: #1e293b; } .feature-card__media { flex: 0 0 clamp(7rem, 28vw, 9rem); inline-size: clamp(7rem, 28vw, 9rem); block-size: 8rem; border-radius: 0.875rem; background: linear-gradient(135deg, #2563eb, #0f766e); } .feature-card__body { min-inline-size: 0; }
The fallback fixes the media block height so the card still has a predictable shape when aspect-ratio is not supported.
@supports (aspect-ratio: 1 / 1) { .feature-card__media { block-size: auto; aspect-ratio: 1 / 1; } }
Keep the enhancement after the fallback. Supported browsers read both rules, and the later declaration changes block-size from the fixed fallback to auto.
CSS.supports("aspect-ratio", "1 / 1") true
A false result means the browser keeps the fallback CSS because the @supports block is ignored.
const media = document.querySelector(".feature-card__media"); const style = getComputedStyle(media); style.aspectRatio "1 / 1" style.blockSize "144px"
@supports (aspect-ratio: not-a-ratio) { .feature-card__media { block-size: auto; aspect-ratio: 1 / 1; } }
Use an impossible value only while testing the fallback path. Do not commit the forced-false condition.
CSS.supports("aspect-ratio", "not-a-ratio") false getComputedStyle(document.querySelector(".feature-card__media")).aspectRatio "auto" getComputedStyle(document.querySelector(".feature-card__media")).blockSize "128px"
@supports (aspect-ratio: 1 / 1) { .feature-card__media { block-size: auto; aspect-ratio: 1 / 1; } }