CSS @supports and Feature Queries: Progressive Enhancement Guide

CSS @supports lets you apply styles only when a browser understands a particular CSS feature. It is one of the most practical tools for progressive enhancement because it helps you write modern styles without breaking older browsers.

Quick answer: Use @supports when you want a feature-based fallback, not a screen-size fallback. It checks whether the browser recognizes a property, value, or selector pattern before applying the enclosed rules.

Difficulty: Beginner to Intermediate

You'll understand this better if you know: basic CSS rule syntax, how selectors and declarations work, and the difference between browser support and layout breakpoints.

1. What Is CSS @supports?

@supports is a CSS at-rule that lets you ask the browser, “Do you support this feature?” If the answer is yes, the browser applies the styles inside the block. If not, it skips them.

This is different from media queries, which check the device or viewport, not whether a CSS feature exists.

2. Why @supports Matters

Browsers do not all gain new CSS features at the same time, and some features may be partially supported. Without feature queries, a modern declaration might be ignored silently or may create an inconsistent experience. @supports gives you a clean way to layer advanced styling on top of safe defaults.

That matters when:

3. Basic Syntax or Core Idea

The basic form is simple: write a condition inside @supports, then place the supported styles in the block.

Minimal example

This example checks for display: grid. If the browser supports Grid, it uses the Grid layout rules.

@supports (display: grid) {
  .layout {
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 1rem;
  }
}

The condition tests a feature, and the browser only applies the styles inside the block when that test succeeds.

Using a fallback first

The most reliable pattern is to write a safe default first, then enhance it with @supports.

.layout {
  display: block;
}

@supports (display: grid) {
  .layout {
    display: grid;
  }
}

Browsers that do not support Grid keep the block layout, while modern browsers upgrade to Grid.

4. Step-by-Step Examples

Example 1: Enhancing layout with CSS Grid

Start with a flexible fallback, then switch to Grid when available. This is a common real-world use of feature queries.

.cards {
  display: block;
}

.card {
  margin-bottom: 1rem;
}

@supports (display: grid) {
  .cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem;
  }

  .card {
    margin-bottom: 0;
  }
}

This approach prevents older browsers from getting a broken grid-only layout.

Example 2: Supporting modern color functions

Some browsers support newer color syntax before others. You can use @supports to introduce the modern version safely.

.button {
  background-color: #1d4ed8;
  color: #fff;
}

@supports (color: oklch(0.6 0.15 250)) {
  .button {
    background-color: oklch(0.55 0.18 250);
  }
}

The fallback color remains available, and newer browsers get the improved color expression.

Example 3: Checking support for a property value

Feature queries can test more than a property name. They can test whether a specific value is accepted.

.panel {
  padding: 1rem;
  border-radius: 0.5rem;
}

@supports (backdrop-filter: blur(10px)) {
  .panel {
    backdrop-filter: blur(10px);
    background-color: rgba(255, 255, 255, 0.65);
  }
}

Using a value test helps when a property exists but only certain values are actually useful for your design.

Example 4: Combining conditions

You can combine checks with and, or, and not. That makes it possible to target more specific support scenarios.

@supports (display: grid) and (gap: 1rem) {
  .layout {
    display: grid;
    gap: 1rem;
  }
}

@supports not (display: grid) {
  .layout {
    display: block;
  }
}

Logical operators let you build precise conditions for advanced compatibility strategies.

5. Practical Use Cases

Feature queries are especially useful in projects that mix stable baseline styles with newer enhancements. Common situations include:

6. Common Mistakes

Mistake 1: Using @supports like a media query

@supports checks feature support, not viewport width or device type. Developers sometimes expect it to react to screen size, but it will not.

Problem: This code tries to show a two-column layout only on wide screens, but it incorrectly uses a feature query instead of a media query.

@supports (min-width: 900px) {
  .layout {
    display: grid;
  }
}

Fix: Use a media query for viewport conditions, and reserve @supports for feature checks.

@media (min-width: 900px) {
  .layout {
    display: grid;
  }
}

The corrected version works because it uses the right CSS tool for the job.

Mistake 2: Forgetting the fallback styles

If you place all styling inside @supports, unsupported browsers may end up with a poor or incomplete experience.

Problem: The base layout is missing, so browsers that do not pass the feature query see almost no usable structure.

@supports (display: grid) {
  .cards {
    display: grid;
    gap: 1rem;
  }
}

Fix: Write a simple baseline first, then enhance it inside the feature query.

.cards {
  display: block;
}

@supports (display: grid) {
  .cards {
    display: grid;
    gap: 1rem;
  }
}

The fallback ensures the content remains usable everywhere.

Mistake 3: Testing only a property name when the value matters

Sometimes the browser supports a property in general, but not the exact value you want to use. Testing only the property can lead to misleading results.

Problem: This code assumes any support for color means the browser supports the modern color function being used.

@supports (color: red) {
  .badge {
    color: oklch(0.7 0.2 200);
  }
}

Fix: Test the exact feature you want to use, including the value syntax when needed.

@supports (color: oklch(0.7 0.2 200)) {
  .badge {
    color: oklch(0.7 0.2 200);
  }
}

The corrected version checks the exact capability that the style depends on.

7. Best Practices

Practice 1: Start with a working baseline

Always make the default experience usable without @supports. Then layer enhancements on top. This keeps the page readable even in older browsers.

.toolbar {
  display: block;
}

@supports (display: flex) {
  .toolbar {
    display: flex;
    justify-content: space-between;
  }
}

This pattern reduces the risk of a completely broken layout when the feature is unavailable.

Practice 2: Keep conditions specific

Use the narrowest useful condition. A specific query is easier to maintain than a broad one that accidentally matches too much.

@supports (display: grid) {
  .gallery {
    display: grid;
  }
}

Specific checks make it clear exactly which feature unlocks the enhancement.

Practice 3: Use feature queries for CSS, not for business logic

@supports should decide styling only. It is not a substitute for product decisions, access control, or application behavior.

@supports (backdrop-filter: blur(10px)) {
  .modal {
    backdrop-filter: blur(10px);
  }
}

That keeps feature detection where it belongs: inside the stylesheet.

8. Limitations and Edge Cases

Note: If you need broad compatibility, test real browsers and devices. Feature queries reduce risk, but they do not replace compatibility testing.

9. Practical Mini Project

Here is a small card component that starts with a simple stacked layout and upgrades to a more modern layout when Grid is available.

.product-list {
  display: block;
}

.product-card {
  padding: 1rem;
  margin-bottom: 1rem;
  border: 1px solid #d1d5db;
  border-radius: 0.5rem;
}

@supports (display: grid) {
  .product-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
  }

  .product-card {
    margin-bottom: 0;
  }
}

This example is complete enough to copy into a real stylesheet. Older browsers get a plain stacked list, while newer browsers get a responsive Grid layout.

10. Key Points

11. Practice Exercise

Expected output: In browsers that support the tested features, the cards should be arranged in columns with extra styling. In older browsers, the cards should still stack vertically and remain readable.

Hint: Put the fallback styles outside @supports, and only place the modern enhancement inside the feature query.

Solution:

.demo-cards {
  display: block;
}

.demo-card {
  padding: 1rem;
  margin-bottom: 1rem;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
}

@supports (display: grid) {
  .demo-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem;
  }

  .demo-card {
    margin-bottom: 0;
  }
}

This solution works because the fallback is usable on its own, and the enhanced version only replaces it when the browser can handle the new layout.

12. Final Summary

CSS @supports is a feature query mechanism that helps you write safer, more resilient styles. It lets you detect whether a browser supports a property, value, or selector pattern before applying enhanced rules.

Use it to layer modern CSS on top of a dependable fallback. That way, older browsers still get a usable experience, and newer browsers can take advantage of richer layout and visual features.

As you keep building with modern CSS, pair @supports with real browser testing and clear fallbacks. The combination gives you progressive enhancement that is practical, maintainable, and predictable.