Categories
MA Web Design and Content Planning

Seminar on @supports

Introduction

Browsers from different vendors and versions have differing levels of support for presentation features in CSS. Prior to 2013, in order to support as many browsers as possible with these differing capabilities, feature detection was carried out using custom JavaScript scripts or third-party libraries, such as Modernizr.

Native support within CSS began in Firefox from version 22 and Chrome from version 28, with the introduction of the @supports feature query. Feature queries are are a family of so-called ‘at-rules’ that include the @media rule, and are commonly used in creating responsive websites and provide a modular way of catering for accessibility on differing platforms and form factors. They are are described as part of the CSS3 Conditional Rules Specification.

Like custom scripts and third-party libraries, they allow the browser to selectively apply rules based on the results of a test. @supports helps the browser to test CSS rules and determine if it can understand them without having to parse the entire declaration block. Similar to the @media rule, it allows the web developer to create CSS declarations that are executed only if supported by the browser, and ignored otherwise.

By implementing feature detection in CSS, site performance is improved by reduced number and size of files to be downloaded, and the computation overhead applying and rendering unnecessary rules. Currently in 2021, availability of the basic conditional syntax stands at around 97%, and for the advanced function syntax it is 72%, on browsers across all platforms.

Syntax

The structure of the ruleset takes the following form:

@supports (property: value) {

    selector { property: value; }
    …

}

The condition to be tested is places inside the brackets. If the test returns a Boolean value of true, the browser continues to parse the declaration block, bound by the curly braces, or ignores it entirely if false. A ruleset may be placed at the top level of the CSS hierarchy, or nested inside any other conditional at-rule; for example, an @media rule:

@media (orientation: landscape) {

    selector { property: value; }
    …

    @supports (property: value) {

        selector { property: value; }
        …

    }
}

Separate conditions can be conjoined using the standard logical operators, AND, OR, and NOT; for example:

@supports not (property: value) { … }

@supports (property: value) and (property: value) { … }

@supports (property: value) or (property: value) { … }

When mixing logical operators, it is necessary to add levels of parentheses to make the precedence of computation clear; for example:

@supports ((property: value) and (property: value))
                              or (property: value) { … }

Another syntax is available in the form of a function to test whether combinator or pseudo selectors are supported (>, :first-child,…) or even custom properties; for example:

@supports selector(A > B) { … }

@supports selector(::-webkit-scrollbar-thumb) { … }

Using these tools, developers can place more widely supported features first in the CSS cascade as fallbacks, and then follow with the less reliably supported features; for example:

/* float rules */
selector { property: value; }
…

/* flex rules */
@supports (display: flex) { … }

/* grid rules */
@supports (display: grid) { … }

In the example, the browser will continue through the CSS applying each block of declarations in turn as long as each test passes. Each layout rendered will be overridden by the last and many declarations will have to be reversed from the previous settings. Logical operators can alleviate this waste of processing resources by only executing a fallback if the more advanced feature is not supported; for example:

/* float rules */
@supports not (display: flex) and not (display: grid) { … }

/* flex rules */
@supports (display: flex) and not (display: grid) { … }

/* grid rules */
@supports (display: grid) { … }

Vendor Prefixes

Any discussion of whether the @supports rule renders the vendor prefixes obsolete must begin with a short introduction for clarity. Vendor prefixes are experimental or non-standard CSS properties and JavaScript APIs. They are used during the development and finalisation phases of new a feature implementation. They are differentiated from standard release versions of the same properties by a prefix associated to a specific browser technology, such as Firefox (-moz-) and Chrome (-webkit-).

Best practice is to list the vendor prefixed declarations first, followed by the standardised W3C release version. This ordering ensures that the release version takes precedence over the experimental version, preventing any differences between the two from causing the release version to be superseded in use. For example:

div.example {
    -webkit-border-radius: 6px;
    -moz-border-radius: 6px;
    -ms-border-radius: 6px;
    -o-border-radius: 6px;
    border-radius: 6px;
}

These properties were popularised during the rise of CSS3, and introduced to test features that are in development, or not officially supported and not guaranteed to be released as standard. However, different properties were implemented in browsers at different times, often with different interpretations of the specification, and impatient web developers began using them in production. By 2012, another problem caused by WebKit, which dominated the mobile space initially, exacerbated the situation when they began pioneering non-standard prefixed properties.

This forced other vendors to support WebKit prefixes or be out of step with the state of the art. The W3C itself considered this a solution but abandoned the notion since it was a threat to open web standards if vendor prefixed properties became standardised. Sensibly, WebKit began moving away from prefixed properties after 2016 and vendors are now considering discontinuing them entirely.

Conclusion

While vendor prefixes were a logical solution to the problem of keeping presentation features both stable but also able to evolve, they also became cumbersome and inconvenient for developers, making style sheets excessively complex and difficult to maintain over the lifecycle of a project. While tools such as AutoPrefixer reduced the work by automatically inserting the required prefixed properties, they could not solve the fundamental issues, such as the inability to discontinue their use once a feature was standardised due to disparity in implementation.

The introduction of the @supports rule does not directly offer a solution to the problems of vendor prefixes because they serve very different purposes, but it does allow better management of them. They can be used together to test if the final implementation of a feature is complete, and if not, apply the prefixed versions. This is most useful for maintainability when there are many prefixes which can be grouped and isolated for future removal.

/* assumed supported */
    div.example {
    border-radius: 6px;
}
 
/* vendor fallbacks */
@supports not (border-radius) {
    div.example {
        -webkit-border-radius: 6px;
        -moz-border-radius: 6px;
        -ms-border-radius: 6px;
        -o-border-radius: 6px;
    }
}

References

https://www.w3.org/TR/css-conditional-3/#at-supports

https://developer.mozilla.org/en-US/docs/Web/CSS/@supports

https://css-tricks.com/how-supports-works

https://css-tricks.com/tag/supports

https://davidwalsh.name/css-supports

https://www.educba.com/css-supports

https://modernizr.com

https://autoprefixer.github.io

https://www.lifewire.com/css-vendor-prefixes-3466867

https://www.sitepoint.com/w3c-css-webkit-prefix-crisis

https://www.futurehosting.com/blog/webkits-developers-want-to-move-away-from-css-vendor-prefixes