w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.44k stars 657 forks source link

[css-color-5] Should relative `rgb()`/`rgba()` and relative `hsl()`/`hsla()` serialize exactly as declared? #10327

Open weinig opened 4 months ago

weinig commented 4 months ago

In CSS Color 5, the serialization of relative colors is defined with the following:

The serialization of the declared value of a relative color function is a string identifying the color function in all-lowercase, followed by "(from ", followed by the serialization of the declared value of the origin color, followed by a single space, followed by a singly-space-separated list of the arguments to the color function, followed by ")".

Does this mean that when serializing rgb(from ...)/rgba(from ...) that the specific spelling as declared, rgb vs rgba should be preserved? Or is ok to canonicalize to one or the other?

Similarly, for hsl(from ...)/hsla(from ...) that the specific spelling as declared, hsl vs hsla should be preserved? Or is ok to canonicalize to one or the other?

kbabbitt commented 4 months ago

The spec goes on to state:

The serialization of the result of a relative color function depends on whether the keyword currentColor is the origin color. If so, the result is serialized as the declared value. This allows the correct value to be used on child elements whose color property has a different value. Otherwise, it is the resolved value, which is a <color>, as defined in CSS Color 4 §  15. Serializing <color> Values.

I understand this to mean:

  1. If the origin color is currentColor, then serialize the spelling as declared. (We don't know the origin color and thus cannot canonicalize it.)
  2. If the origin color is not currentColor, then resolve the relative color and serialize canonically as described in Color 4.

There are few tests with examples for how canonicalization happens in case 2: color-valid.html, color-valid-rgb.html, color-valid-hsl.html. Notably:

cc @svgeesus to confirm.

weinig commented 4 months ago

I filed a separate issue on the ambiguity here - https://github.com/w3c/csswg-drafts/issues/10305, as I don't think the current spec text is clear on what the rules are.

Regardless of that however, the question posed here remains, as you can just set currentColor as the origin:

That is, should:

rgb(from currentcolor r g b)

and

rgba(from currentcolor r g b)

serializations maintain the exact spelling of function name.

@kbabbitt, the tests you mention, notably, cover the absolute case, which has clear rules about which spelling to do, and those rules are not about what spelling was used at the declaration site, but rather about the resolved value of the alpha channel. As you might not know the value of the alpha channel in a relative color, the exact same rules can't be followed.

annevk commented 4 months ago

cc @LeaVerou

cdoublev commented 4 months ago

Fwiw, I would prefer to avoid omitting alpha >= 1 and canonicalizing rgba?() and hsla?() depending on whether it is omitted (edit: never mind, I have no preference), but I do not know the "historical reasons" why browsers need to resolve them when serializing them as components of declared values.

weinig commented 4 months ago

I don’t think the historical concerns apply to the relative form as there is no existing use to worry about breaking.

LeaVerou commented 3 months ago

Yup, I think it's fine to do whatever is reasonable in this case, since the historical concerns don’t apply.

weinig commented 3 months ago

My preference here would be to serialize:

rgb(from ...) and rgba(from ...) as rgb(from ...) hsl(from ...) and hsla(from ...) as hsl(from ...)

This loses no useful information, and is minimal, which generally CSS serialization tries to achieve.

It's not a huge deal, implementation wise, to preserve the specific spelling for serialization, so whatever is decided here is fine.

LeaVerou commented 3 months ago

That makes sense. Agenda+ so we can resolve.

svgeesus commented 3 months ago

My preference here would be to serialize:

rgb(from ...) and rgba(from ...) as rgb(from ...) hsl(from ...) and hsla(from ...) as hsl(from ...)

I agree this makes the most sense, and would be happy with that resolution.

svgeesus commented 3 months ago

proposed resolutions:

  1. relative rgb()/rgba() and relative hsl()/hsla() serialize as rgb() and hsl() when alpha is unity.
  2. relative rgb()/rgba() and relative hsl()/hsla() serialize exactly as specified if they use currentcolor, as alpha is unknown.
css-meeting-bot commented 3 months ago

The CSS Working Group just discussed [css-color-5] Should relative `rgb()`/`rgba()` and relative `hsl()`/`hsla()` serialize exactly as declared?, and agreed to the following:

The full IRC log of that discussion <ChrisL> relative rgb()/rgba() and relative hsl()/hsla() serialize as rgb() and hsl() when alpha is unity.
<jarhar> ChrisL: i have a proposed resolution im going to paste it in now
<jarhar> ChrisL: regardless of what you specified, you drop the a
<jarhar> ChrisL: thats consistent with everywhere else i believe
<jarhar> ChrisL: many people said it was fine
<jarhar> fantasai: and this is for the specified value?
<jarhar> ChrisL: the spec says if its one then we drop the a. but i have two and the other one is up for discussion
<jarhar> fantasai: any objections?
<ChrisL> RESOLVED: relative rgb()/rgba() and relative hsl()/hsla() serialize as rgb() and hsl() when alpha is unity.
<jarhar> ChrisL: for the second one theres two ways we could go with
<ChrisL> relative rgb()/rgba() and relative hsl()/hsla() serialize exactly as specified if they use currentcolor, as alpha is unknown.
<jarhar> ChrisL: let me give one possible resolution
<lea> fantasai: I wonder if we could quickly resolve on that accentColor issue since it was brought up anyway and seemed to have general consensus? 😇
<jarhar> ChrisL: so if youre using currentcolor then you dont know what ? is. if you set ? you get rgba, if you set ? you get rgb
<fantasai> s/? you get rgba/rgba you get rgba/
<kbabbitt> s/what ? is/what alpha is/
<fantasai> s/? you get rgb/rgb you get rgb/
<jarhar> ChrisL: the other one is to say that we dont care, we just use the aimiless form beauuse in the modern syntax you can set rgb and then give an alpha. either we eagerly preserve the a's or we get rid of them. i dont have a stron gopinion
<jarhar> florian: we prefer the shortest serialization possible. if you want it the other way around i would be ?, the most important thing is define something
<jarhar> ChrisL: any other opinions?
<florian> s/if you want it the other way around i would be ?/if someone wants it the other way around for some reason i would be fine with it.
<jarhar> fserb: i think there is a use case of people trying to get the color back and this is more work because it may change, maybe we should keep it, but not super strong on this. i think i would favor that
<florian> s/we prefer the shortest /we tend to prefer the shortest
<jarhar> ChrisL: the only argument i had against it is that you have to keep around exactly what the author typed, but
<jarhar> fserb: its usually eaiser to do the shortest
<jarhar> ChrisL: yeah exactly
<jarhar> fantasai: anybody working on serialization have any idea? emilio?
<jarhar> emilio: like preference for shortest serialization?
<emilio> s/like/slight
<jarhar> fantasai: ok, then does anybody object to that?
<kbabbitt> +1 slight preference for shorter serialization
<jarhar> fserb: for no relative colors do we always do the short version?
<ChrisL> RESOLVED: relative rgb()/rgba() and relative hsl()/hsla() serialize as rgb() and hsl() when alpha is unity or unknown (like with currentColor)
<jarhar> ChrisL: we drop the a
kbabbitt commented 1 month ago

Agenda+ to clarify the intent of the WG in the resolution taken previously.

The question originally posed was, by my read, a narrow matter of: when a relative color is serialized as rgb()/rgba() or hsl()/hsla(), whether the form without the a should be used.

Currently, per spec, the above only happens when a relative color is based on currentcolor. For relative colors that are resolvable at parse time, they serialize as their resolved form. And per the same spec section, the resolved form serializes as color(srgb ...) since the mixing color space was srgb or hsl. Blink and Gecko have both shipped this behavior.

Some WPT changes landed recently that appear to interpret the resolution more broadly: that relative colors declared in rgb()/rgba()/hsl()/hsla() should serialize in their declared forms rather than their resolved forms. In fact, the WPT changes go one step further and assert that relative colors declared with other functions such as lab() or lch() should also serialize in their declared forms rather than resolved forms. (See color-valid-relative-color.html in the linked commit.)

Unfortunately, I did not realize that 'should' implication at the time we took the resolution, nor did I realize that it would constitute a break from shipped behavior. :|

The question of whether to serialize in declared or resolved form is asked more directly in #10305, which the WG has not resolved on. Because that would be a breaking change, it's not clear to me we can make it absent compat analysis. I'm happy to do that analysis; however these changes also impact Interop2024, and the remaining runway for that is short. I can't promise I'd be able to get good enough data for the WG to resolve and implementations to adopt the change by the end of the year.

How does the rest of the WG understand the question and resolution here vs 10305?

cc @emilio

weinig commented 1 month ago

@kbabbitt, I think the WPT changes are in line with the changes from https://github.com/w3c/csswg-drafts/issues/10328, where I wrote out a more thorough definition for the serialization of relative colors.

kbabbitt commented 1 month ago

@weinig Thanks for the pointer, that issue does cover it, not sure how I missed it. Will take up further discussion there.