Using feature queries
Feature queries are conditional group rules that test whether the user agent supports or doesn't support one or more CSS features, such as CSS properties and property values. Feature queries give web developers a way to test to see if a browser has support for a certain feature, and then provide CSS that will only run based on the result of that test. In this guide, you will learn how to implement progressive enhancement using feature queries.
Feature queries are created using the CSS at-rule @supports
(or the supports()
function within @import
at-rules).
Syntax
CSS feature queries are part of the CSS conditional rules module, which also defines the media query @media
at-rule. Feature queries behave similarly to media queries. The difference is that with a media query, you are testing something about the environment in which the web page is running, whereas with feature queries you are testing browser support for CSS features.
A feature query consists of the @supports
at-rule followed by the support condition or a supports()
function and declaration parameter within an @import
at-rule declaration:
/* `@supports` at-rule */
@supports <support-condition> {
CSS rules to apply
}
/* `supports()` function */
@import url_to_import supports(<declaration>);
For example, we can apply a set of styles or import an entire stylesheet if the user-agent supports red
as a valid value for the CSS color
property:
/* `@supports` at-rule */
@supports (color: red) {
CSS rules to apply
}
/* `supports()` function */
@import `/css/styles.css` supports(color: red);
As another example, if you want to check if a browser supports the row-gap
property you would write the following feature query. It doesn't matter which value you use in a lot of cases: if all you want is to check that the browser supports this property, then any valid value will do.
<div class="box">
If your browser supports the row-gap property, the border will be dashed and
text will be red.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (row-gap: 10px) {
.box {
border: 4px dashed darkgreen;
color: red;
}
}
The value part of the property-value pair matters more if you are testing for new values of a particular property. All browsers support color: red
: this dates back to CSS1. However, there are often additional values added to properties in CSS, like relative colors, which may not be supported. Feature queries enable testing property and value pairs, meaning we can detect support for values.
Expanding on the color
property example above, here we check if the browser supports the color: AccentColor
declaration:
/* `@supports` at-rule */
@supports (color: AccentColor) {
CSS rules to apply
}
/* `supports()` function */
@import `/css/styles.css` supports(color: AccentColor);
In these examples, we've used feature queries to check if the user-agent supports a specific value of a CSS property, listing the single declaration within parenthesis. You can test for multiple property values or the lack of support.
Testing for lack of support
In addition to asking the browser if it supports a feature, you can test for the opposite by adding in the not
keyword:
/* `@supports` at-rule with `not` */
@supports not (property: value) {
CSS rules to apply
}
The CSS inside the following example feature query will run if the browser does not support row-gap
.
<div class="box">
If your browser does not support row-gap, the content will be darkgreen with a
dashed border.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports not (row-gap: 10px) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
Testing for more than one feature
You may need to test support for more than one property in your feature query. To do so, you can include a list of features to test for, separated by and
keywords:
/* multiple feature `@supports` at-rule */
@supports (property1: value) and (property2: value) {
CSS rules to apply
}
For example, if the CSS you want to run requires that the browser supports CSS Shapes and CSS grid, you could create a rule that tests browser support for both of these features. The following rule will only return true if shape-outside: circle()
and display: grid
are both supported by the browser.
<div class="box">
If your browser supports <code>display: grid</code> and
<code>shape-outside: circle()</code>, the content will be darkgreen with a
dashed border.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (display: grid) and (shape-outside: circle()) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
Testing for at least one of multiple features
You can also use or
to apply CSS only if one or more declarations are supported:
/* any feature `@supports` at-rule */
@supports (property1: value) or (property2: value) {
CSS rules to apply
}
This can be particularly useful if a feature is vendor prefixed, as you can test for the standard property plus any vendor prefixes.
<div class="box">
The text and border will be green if your browser supports font smoothing.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (font-smooth: always) or (-webkit-font-smoothing: antialiased) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
Additional feature query options
Feature queries are not limited to property-value pairs. You can include font-tech()
, font-format()
, and selector()
functions in your feature queries to selectively apply CSS based on whether the user-agent supports a specified font technology, font format, or selector syntax, respectively.
For example, the selector()
function can be used to import a stylesheet for browsers that support a vendor-prefixed pseudo-element:
/* A `selector()` query within a `supports()` function */
@import `/css/webkitShadowStyles.css`
supports(selector(::-webkit-inner-spin-button));
Examples
Browser support test
In this example, we check if the browser supports the AccentColor
<system-color>
and use display: none
to change the default "not supported" message to a "supported" message if the color type is supported.
HTML
<p class="accentcolor">
Your browser does <span>not</span> support <code>AccentColor</code> as a color
value.
</p>
CSS
body {
font: 1.2em / 1.5 sans-serif;
}
p {
padding: 1em;
}
@supports (color: AccentColor) {
p {
color: green;
border: 2px solid;
}
span {
display: none;
}
}
@supports not (color: AccentColor) {
p {
color: red;
}
}
Result
Limitations of feature queries
The @supports
rule tests to see if browsers can parse one or more property/value pairs, and therefore if they claim to support the associated feature(s). If the property/value pairs are understood by a browser it returns a positive response. Feature queries check that declarations are considered valid by a browser, but can't be used to check if it supports a feature properly without bugs or spec violations. Feature queries cannot test for partial implementations.
Summary
Feature queries are a useful tool for progressively enhancing a site. They enable you to provide a good solution for all browsers, and an enhanced solution for browsers that support newer properties and values.
You don't need to use feature queries to start using new CSS features; CSS error handling means the browser simply ignores CSS it does not yet recognize. However, feature queries are a useful alternative to fallback declarations, and enable writing code once that can eventually be supported everywhere.