# `@supports` ``: Draft 2.1 *([Issue](https://github.com/sass/sass/issues/2780), [Changelog](supports-general.changes.md))* This proposal defines how Sass parses supports queries that use the [``] production. [``]: https://drafts.csswg.org/mediaqueries-4/#typedef-general-enclosed ## Table of Contents * [Background](#background) * [Summary](#summary) * [Design Decisions](#design-decisions) * [How Much To Support](#how-much-to-support) * [SassScript Injection](#sassscript-injection) * [Backwards Compatibility](#backwards-compatibility) * [Syntax](#syntax) * [`SupportsCondition`](#supportscondition) * [`InterpolatedAnyValue`](#interpolatedanyvalue) ## Background > This section is non-normative. Historically, Sass has supported the `@supports` condition syntax as defined in [the April 2013 CSS Conditional Rules Level 3 Candidate Recommendation], with the addition of supporting raw SassScript expressions in the declaration syntax. The [Editor's Draft] (as published October 2019) expands this syntax with a `` production to ensure parser forwards-compatibility with future CSS, which Sass does not yet support. The [Level 4 Editor's Draft] (as of March 2019) further adds a `selector()` function syntax (which is covered by ``), which is [supported by Firefox] as of version 69. [the April 2013 CSS Conditional Rules Level 3 Candidate Recommendation]: https://www.w3.org/TR/2013/CR-css3-conditional-20130404/#at-supports [Editor's Draft]: https://drafts.csswg.org/css-conditional-3/#at-supports [Level 4 Editor's Draft]: https://drafts.csswg.org/css-conditional-4/#at-supports-ext [supported by Firefox]: https://caniuse.com/#search=supports%20selector Sass's current `@supports` rule syntax allows SassScript expressions in place of either the declaration name or value in ``. This syntax doesn't need to be wrapped in interpolation, which means that many expressions that would be parsed in CSS as `` are currently parsed by Sass as ``, and even more expressions need arbitrary look-ahead to determine whether they have a `:` that would distinguish them between `` and ``. ## Summary > This section is non-normative. Sass will parse the `` production essentially as defined by CSS, with the following exceptions: * SassScript can be injected in the productions using interpolation. * The `( )` syntax for `` may not contain top-level `":"` tokens. This preserves backwards-compatibility with existing `@supports` rules that use non-interpolated SassScript expressions in `` and may help catch accidental syntax errors. ### Design Decisions #### How Much To Support Per Sass's CSS compatibility policy, it must add support for at least the `selector()` function, since it's been shipped in a browser. Everything else under `` is optional, though, since the spec says "Authors must not use `` in their stylesheets." We could choose to avoid implementing it entirely and just special-case `selector()`, or we could choose to implement only the function syntax and ignore the `( )` syntax. This proposal covers the full `` syntax for the same reason CSS does: forwards compatibility. Sass has a general policy of loose coupling with CSS, so that Sass needs as few updates as possible as CSS continues to evolve. Supporting the full range of potential syntax for `@supports` conditions now means that we'll need fewer proposals like this in the future as new conditions are added in practice. #### SassScript Injection There are two possible ways to allow SassScript to be injected into the `` production. One is to support it only via interpolation, allowing the production to otherwise exactly match the CSS syntax; the other is to allow raw Sass-script values to be used within the parentheses. The latter option is appealing at first glance. It requires fewer characters, and matches the parsing of ``, which allows non-interpolated expressions on either side of the `":"`. However, it's only CSS-compatible as long as all the syntax CSS uses in those positions is also a valid SassScript expression. If not, Sass must continually update its syntax to support new CSS constructs. We've been burned by this in the past. Both this proposal and the [media ranges proposal] would have been unnecessary (or at least much simpler) if the rules in questions had only allowed SassScript via interpolation in the first place. And it's likely that this syntax *will* evolve in ways that aren't SassScript-compatible, as with the `selector()` function [mentioned above]. [media ranges proposal]: ../accepted/media-ranges.md [mentioned above]: #background Given this, it's much safer to only allow SassScript within interpolation. #### Backwards Compatibility The `` syntax is very broad, so care is needed to ensure that adding support for it doesn't break existing Sass stylesheets—particularly those that are relying on SassScript in ``. For example, strictly speaking `(foo + bar: baz)` is valid CSS that parses as ``, but Sass needs to continue to parse it as a declaration containing a SassScript expression on the left-hand side. The question then is how to balance backwards-compatibility with CSS compatibility. In most cases, CSS takes clear precedence, and we could manage that here by declaring that ``s that begin with an `InterpolatedIdentifier` must immediately follow it with a `":"` and allowing all other syntax to be parsed as ``. However, this approach is not without its downsides. It would require a deprecation period, and it would parse `` in a number of cases (such as `(foo: )` or `(fo o: bar)`) that are much more likely to be typos. Fortunately, `` is explicitly specified for forwards-compatibility rather than having CSS semantics in and of itself, so we have more flexibility to limit compatibility with it in favor of Sass backwards compatibility. This allows us to limit the syntax we parse as `` to forbid a top-level `":"`, which ensures it's never ambiguous with a declaration and thus with raw SassScript. ## Syntax ### `SupportsCondition`
**SupportsCondition**   ::= 'not' SupportsInParens
                      | SupportsInParens ('and' SupportsInParens)*
                      | SupportsInParens ('or' SupportsInParens)*
**SupportsInParens**    ::= '(' (SupportsCondition | SupportsDeclaration | SupportsAnything) ')'
                      | SupportsFunction | Interpolation
**SupportsDeclaration** ::= Expression¹ ':' Expression
**SupportsAnything**    ::= [InterpolatedIdentifier]² [InterpolatedAnyValue]³?
**SupportsFunction**    ::= [InterpolatedIdentifier]⁴ '(' [InterpolatedAnyValue]? ')'
[InterpolatedIdentifier]: ../spec/syntax.md#interpolatedidentifier [InterpolatedAnyValue]: #interpolatedanyvalue 1: This `Expression` may not begin with the identifier `"not"` or the token `"("`. 2: This `InterpolatedIdentifier` may not be the identifier `"not"`. 3: This `InterpolatedAnyValue` may not contain a top-level `":"`, and it may not begin with the identifier tokens `"and"` or `"or"`. 4: This `InterpolatedIdentifier` may not be the identifier `"not"`. No whitespace is allowed between it and the following `"("`. The identifiers `"not"`, `"and"`, and `"or"` are matched case-insensitively for the purposes of this production. > Implementations must perform some amount of lookahead to disambiguate between > `SupportsDeclaration`, `SupportsAnything`, and `SupportsFunction`. When doing > so, it may be helpful to note that the contents of a `SupportsInParens` must > be parsed as a `SupportsDeclaration` if and only if it contains a top-level > `":"`. ### `InterpolatedAnyValue` The `InterpolatedAnyValue` production is identical to CSS's [``] except that after it parses `"#{"`, it parses an `Expression` which must be followed by `"}"`. [``]: https://drafts.csswg.org/css-syntax-3/#typedef-any-value