← Back to CSS Course | Chapter 9: Modern CSS Features | Lesson 12 of 25

CSS @supports

Imagine a chef who checks the pantry before committing to a recipe: 'if we have saffron, make the fancy version, otherwise make the regular one.' The kitchen doesn't break just because an ingredient is missing. @supports is that pantry check for CSS -- it lets a stylesheet test whether the browser understands a feature before relying on it.
Syntax
css
@supports (property: value) {
  selector {
    property: value;
  }
}

Basic @supports Syntax

@supports wraps a block of CSS rules in a condition that tests whether the browser can parse and apply a given property-value pair, only applying the enclosed rules if the test passes. The condition itself looks like a real declaration inside parentheses.

Note:
  • Accepted values:
  • @supports (property: value) — tests a declaration
  • @supports not (...) — negates the test
  • @supports (...) and (...) — both must match
  • @supports (...) or (...) — either matches
  • @supports selector(...) — tests selector support

Example: Basic @supports Syntax

css
<style>
.cell { background: #cfe8ff; border: 2px solid #1e6fd9; padding: 10px; }
@supports (display: grid) {
  .container {
    display: grid;
  }
}
</style>
<div class="container"><div class="cell">A</div><div class="cell">B</div><div class="cell">C</div></div>

Combining Conditions with and/or/not

Multiple feature tests can be combined with and (all must pass), or (any must pass), and not (negates a single test), letting a feature query express fairly precise support requirements in one rule.

Note:
  • Accepted values:
  • and — every condition must be supported
  • or — at least one must be supported
  • not — negates one condition
  • ( ) — parentheses are required around each condition
  • mixing and and or — needs extra parentheses

Example: Combining Conditions with and/or/not

css
<style>
p { background: #cfe8ff; border: 2px solid #1e6fd9; margin: 0; padding: 8px; }
@supports (display: grid) and (gap: 1rem) {
  .container {
    display: grid;
    gap: 1rem;
  }
}
</style>
<div class="container"><p>Grid with gap</p><p>Grid with gap</p></div>

The not Operator

Prefixing a condition with not applies the enclosed rules only when the feature is NOT supported, which is the natural way to write a true fallback path for genuinely old browsers.

Note:
  • Accepted values:
  • not (property: value) — matches when unsupported
  • not (A) and (B) — invalid; wrap as not ((A) and (B))
  • @supports not (display: grid) — fallback styles
  • note: the not operand must be wrapped in parentheses

Example: The not Operator

css
<style>
.container { background: #cfe8ff; border: 2px solid #1e6fd9; padding: 10px; }
@supports not (display: grid) {
  .container {
    display: block;
  }
}
</style>
<div class="container">Shown as block where grid is unsupported</div>

Progressive Enhancement Use Case

The standard pattern is to write baseline CSS that works everywhere first, then layer @supports blocks on top to opt modern browsers into enhanced layouts or effects -- rather than writing modern-only CSS and trying to patch old browsers afterward.

Note:
  • Accepted values:
  • default declarations first — the fallback
  • @supports (display: grid) { ... } — enhanced styles
  • @supports (gap: 1rem) — feature-test individual properties
  • later @supports — override the fallback

Example: Progressive Enhancement Use Case

css
<style>
.cell { background: #cfe8ff; border: 2px solid #1e6fd9; padding: 10px; }
.container {
  display: block;
}
@supports (display: flex) {
  .container {
    display: flex;
  }
}
</style>
<div class="container"><span class="cell">A</span><span class="cell">B</span></div>

Testing Selector Support

@supports selector(...) tests whether the browser understands a given CSS selector syntax (not just a property), useful for newer selectors like :has() that have uneven adoption.

Note:
  • Accepted values:
  • @supports selector(:has(a)) — tests :has() support
  • @supports selector(:focus-visible) — tests :focus-visible
  • selector(<selector>) — any single complex selector
  • not selector(...) — negation is allowed

Example: Testing Selector Support

css
<style>
.card { background: #cfe8ff; border: 2px solid #1e6fd9; padding: 12px; margin-bottom: 8px; width: 200px; }
@supports selector(:has(a)) {
  .card:has(a) {
    cursor: pointer;
  }
}
</style>
<div class="card"><a href="#">Card with a link</a></div>
<div class="card">Card without a link</div>
Live Example
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting the parentheses around the condition, as in @supports display: grid, which is invalid.
  2. Forgetting the parentheses around the declaration, as in @supports display: grid, when it must be @supports (display: grid).
  3. Putting the fallback style after the @supports block, where it overrides the modern style.
Browser Support

Supported in all modern browsers: Chrome, Firefox, Safari, Edge and Opera.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.