csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

[css-color-4] `rgba()` is legacy so does not support `none`. Right?

Open svgeesus opened this issue 2 years ago • 1 comments

From CSS Color 4:

The rgb() function defines an sRGB color by specifying the red, green, and blue channels directly

For legacy reasons, rgb() also supports a legacy color syntax that separates all of its arguments with commas:

Also, an rgba() legacy color syntax also exists, with an identical grammar and behavior to rgb().

Now I guess there are two ways of reading this:

a) rgba() is a legacy only syntax, so rgba(18% none 56% / 30%) is invalid b) rgba() has a modern syntax, which is not described explicitly but is "identical" except for using rgba, and a legacy syntax given below.

@tabatkins Which is it?

I had assumed a). But WPT tests like parsing/color-computed-rgb.html seem to assume b). WPT results as do Chrome and Safari.

Either way the spec needs to clarify.

svgeesus avatar Oct 18 '22 17:10 svgeesus

I think I intended (a), but I have no opinion on which we go with. If browsers and WPTs currently do (b), that's fine with me, so we should clarify in that direction.

tabatkins avatar Oct 18 '22 17:10 tabatkins

@csnardi you wrote the WPT in question, should the spec be updated to add a non-legacy rgba() syntax (no commas, slash for alpha, accepts none)?

Comments from browser engineers working on this aspect very welcome.

svgeesus avatar Oct 26 '22 13:10 svgeesus

I am not a browser engineer but I would like to add that the legacy rgb() syntax (production) accepts <alpha-value> whereas legacy color syntax defines that non-opaque forms use a separate notation (for example hsla() rather than hsl()).

EDIT: to be clear, I would remove non-opaque forms use a separate notation (for example hsla() rather than hsl()) from the definition of legacy color syntax, because rgba() and hsla() are aliases, ie. they can be an alias of legacy rgb() or hsl() but they "only" are aliases.

For legacy reasons, rgb() also supports a legacy color syntax that separates all of its arguments with commas:

rgb() = rgb( <percentage>#{3} , <alpha-value>? ) | rgb( <number>#{3} , <alpha-value>? )

For Web compatibility, the syntactic forms of rgb(), rgba(), hsl(), and hsla(), (those defined in earlier specifications) also support a legacy color syntax which has the following differences:

  • color components are separated by commas (optionally preceded and/or followed by whitespace)
  • non-opaque forms use a separate notation (for example hsla() rather than hsl())
  • ...

I am not able to say why none and color channel keywords are not accepted in legacy color functions, but as a user I would prefer them to be accepted in all color function syntaxes (eg. as defined in 3. Relative Color Syntax for color channel keywords, even if there are also not accepted in <device-cmyk()>) or if it is not possible, only in non-legacy color functions, ie. not in rgba() and hsla().

EDIT:

Also as functionality is added over time, having to support [none] in all the various legacy syntaxes as well (with all their varying web-compat issues) would be burdensome for implementers while offering no particular benefit for content authors.

https://github.com/w3c/csswg-drafts/issues/7137#issuecomment-1065965841

This does not seem burdensome to me and the benefit I see for content authors would be that they would not have to remember in which syntax none in valid or not.

cdoublev avatar Oct 27 '22 10:10 cdoublev

My interpretation of the spec has been that legacy rgb() has one syntax and modern rgb() has another syntax, and that rgba() is just an alias for the corresponding syntax.

Put another way: legacy rgba() does not support none, but modern rgba() does.

(And the same goes for hsl() and hsla().)

GPHemsley avatar Nov 12 '22 00:11 GPHemsley

Okay, sounds like a consensus is forming and so I should make the spec edits.

svgeesus avatar Nov 14 '22 15:11 svgeesus

rgba() is legacy so does not support none. Right?

Wrong :)

svgeesus avatar Nov 14 '22 15:11 svgeesus

I assume that the consensus is to accept none as an argument of rgba() and hsla() when they are aliases of the modern rgb() and hsl(), respectively, and that legacy rgba() or legacy hsla() are a short and confusing concept/naming for rgba() or hsla() as aliases of legacy rgb() or hsl(), that should be removed from the spec (like the link in the definition of <color> directing from <rgba()> to the definition of rgba() legacy color syntax).

  color:  rgb(0, 0, 0, none); /* invalid */
  color: rgba(0, 0, 0, none); /* invalid */
  color:  rgb(0 0 0 / none);  /* valid, serializes to rgba(0, 0, 0, 0) */
  color: rgba(0 0 0 / none);  /* valid, serializes to rgba(0, 0, 0, 0) */

Please allow me this additional observation, which is also valid for <hsla()>.

<color> includes both ... | <rgb()> | <rgba()> | ..., therefore a CSS parser should not recognize rgba as a function name alias of rgb when matching against <rgb()>: an input specified with rgba(...) should match <rgba()> instead.

<rgba()> is not defined with a basic syntax therefore w3c/reffy only extracts some prose and a grammar-driven parser implementation must define it on its side. It cannot define it with <rgba()> = <rgb()> because this would require to recognize rgba as a function name alias of rgb.

It might tempting to say that rgba() should be defined in prose as a legacy alias for rgb() and that <rgba()> should be removed from <color>, but similarly, <repeating-*-gradient()> is not defined with a basic syntax, is clearly not legacy, and is semantically different than the corresponding <*-gradient()>.

In my opinion, an ideal solution would be to define the arguments of <rgb()> and <rgba()> with a non-terminal:

<rgb-fn-args> = [
    [<percentage> | none]{3} [/ [<alpha-value> | none]]?
  | [<number> | none]{3} [/ [<alpha-value> | none]]?
<rgb()> = rgb(<rgb-fn-args>)
<rgba()> = rgba(<rgb-fn-args>)

<rgb-fn-args> could even include <legacy-rgb-fn-args>, which would allow w3c/webref to remove legacyValue for <rgb()>.

cdoublev avatar Nov 15 '22 14:11 cdoublev

@cdoublev I'm not sure I'm following your explanation/argument. Allow me to be deliberately verbose in order to ensure we're all on the same page.


css-color-3 defines rgb() with one syntax (3 comma-separated arguments) and rgba() with another syntax (4 comma-separated arguments):

rgb() rgba()
3 arguments ✔️
4 arguments ✔️

css-color-4 expands those definitions into a superset "legacy color syntax", using either 3 or 4 comma-separated arguments, invoked using either rgb() or rgba():

rgb() rgba()
3 arguments ✔️ ✔️
4 arguments ✔️ ✔️

That rgba() is what I refer to as "legacy rgba()" and is apparently already settled.


css-color-4 also defines a new syntax for rgb(), using only spaces and a slash to separate the alpha value, and allowing none. Under discussion here is whether that syntax is also available to rgba() - what I refer to as "modern rgba()".

The two options mentioned in the original comment are as follows:

a) rgba() is legacy syntax only

rgb() rgba()
legacy syntax ✔️ ✔️
modern syntax ✔️

b) rgba() is a complete alias of rgb()

rgb() rgba()
legacy syntax ✔️ ✔️
modern syntax ✔️ ✔️

Consensus appears to be (b).


After having ruminated and reread your comment, I think you are merely arguing over how to define this grammatically? (When you say rgba() and hsla() "should be removed from the spec" you mean just as a distinct grammatical rule, rather than the entire concept?)

If that's the target, this is one way to do it:

<legacy-rgb-syntax> = <percentage>#{3} , <alpha-value>? | <number>#{3} , <alpha-value>?
<modern-rgb-syntax> = [<percentage> | none]{3} [ / [<alpha-value> | none] ]? | [<number> | none]{3} [ / [<alpha-value> | none] ]?
<rgb-function-name> = rgb | rgba
<rgb()> = <rgb-function-name>( [ <legacy-rgb-syntax> | <modern-rgb-syntax> ] )

But if not, this could also work:

<rgb()> = rgb( [ <legacy-rgb-syntax> | <modern-rgb-syntax> ] )
<rgba()> = rgba( [ <legacy-rgb-syntax> | <modern-rgb-syntax> ] )

(And similarly for hsl() and hsla().)

The first option is essentially how I've implemented it.

The second option seems unnecessarily redundant if consensus is not interpretation (a), as it ostensibly requires rgb() and rgba() to be parsed independently.


EDIT: Oh, that's exactly what you were saying, isn't it.

GPHemsley avatar Nov 16 '22 03:11 GPHemsley

Oh, that's exactly what you were saying, isn't it.

Yes. I would like to avoid spec readers thinking that rgba() (or hsla()) is entirely legacy...

rgba() is legacy so does not support none. Right?

Wrong :)

... and I would like <rgba()>, <hsla()>, <repeat-*-gradient()>, to be defined with a basic syntax or alternatively, to be removed from any parent production, and defined as non-legacy aliases.

cdoublev avatar Nov 16 '22 04:11 cdoublev

@GPHemsley thanks for listing all the differences, for CSS Color 4. Let me add to that: CSS Color 5 adds Relative Color Syntax which for Relative sRGB Colors gives a grammar which extends the modern (comma-less, slash alpha) rgb() function (only).

In Relative HSL Colors there is a clarification:

Relative color syntax is only applicable to the non-legacy HSL syntactic forms.

That isn't needed for HWB, Lab, LCH, Oklab and Oklch which don't have legacy forms but clarification is needed for rgba(). Currently it says

Relative color syntax is only applicable to the non-legacy RGB syntactic forms.

If all rgba() is legacy then it needs to be explicitly excluded from RCS; if the comma-form is legacy and the comma-less, slash alpha form is not legacy then that form needs to be added to the grammar.

Given the way the discussion is going I would argue for the latter option.

svgeesus avatar Nov 16 '22 16:11 svgeesus

So in CSS Color 5 we would have

<legacy-rgb-syntax> = <percentage>#{3} , <alpha-value>? | <number>#{3} , <alpha-value>?
<modern-rgb-syntax> = [<percentage> | <number> | none]{3} [ / [<alpha-value> | none] ]? 
<relative-rgb-syntax> = from <color>  <modern-rgb-syntax>
<rgb-function-name> = rgb | rgba
<rgb()> = <rgb-function-name>( [ <legacy-rgb-syntax> | <modern-rgb-syntax> | <relative-rgb-syntax> ] )

Two changes there, one to add RCS and one to make modern syntax accept a mixture of <number> and <percentage>.

svgeesus avatar Nov 16 '22 16:11 svgeesus

Also tempted to back-port the "accept a mixture of <number> and <percentage>" to CSS Color 4. I believe this already has tests in WPT (because people are implementing CSS Color 5 and 4 at the same time).

svgeesus avatar Nov 16 '22 16:11 svgeesus

Given the way the discussion is going I would argue for the latter option.

I do not repeat to insist but only to be sure that there is no misunderstanding: accepting RCS and none in 2 of the 4 syntaxes is not my personal preference, which is to accept them either in the four syntaxes or only in comma-less slash alpha rgb() and hsl(), because I think it is less error-prone for CSS authors.

There is no precedent for <fn-name>(...). If it the CSS value definition syntax does not allow it, and if non-legacy hsla|rgba have to be included in the syntax as non-terminals, then it can be defined like this:

<rgb-syntax> = [ <legacy-rgb-syntax> | <modern-rgb-syntax> | <relative-rgb-syntax> ]
<rgb()> = rgb( [ <rgb-syntax> ] )
<rgba()> = rgba( [ <rgb-syntax> ] )

cdoublev avatar Nov 16 '22 17:11 cdoublev

Given the way the discussion is going I would argue for the latter option.

I do not repeat to insist but only to be sure that there is no misunderstanding: accepting RCS and none in 2 of the 4 syntaxes is not my personal preference, which is to accept them either in the four syntaxes or only in comma-less slash alpha rgb() and hsl(), because I think it is less error-prone for CSS authors.

CSS authors concerned about being confused can use the modern syntax across the board.

But implementors are another story.

Also tempted to back-port the "accept a mixture of <number> and <percentage>" to CSS Color 4. I believe this already has tests in WPT (because people are implementing CSS Color 5 and 4 at the same time).

This is food for thought, as is @cdoublev's suggestion. Implementations could be simpler if they didn't have to branch for all the different, mutually-exclusive syntaxes.

GPHemsley avatar Nov 16 '22 21:11 GPHemsley

For what it is worth, Chrome supports none in all 4 syntaxes.

Tab argued in #7137 that the comma-separated argument syntax should not be encouraged to be used. I would argue that rgba/hsla should also not be encouraged to be used for the same reason, but the maintenance burden only exists for spec editors, imo.

cdoublev avatar Jan 13 '23 10:01 cdoublev

Aside

Chrome also serializes none to 0 in hwb() but the spec wants this only for legacy syntaxes.

If a color with a missing component is serialized or otherwise presented directly to an author, then for legacy color syntax it represents that component as a zero value; otherwise, it represents that component as being the none keyword.

I guess the reason to serialize none to 0 in legacy syntaxes is back-compat but can you please clarify me how it saves back-compat?

EDIT: nevermind, hwb() serializes to rgb(), therefore the above sentence is just wrong and it should be for sRGB color functions.

cdoublev avatar Jan 13 '23 10:01 cdoublev

hsla() is the most common color syntax in the wild, after 6-digit hex.

svgeesus avatar Jan 13 '23 21:01 svgeesus

For what it is worth, Chrome supports none in all 4 syntaxes.

It does? We currently have good interop on what is invalid for rgb() and rgba() and that test currently says:

    ["rgb(none, none, none)", "The none keyword is invalid in legacy color syntax"],
    ["rgba(none, none, none, none)", "The none keyword is invalid in legacy color syntax"],
    ["rgb(128, 0, none)", "The none keyword is invalid in legacy color syntax"],
    ["rgb(255, 255, 255, none)", "The none keyword is invalid in legacy color syntax"],

Do we really want to make legacy, comma-based rgb and rgba accept 'none'?

I agree with @tabatkins that mixing <number> and <percentage> across the board would be good:

That said, I definitely support loosening the restriction; it seems like a legacy constraint from an earlier age of CSS design.

I'm a lot less convinced that allowing legacy, comma-based syntaxes to accept 'none' (and RCS??) would be a good idea. Although having re-read this and related issues for the n th time, I'm also not sure if that is actually being proposed.

svgeesus avatar Feb 07 '23 18:02 svgeesus

For what it is worth, Chrome supports none in all 4 syntaxes.

It does?

No. I apologize.

I'm a lot less convinced that allowing legacy, comma-based syntaxes to accept 'none' (and RCS??) would be a good idea. Although having re-read this and related issues for the n th time, I'm also not sure if that is actually being proposed.

I proposed it, as well as accepting them only in modern rgb()/hsl(). But I have no strong opinion.

I came across this usage data a few days ago (referenced in this guide). I was surprised to see rgba() and hsla() more often used than rgb() and hsl(). I would have been interested to know if authors are more often using legacy or modern syntax. Anyway, authors using the legacy syntax would now have to switch to the modern syntax in order to use none and RCS.

cdoublev avatar Feb 07 '23 20:02 cdoublev

I came across this usage data a few days ago (referenced in this guide).

I know (I was a reviewer that year) and it was similar in earlier years.

I was surprised to see rgba() and hsla() more often used than rgb() and hsl().

People needed a reason to use something other than the six-digit hex they had used since forever. The explicit-alpha functional forms rgba() and hsla() were the first thing that gave them something they could not do otherwise (eight-digit hex was made available in browsers a lot later).

HSL gets used a lot for content creation, but often in sass or some other postprocessor to manipulate the HSL values then spit out the result in hex or whatever.

svgeesus avatar Feb 07 '23 20:02 svgeesus

The CSS Working Group just discussed [css-color-4] `rgba()` is legacy so does not support `none`. Right?, and agreed to the following:

  • RESOLVED: non-RCS rgb() and hsl() can mix numbers/%s when using the modern syntax (space and slash, not comma).
The full IRC log of that discussion <TabAtkins> chris: We have an old syntax which has commas for everything
<TabAtkins> chris: And a new which is space and slash
<astearns> ack chris
<TabAtkins> chris: We've added a bunch to that new syntax, like RCS
<TabAtkins> chris: But it used to be that everything had to be a % or everything a number, that's from CSS1
<TabAtkins> chris: In RCS you can mix them tho
<TabAtkins> chris: My proposal is that the modern syntax, only, allows mixing %s and numbers.
<TabAtkins> chris: Also the modern syntax can use 'none'. I'd also like to keep that restricted to the modern syntax.
<TabAtkins> chris: We have very good interop on what's currently specified
<TabAtkins> chris: People have argued that for clarity we should extend the capabilities to all syntaxes, but I think it's a bad idea - we have very solid interop as-is.
<TabAtkins> I'm totally fine with this.
<TabAtkins> chris: So proposed reoslution is we backport mixing number/% to the modern syntax of non-RCS rgb() and hsl()
<argyle> +1
<TabAtkins> astearns: Questions?
<TabAtkins> astearns: I assume this is okay with the issue commenters?
<TabAtkins> chris: There's one in the thread who had several suggestions but no strong opinion. Other than that, agreement in the thread.
<TabAtkins> RESOLVED: non-RCS rgb() and hsl() can mix numbers/%s when using the modern syntax (space and slash, not comma).

css-meeting-bot avatar Feb 08 '23 17:02 css-meeting-bot

The modern syntax of rgba() is now explicitly listed in the grammar.

svgeesus avatar Feb 20 '23 20:02 svgeesus