Skip to content

Commit

Permalink
[css-values-5] Per WG resolution, switch comma-containing productions…
Browse files Browse the repository at this point in the history
… to allowing {} wrappers, rather than semicolon upgrades. #9539
  • Loading branch information
tabatkins committed Oct 15, 2024
1 parent f8e266b commit 3a4a0d6
Showing 1 changed file with 72 additions and 51 deletions.
123 changes: 72 additions & 51 deletions css-values-5/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -76,39 +76,36 @@ Functional Notation Definitions</h3>
See [[css-values-4#component-functions]].

<h4 id=component-function-commas>
Commas and Semicolons in Functions</h4>
Commas in Function Arguments</h4>

[=Functional notation=] often uses commas
to separate parts of its internal grammar.
However, some functions
(such as ''mix()'')
allow values that, themselves,
can contain commas.
These values
(currently <<whole-value>>, <<declaration-value>>, and <<any-value>>)
are <dfn export>comma-containing productions</dfn>.

To accommodate these sorts of grammars unambiguously,
commas in functional grammars are <em>implicitly upgradeable</em> to semicolons in Level 5;
that is,
commas in a [=functional notation=]'s grammar
can instead be represented as <<semicolon-token>>s.
This is all-or-nothing:
either every comma in the [=functional notation=] must be written as a semicolon,
or none of them must be.

When a [=functional notation=] is parsed,
initially commas (<css>,</css>) and comma-multipliers (<css>#</css>)
in the grammar match only <<comma-token>>s in the value,
and any [=comma-containing productions=] are not allowed to contain <<comma-token>>s
(the productions must be interpreted as ending before the comma).
If a <<semicolon-token>> is encountered while parsing the [=functional notation=],
the contents must be <em>re-interpreted</em>,
with commas and comma-multipliers in the grammar matching only <<semicolon-token>>s in the value,
and [=comma-containing productions=] being allowed to contain <<comma-token>>s.

Commas contained in productions defined as <dfn>comma-containing productions</dfn>
(such as <<any-value>> or <<whole-value>>)
are not implicitly upgradeable.
Even when a [=functional notation=] is being re-interpreted with semicolons,
these productions end when a <<semicolon-token>> is encountered.
the [=comma-containing productions=] can be optionally wrapped in curly braces {}.
These braces are syntactic, not part of the actual value.
Specifically:

* A [=comma-containing production=] can either start with a "{" token, or not.
* If it does not start with a "{" token,
then it cannot contain commas or {} blocks,
in addition to whatever specific restrictions it defines for itself.
(The production stops parsing at that point,
so the comma or {} block is matched by the next grammar term instead;
probably the function's own argument-separating comma.)
* If it does start with a "{" token,
then the production matches just the {} block that the "{" token opens.
It represents the <em>contents</em> of that block,
and applies whatever specific restrictions it defines for itself
to those contents,
ignoring the {} block wrapper.

<div class="example">
For example, the grammar of the ''random-item()'' function is:
Expand All @@ -123,56 +120,80 @@ Commas and Semicolons in Functions</h4>
like:

<pre>
list-style: random-item(--x, disc, circle, square);
</pre>

It is <em>allowed</em>, however,
to "upgrade" the commas to semicolons,
like:

<pre>
list-style: random-item(--x; disc; circle; square);
</pre>

Both of the above mean the exact same thing.
However, mixing commas and semicolons does not work;
the following can produce an invalid value after substitution:

<pre>
list-style: random-item(--x; disc, circle; square);
font-family: random-item(--x, serif, sans-serif, monospace);
</pre>

because it represents choosing between two values
(<css>disc, circle</css> and <css>square</css>)
and <css>disc, circle</css> is not a valid 'list-style' value.

However, sometimes the values you want to choose between
need to include commas.
For example, in 'font-family':
When this is the case,
wrapping the values in {}
allows their commas to be distinguished
from the function's argument-separating commas:

<pre>
font-family: random-item(--x; Times, serif; Arial, sans-serif; Courier, monospace);
font-family: random-item(--x, {Times, serif}, {Arial, sans-serif}, {Courier, monospace});
</pre>

This randomly chooses one of three font-family lists:
either ''Times, serif'', or ''Arial, sans-serif'', or ''Courier, monospace''.
But if only single fonts were needed for each choice,
commas <em>could</em> have been used to separate them:

This is not all-or-nothing;
you can use {} around <em>some</em> arguments that need it,
while leaving others bare when they don't need it.
You are also allowed to use {} around a value when it's not strictly required.
For example:

<pre>
font-family: random-item(--x, serif, sans-serif, monospace);
font-family: random-item(--x, {Times, serif}, sans-serif, {monospace});
</pre>

This represents choosing between three font-family lists:
either ''Times, serif'', or ''sans-serif'', or ''monospace''.

However, this {}-wrapping is <em>only</em> allowed for some function arguments--
those defined as [=comma-containing productions=].
It's not valid for any other productions;
if you use {} around other function arguments,
it'll just fail to match the function's grammar
and become invalid.
For example, the following is <strong>invalid</strong>:

<pre>
background-image: linear-gradient(to left, {red}, magenta);
</pre>
</div>

[=Functional notations=] are serialized with commas (rather than semicolons) whenever possible.
Note: Because {} wrappers are allowed even when not explicitly required,
they can be used defensively around values
when the author isn't sure if they'll end up containing commas or not,
due to [=arbitrary-substitution functions=] like ''var()''.
For example, ''font-family: random-item(--x, {var(--list1)}, monospace)''
will work correctly
regardless of whether the ''--list1'' custom property
contains a comma-separated list or not.

[=Functional notations=] are serialized without {} wrappers whenever possible.

The following generic productions are [=comma-containing productions=]:

* <<any-value>>
* <<whole-value>>
* <<declaration-value>>

For legacy compat reasons,
the <<declaration-value>> defined the fallback value for ''var()''
is a <dfn export>non-strict comma-containing production</dfn>.
It ignores the rules restricting what it can contain
when it does not start with a "{" token:
it is allowed to contain commas and {} blocks.
It still follows the standard [=comma-containing production=] rules
when it <em>does</em> start with a "{" token, however:
the fallback is just the contents of the {} block,
and doesn't include the {} wrapper itself.

Other contexts <em>may</em> define that they use [=non-strict comma-containing productions=],
but it <em>should</em> be avoided unless necessary.

<h2 id="level-4-extensions">
Extensions to Level 4 Value Types</h3>

Expand Down

0 comments on commit 3a4a0d6

Please sign in to comment.