sass/accepted/media-logic.md
2023-08-22 21:10:27 +00:00

5.2 KiB

Media Logic: Draft 1.1

(Issue, Changelog)

This proposal adds support for the full Media Queries Level 4 syntax for media conditions, including arbitrary boolean logic using and, or, and not.

Table of Contents

Background

This section is non-normative.

For historical reasons, Sass fully parses media queries and allows SassScript to be embedded directly in them, as in @media ($query: $value), in contrast to most other at-rules in which SassScript can only be injected using interpolation. This means that as CSS adds new media query syntax, Sass is obligated to update its specification to accommodate it.

Media Queries Level 4 adds support for arbitrary boolean logic in media queries, such as @media ((width >= 100px) and (width <= 800px)) or (grid). Sass must therefore update its syntax accordingly.

Summary

This section is non-normative.

The proposal is relatively straightforward: it adds the new syntax to Sass's grammar. It is worth noting, though, that this will require a few breaking changes. These are unlikely to affect many real-world stylesheets, but they're worth highlighting nevertheless.

The new syntax allows any <media-condition> to appear inside a <media-in-parens>. This means that queries beginning with (not or (( must be parsed as nested media queries, rather than SassScript expressions as they have historically been parsed. We'll issue a short deprecation period for the SassScript expressions in question, recommending users migrate them to interpolation instead, then drop support and begin parsing them as media queries for CSS compatibility.

Syntax

MediaQuery

Replace the definition of the MediaQuery production with the following (with all identifiers matched case-insensitively):

MediaQuery     ::= MediaNot
                 | MediaInParens (MediaAnd* | MediaOr*)
                 | MediaType ('and' MediaNot | MediaAnd*)
MediaType      ::= InterpolatedIdentifier InterpolatedIdentifier¹?
MediaNot²      ::= 'not' MediaOrInterp
MediaAnd²      ::= 'and' MediaOrInterp
MediaOr²       ::= 'or' MediaOrInterp
MediaOrInterp  ::= Interpolation | MediaInParens
MediaInParens  ::= '(' Expression³ ')'
                 | '(' Expression³ <mf-comparison> Expression³ ')'
                 | '(' Expression³ <mf-lt> Expression³ <mf-lt> Expression³ ')'
                 | '(' Expression³ <mf-gt> Expression³ <mf-gt> Expression³ ')'
                 | '(' MediaNot ')'
                 | '(' MediaInParens (MediaAnd* | MediaOr*) ')'

  1. This InterpolatedIdentifier may not be the identifier "and".

  2. No whitespace is allowed between the identifier and the MediaOrInterp in these productions.

  3. These Expressions may not:

    • Contain binary operator expressions with the operators =, >, >=, <, or <=, except within parentheses (including function calls and map literals) and square brackets.

    • Begin with the case-insensitive identifier "not".

    • Begin with the character "(".

CssMediaQuery

Replace the definition of the CssMediaQuery production with the following (with all identifiers matched case-insensitively):

CssMediaQuery     ::= CssMediaCondition
                    | CssMediaType ('and' CssMediaNot | CssMediaAnd*)
CssMediaType      ::= <ident-token> <ident-token>¹?
CssMediaCondition ::= CssMediaNot | CssMediaInParens (CssMediaAnd* | CssMediaOr*)
CssMediaNot       ::= 'not' CssMediaInParens
CssMediaAnd       ::= 'and' CssMediaInParens
CssMediaOr        ::= 'or' CssMediaInParens
CssMediaInParens  ::= '(' <declaration-value> ')'

  1. This <ident-token> may not be the identifier "and".

Deprecation Process

Before this specification is applied in full force, it will be applied with the following modifications:

  • MediaInParens will not allow the productions '(' MediaNot ')' or '(' MediaInParens (MediaAnd* | MediaOr*) ')'.

  • If the first Expression in a MediaInParens production begins with the case-insensitive identifier "not" or the character "(", emit a deprecation warning.