CSS @supports: How to Check Browser Support
Use CSS @supports to test whether a browser accepts a feature, then layer enhancements over a reliable fallback.
Use CSS @supports to ask whether a browser accepts a particular CSS declaration or selector, then apply styles that depend on it. Put a usable baseline first and the enhancement inside the feature query. A successful check means the browser accepts the tested syntax; it does not guarantee that the feature behaves correctly in every case.
Basic syntax and progressive enhancement
A feature query takes a condition in parentheses and a block of CSS. It can appear at the top level of a stylesheet or inside another conditional group rule.
.card {
display: block;
}
@supports (display: grid) {
.card-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
}
The ordinary rule is the fallback. Browsers that accept the query apply the enhanced rule; browsers that do not accept it ignore the block. The fallback should still let people use the page.
Test the value your implementation actually needs. For example, checking that a property accepts a familiar value does not prove that it accepts a newer value used by your design.
Choose the condition that matches the feature
| Need | Pattern | Meaning |
|---|---|---|
| Check a property and value | @supports (display: grid) |
The browser accepts this declaration. |
| Require multiple declarations | @supports (display: grid) and (gap: 1rem) |
Every condition must pass. |
| Allow alternatives | @supports (text-stroke: 1px) or (-webkit-text-stroke: 1px) |
At least one condition must pass. |
| Target browsers that do not accept a declaration | @supports not (display: grid) |
The condition must fail. |
| Check selector syntax | @supports selector(:has(article)) |
The browser accepts the selector syntax. |
Use parentheses to group expressions when combining conditions. For example, @supports ((display: grid) and (gap: 1rem)) or (display: flex) accepts either the complete grid requirement or flexbox.
Property and value checks
Write a declaration inside parentheses, including the value your CSS relies on:
@supports (color: color(display-p3 1 0 0)) {
.brand-mark {
color: color(display-p3 1 0 0);
}
}
This asks whether the browser accepts that specific declaration. The condition is not a visual or quality test. If a feature has implementation quirks, validate the result in the browsers and devices that matter to your site.
Selector checks
Use selector() when the thing you need to test is selector syntax, such as :has():
.card-list {
/* Baseline styles */
}
@supports selector(:has(a)) {
.card:has(a) {
outline: 2px solid currentColor;
}
}
The argument is a selector. A selector query is not a replacement for a property query: choose the form that tests the syntax your enhanced rule depends on.
Other supported query forms
The MDN @supports reference also documents queries for at-rules and font technology or format. Consult the reference for the exact syntax for the feature you need, and use an ordinary fallback for browsers that do not accept it.
Use @supports not for a targeted fallback
A negative query can apply a special treatment only when a capability is missing:
.layout {
display: block;
}
@supports not (display: grid) {
.layout {
/* Add a fallback-specific adjustment if the baseline needs one. */
}
}
Prefer a good baseline that works without the feature. Use not when unsupported browsers need an actual alternative or adjustment, rather than adding a query that has no practical effect.
Check support from JavaScript
When application logic needs the same kind of CSS support condition, use CSS.supports():
if (CSS.supports("display", "grid")) {
document.documentElement.classList.add("supports-grid");
}
if (CSS.supports("selector(:has(a))")) {
document.documentElement.classList.add("supports-has");
}
The API can test a property and value or a support condition. Check its dedicated compatibility information before making browser-version assumptions; a feature query reports syntax acceptance, not whether every implementation detail is free of bugs.
Practical workflow
- Build the baseline. Write styles that keep the content usable without the new feature.
- Identify the dependency. Decide whether you need to test a declaration, selector, at-rule, or font capability.
- Test the precise syntax. Include the actual value or selector used by the enhancement.
- Layer the enhancement. Put only dependent declarations inside
@supports. - Exercise both paths. Check a browser that accepts the feature and one that does not, especially for high-impact layout or interaction.
- Check behavior, not just parsing. Test real pages at relevant viewport sizes and with the content your design encounters.
Limits and edge cases
- Acceptance is not correctness. A true result says the browser accepts the tested syntax. It does not establish that the feature works as intended in every situation.
- Partial support can matter. A browser may accept syntax while still having behavior differences or bugs. Real-browser testing is needed where those differences affect users.
- Test the exact value. A check for a property with one value does not establish support for a different, newer value.
- Do not query every new declaration by habit. Browsers generally ignore CSS they do not recognize. Queries are most useful when they enable a clear enhancement or a necessary fallback.
- Keep the fallback independent. If the baseline itself depends on the queried feature, the unsupported path may still fail.
- Use the correct query type. Test selector syntax with
selector(); test a property value with a declaration condition.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The enhanced rule never applies. | The declaration is unsupported, misspelled, or has a value the browser does not accept. | Check the exact property-value pair and inspect the browser’s computed styles. |
| The query passes but the page looks wrong. | The browser accepts the syntax but has a behavior difference, or the layout has another constraint. | Test the feature in the affected browser and adjust the enhancement or add a targeted workaround. |
| A selector query does not match the expected result. | The selector syntax check and the selector’s matching behavior are different questions. | Verify the selector itself against the document structure and test the resulting styles in a real browser. |
| A browser uses an unwanted fallback despite partial support. | The query checks syntax acceptance, not implementation completeness or quality. | Keep the baseline safe and use browser testing to decide whether a targeted workaround is needed. |
| JavaScript throws while checking support. | The code runs where CSS or CSS.supports is unavailable. |
Guard the call with if (window.CSS && CSS.supports) when that environment is in scope. |
Performance and reliability
@supports is a stylesheet condition, not a network request or browser-version lookup. Keep queries focused on real dependencies and avoid duplicating large blocks of styles across supported and unsupported paths. A clean baseline makes the page more resilient when a feature is absent or has unexpected behavior.
For high-impact features, validate both paths in the browsers and devices relevant to your users. The query cannot reveal runtime errors, visual defects, accessibility problems, or whether a browser’s implementation matches every assumption in your design.
Or skip the browser setup
If you need screenshots of the result across pages or as part of a capture workflow, ScreenshotNeo is a website screenshot API and MCP server. Its capture options include viewport and device presets, full-page capture, custom CSS and JavaScript, and waiting for a selector, delay, or network idle. A screenshot can help inspect rendered output; it does not replace checking CSS support in the target browser.
One GET request returns an image or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page info, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for 1,000 screenshots a month, with no card.
FAQ
Does @supports detect the browser version?
No. It tests whether the user agent accepts the condition you wrote, not which version it is or whether all behavior is correct.
Should every modern CSS rule be wrapped in @supports?
No. Use it when the result enables a useful progressive enhancement or a meaningful unsupported-feature path. A sound baseline often suffices because unrecognized CSS is generally ignored.
Can I check whether a browser supports a CSS selector?
Yes. Use a selector condition such as @supports selector(:has(article)), then test the selector’s behavior in the actual document as needed.


