- Home
- /
- Tutorials
- /
- CSS Tutorial
- /
- Feature Queries
CSS Responsive Design
Feature Queries
Apply progressive enhancements only when the browser understands a required CSS declaration or selector. It covers declaration queries, selector queries, combined conditions, progressive enhancement, and the limits of syntax-based support detection.
The @supports rule asks whether a browser understands a CSS declaration or selector. It allows an enhanced rule set to sit above a working baseline without browser sniffing.
Feature queries are most valuable when an unsupported enhancement would otherwise invalidate a layout or interaction. They do not prove that an implementation has no bugs, and they cannot replace testing. Their job is narrower: select a branch of CSS according to syntax the browser says it can parse. This topic also connects with Container Queries and Nesting & Scope.
Test a feature, not a browser
Example
css
.layout { display: block; }
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 16rem 1fr;
}
}
@supports selector(:has(*)) {
.field:has(:invalid) { border-color: #b91c1c; }
}A declaration query checks whether the browser parses a property-value pair. It does not prove every edge case is bug-free.
Combine conditions
Example
css
@supports (text-wrap: balance) and (font-size: 1cqi) {
.card-title {
text-wrap: balance;
font-size: clamp(1.25rem, 4cqi, 2rem);
}
}Feature Query Syntax
| Condition | Tests | Example |
|---|---|---|
| Declaration | Whether a property-value pair parses. | @supports (display: grid) |
| selector() | Whether a selector parses. | @supports selector(:has(*)) |
| not | The inverse of a support condition. | @supports not (display: grid) |
| and / or | A combination of support conditions. | @supports (a: b) and (c: d) |
Complete Example
Run the complete document below in the Try It editor. Resize the preview or interact with the controls where the example calls for it.
Complete runnable example
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Feature Queries example</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 2rem;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
main { max-width: 70rem; margin-inline: auto; }
article, section, aside, form, .card, .panel {
padding: 1rem;
border: 1px solid #cbd5e1;
border-radius: 0.6rem;
}
.layout { display: block; }
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 16rem 1fr;
}
}
@supports selector(:has(*)) {
.field:has(:invalid) { border-color: #b91c1c; }
}
@supports (text-wrap: balance) and (font-size: 1cqi) {
.card-title {
text-wrap: balance;
font-size: clamp(1.25rem, 4cqi, 2rem);
}
}
</style>
</head>
<body>
<main class="layout"><label class="field">Required email <input type="email" required value="not-an-email"></label><article><h1>Progressive enhancement</h1><p>Grid and :has() styles are added only when supported.</p></article></main>
</body>
</html>Browser Support
Feature | Chrome | Edge | Firefox | Safari |
|---|---|---|---|---|
| @supports | 28 | 12 | 22 | 9 |
Versions show the first stable desktop release with unprefixed support. Data source: MDN Browser Compatibility Data 8.0.8. A partial-support note is included where it changes how the example behaves.
Notes
- Write a sound baseline first. A feature query should enhance that baseline, not hide essential content from older browsers.
- Test the example with real content, keyboard input, browser zoom, and the narrowest layout your project supports.
- Use a fallback when the support table shows that one of your required browsers predates the feature.
Conclusion
A sound baseline plus a targeted @supports enhancement is safer than browser detection.
Place essential content and a usable layout outside the query, then add the smallest enhancement inside it. If support is already universal across the project’s browser range, remove the obsolete query rather than keeping permanent scaffolding around ordinary CSS.
