Developing with progressive enhancement is not only a best practice, it’s necessary given the complexity of today’s websites and applications. We should never be sacrificing accessibility for convenience, so minimizing dependency on CSS3 features is important and, as a last resort, we need some form of feature detection for providing fallbacks.

The most common CSS feature detection methods require JavaScript. Modernizr, for example, is a popular detection library that checks support for CSS3 features (among other things). Some developers also write their own custom JavaScript tests.

Feature detection is about to become more streamlined, as CSS has a powerful new conditional rule that’s recently gained browser support. This is exciting news because we’re able to test the support of any CSS feature and provide the necessary fallbacks directly in the CSS.

The @supports Rule

CSS has built-in graceful degradation; if a browser doesn’t support a property or value, it simply ignores it. And if we include a fallback property that’s supported, nothing breaks. @supports drives this concept further by setting conditions based on browser support.

@supports is like a media query––it’s actually a feature query. A browser tests whether it can use certain properties if supported or ignore them if not. This means that soon we won’t need to rely on JavaScript for feature detection, so it will be easier than ever to use the latest CSS features and set fallback solutions for unsupported browsers.

Syntax

The syntax is similar to a media query, but instead of a media feature, we set a CSS declaration as the condition. The browser executes the styles inside the rule based on that condition.

For instance, the following transition is applied to .box only if the browser supports CSS transitions.

@supports (transition: .5s) {
  .box { transition: .5s; }
}

Operators

Like media queries, @supports has three operators that can test for support in different ways.

not

The not operator checks if the browser has no support for the CSS property. This is especially useful when using new layout features like flexbox because our layout doesn’t have to be chained to flexbox support.

@supports not (display: flex) {
  .box { display: inline-block; width: 25%; }
}

If flexbox is not supported, the browser will apply the inline-block and width values to .box instead.

and

With and, the condition is true only if every expression is true. This way we can test for multiple features at once.

@supports (display: flex) and (transition: .5s) {
  .box { display: flex; transition: .5s; }
}

If flexbox and transitions are supported, the browser will apply the styles defined in the rule. If not, the browser simply ignores the entire rule.

or

With or, the expression is true if one or all the expressions defined are also true. This operator is useful for vendor-prefixed properties.

@supports (-webkit-transform: rotate(-25deg)) or
          (   -moz-transform: rotate(-25deg)) or
          (    -ms-transform: rotate(-25deg)) or
          (        transform: rotate(-25deg)) {
   .box {
     -webkit-transform: rotate(-25deg)
        -moz-transform: rotate(-25deg)
         -ms-transform: rotate(-25deg)
             transform: rotate(-25deg)
   }
}

Final Thoughts

The not operator might be @supports most useful feature because it’s the closest method to using JavaScript for feature detection and fallbacks.

Writing those extra CSS declarations may seem redundant––like code bloat. But keep in mind that feature detection functionality that’s native to the browser will perform faster than JavaScript.

We can start using @supports today, as it works in Chrome 28, Firefox 22, and Opera 12.1. It’s still too soon to abandon Modernizr altogether, but it’s worth noting that they’ve added a new detect that defers to the @supports rule in our style sheet if the browser supports it.