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.
- It performs feature detection in CSS.
- It works with properties, values, and some selector checks.
- It helps you ship modern styles safely while keeping fallbacks available.
- It is commonly used with newer layout features like Grid, backdrop-filter, or custom selectors.
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:
- you want a fallback layout for older browsers,
- you are using a new CSS property that may not be available everywhere,
- you want to avoid duplicate classes or user-agent checks,
- you need the stylesheet itself to decide which styles are safe to use.
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:
- Switching from float-based or block-based layouts to Grid or Flexbox when supported.
- Applying visual polish such as backdrop-filter, subgrid, or advanced color syntax only in capable browsers.
- Guarding experimental or newer properties so fallback styling still looks acceptable.
- Building design systems that need one stylesheet to serve multiple browser generations.
- Creating print or embedded views that must stay readable even when advanced features are missing.
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
- @supports only tells you whether the browser claims to support the feature syntax; it does not guarantee a feature is visually perfect in every situation.
- Some properties may be partially supported, so a positive result does not always mean identical rendering across browsers.
- Browser support for a selector test can be narrower than support for a plain property check.
- Feature queries cannot detect bugs in a browser implementation; they only check support status, not correctness of behavior.
- If a browser ignores the whole @supports rule because it does not understand it, your fallback styles must already exist outside the block.
- Nested @supports rules can become hard to read if you stack too many conditions without clear structure.
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
- @supports applies styles only when a CSS feature is supported.
- It is for feature detection, not screen-size detection.
- Write a solid fallback first, then enhance the experience inside the query.
- Test the exact property or value you depend on.
- Use it to improve compatibility, not to replace browser testing.
11. Practice Exercise
- Create a card layout with a simple block fallback.
- Add an @supports rule that upgrades the layout to Grid.
- Include one optional enhancement such as backdrop-filter or a modern color value.
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.