Photograph of grid trusswork against a blue sky

A front-end developer’s working life is often characterized by the question “what does this work in?” Gauging what CSS is supported in which browsers, and creating elegant fallbacks when they are not, can often consume the majority of development hours. Feature detection tools like Modernizr help immensely, but they don’t cover every case, and consist of yet more JavaScript that must be loaded and run.

Ideally, we would have a native method for checking what CSS is supported in any browser, enabling us to target features directly. With @supports and the JavaScript CSS.supports() method, we have exactly that. In this article, I’ll concentrate on the CSS rule, including a JavaScript solution for non-supportive browsers.

Feature Detection Syntax

As an @-rule, @supports are written just like @mediaqueries, and may be considered a form of CSS “if” statement. For example, to determine if a browser supports vw units:

@supports ( width: 75vw ) {
      /* rules for CSS browsers with vw unit support */
}

Note that the condition inside the parentheses must be a valid CSS property: value combination; @supports (vw) { } won’t work. You can also write @supports in or tests. This is useful (and necessary) if you want to detect support for properties or values that still require vendor prefixes:

@supports ( display: -webkit-flex ) or ( display: -ms-flex ) or ( display: flex ) {
      /* code for browsers with flexbox support */
}

You can also use and to add conditions. For example, to test if a browser supports both the CSS currentColor variable and HSL color:

@supports ( color: currentColor ) and ( color: hsl(0,50%,30%) {
	/* code for browsers with currentColor and hsl support */
}

Again, it doesn’t particularly matter what the CSS values are, so long as they are valid. Finally – and probably least effective, right now – is the not condition:

@supports not (display: inline-grid) {
      /* CSS browsers that do not support CSS grids */
}

and, or, and not may also be used in complex combinations.

Support

@supports is supported in Firefox, Chrome, Android (4.4+) and Opera, but not yet Safari or Internet Explorer. That creates something of a paradox: @supports would appear to be only useful in browsers that already support it, leaving the rest out in the cold. That’s not entirely true, as we’ll see in a moment.

Using @supports with non-supporting browsers

Despite appearances, it is possible to use @supports indirectly in browsers that don’t yet support the CSS rule or JavaScript method. A recent comment on my article about using Open Type true small caps pointed out a useful example of a problem that should be addressed with @supports: it’s not possible to mix-and-match font-variant: small-caps with OpenType, meaning that browsers that fail to support the latter will display text lowercase. This can be treated with a clever use of @supports in a method reminiscent of the “Triggering JavaScript Actions with CSS Media Queries” article I wrote recently, using the wonderful Questa Grande typeface by Martin Majoor & Jos Buivenga:

@font-face {
      font-family: Questa Grande;
      src: url(Questa_Grande_Regular.otf) ;
}
p {
	font-family: Questa Grande, sans-serif;
	font-size: 2rem;
}
@supports ( -moz-font-feature-settings: "smcp=1" )
or ( -moz-font-feature-settings: "smcp" ) 
or ( -ms-font-feature-settings: "smcp" ) 
or ( -o-font-feature-settings: "smcp" )
or ( -webkit-font-feature-settings: "smcp" ) 
or ( font-feature-settings:"smcp") {  
      p:first-of-type:first-line {
            -moz-font-feature-settings: "smcp=1";
            -moz-font-feature-settings: "smcp";
            -ms-font-feature-settings: "smcp";
            -o-font-feature-settings: "smcp";
            -webkit-font-feature-settings: "smcp";
            font-feature-settings:"smcp";
      }
      body:before {
		content: 'small-caps';
		display: none;
		}
}

Meaning: if the browser supports OpenType, write the CSS to support true small-caps for the first line of the first paragraph, and add the hidden content of “small-caps” to a pseudo-element in the <body>. This becomes our “message” to JavaScript, which is added to the bottom of the page:

var smallcaps = window.getComputedStyle(document.body, ':before' ).content;
if (smallcaps!== "small-caps") {
	var css = document.createElement('style');
	var styles = 'p:first-of-type:first-line { font-variant: small-caps }';
	if (css.styleSheet) {
		css.styleSheet.cssText = styles;
	} else {
		css.appendChild(document.createTextNode(styles))
	}
document.getElementsByTagName("head")[0].appendChild(css);
}

The script looks for this hidden text: if it doesn’t find it, the script writes a CSS fallback for the paragraph and adds it to the <head> of the document, so that the first-line is rendered in classic small-caps for browsers that don’t support OpenType features.

The result, using the opening line of Mervyn Peake's Gormenghast trilogy:

Gormenghast, that is, the main massing of the original stone, taken by itself would have displayed a certain ponderous architectural quality were it possible to have ignored the circumfusion of those mean dwellings that swarmed like an epidemic around its outer walls.

Conclusion

While there’s an argument that @supports and its JavaScript equivalent won’t be truly up to CSS feature detection until every browser supports it, as I’ve shown there are ways of using it for every browser: you could easily use the same technique to slip other statements inside the <body> pseudo-element to be checked by JavaScript, or employ the classic technique of adding multiple classes indicating support for the features you wish to test for, just as Modernizr does, using native JavaScript and CSS.

Photograph by Thomas Lieser, licensed under Creative Commons.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.