w3c / csswg-drafts

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

CSS color serialization and <input type=color> with alpha/colorspace #11008

Open annevk opened 1 week ago

annevk commented 1 week ago

For https://github.com/whatwg/html/pull/10456 we allow <input type=color>'s internal color to be set as follows:

This color is then color space converted depending on the colorspace attribute and serialized for API purposes and form submission. We only support the 'srgb' and 'display-p3' color spaces for now.

When colorspace is set to "limited-srgb" (default value) and the alpha attribute is not set we specifically instruct CSS color serialization to use the hexadecimal syntax for compatibility.

Otherwise we follow CSS color serialization directly.

However, what is not clear currently is what kind of normalization color space conversion applies. E.g., if the input is lab(51.2345% -13.6271 16.2401), is the output going to be color(srgb 0.41587 0.503670 0.36664)? How is that defined? What if the input is #11223344?

Ideally from a web developer perspective I think it would always use color(srgb ...) or color(display-p3 ...) unless we needed compatibility with the existing input as described above. But now I'm wondering if that is actually what we ended up requiring or if there are some other loose ends to tighten up.

cc @svgeesus @lukewarlow @weinig

weinig commented 1 week ago

I think this likely could be defined in terms of non-modifying relative color calculation. That is,

lab(51.2345% -13.6271 16.2401)

with colorspace srgb selected, it gets serialized as if:

color(from lab(51.2345% -13.6271 16.2401) srgb r g b / alpha)

which would compute to a color(srgb 0.41587 0.503670 0.36664).

This would work just as well with the value #11223344

color(from #11223344 srgb r g b / alpha)

This is fully defined and already in engines, so should be straight forward to implement as well.

annevk commented 1 week ago

That's great! Any idea @svgeesus how to do the appropriate wording?

svgeesus commented 1 week ago

I'm thinking about it. It does need to be explicit in the spec, not just deducible to experts.

color(from lab(51.2345% -13.6271 16.2401) srgb r g b / alpha)

which would compute to a color(srgb 0.41587 0.503670 0.36664).

This would work just as well with the value #11223344

Sure but we need to have language so that which of these two you get is clear, and can be interoperable.

annevk commented 1 week ago

So we already do color space conversion to either sRGB or Display P3. So we are guaranteed to have a CSS color in either of those color spaces. And Display P3 is always serialized as color(display-p3 ...). And sRGB is serialized as #rrggbb with the HTML-compatible directive set.

So the main question is what happens when the HTML-compatible directive is not set (i.e., when the alpha attribute is set). It seems nice to output color(srgb ...) there, but this is not guaranteed as the CSS color, while it is guaranteed to be sRGB, can still have many different representations.

I see two ways of getting there:

My current prototype does a combination of these. WebKit's internal Color object has a way to force color(...) serialization, which I use in the sRGB + alpha attribute case.

lukewarlow commented 1 week ago

This seems awfully generic given the need though.

While that's true in this case, there's a proposal for a JS Color API and as part of that we might (I've definitely wanted this before) want to choose the serialisation. For example hex with alpha has wider browser and tooling support so you might want to output the colour in that serialisation.

svgeesus commented 1 week ago

So the main question is what happens when the HTML-compatible directive is not set (i.e., when the alpha attribute is set).

You probably already know this and just simplified for brevity, but since you are prototyping an implementation I felt the need to point out that non-unity alpha is only one of the cases that means #rrggbb is not used. The other is when this precondition is false::

The RGB component values are internally represented as integers between 0 and 255 inclusive (i.e. 8-bit unsigned integer)

Which means that extended-range sRGB will not use the hex serialization, either. For example color(srgb -0.41 0.92 -0.18) is not representable with 8 bits per component due to the negative values.

annevk commented 1 week ago

True, but we only support "limited-srgb" and "display-p3" for now. If we add "srgb" in the future it'll support the full sRGB range and for that we would also need a "use color() function" override I suppose.

weinig commented 1 week ago

(I think adding "srgb" would be good. In general, now that we srgb is not clamped to [0,1], it's a pretty good default interchange value).

Would be good to clarify what behavior is desired for some edge case input values:

Should none be serialized if it is explicitly stated?

<input type="color" colorspace="display-p3" value="color(srgb none 0 1)">

Should NaN / infinity be serialized.

<input type="color" colorspace="display-p3" value="color(srgb calc(NaN) 0 1)">
<input type="color" colorspace="display-p3" value="color(srgb calc(infinity) 0 1)">

Should currentColor be supported? And if so, what value should it serialize as?

<input type="color" colorspace="display-p3" value="color(from currentColor srgb r g b / 2)">
svgeesus commented 1 week ago

I agree that these cases should be explicitly covered, but also don't see a typical color picker generating them.

weinig commented 6 days ago

Right, the color picker is unlikely to ever generate them, but I believe the user can explicitly set the value as a string using the value attribute.

annevk commented 5 days ago

Yeah, I think those should be covered by how we invoke "parse a CSS <color>" without passing in context. So currentcolor will always become opaque black for instance.

("limited-srgb" unfortunately has to be the default to preserve compatibility with the existing control. We could add an opt-in for "srgb". I decided to wait with adding a lot more until we've gotten some initial web developer feedback. It's easier to add things than to remove them again.)

annevk commented 2 days ago

I think I will address this problem in the HTML PR for now by adding language to the effect of

Set color to color converted to use the 'color()' function.

which will at least make it unambiguous what we need. I believe the questions Sam raised are already addressed by the combination of https://github.com/whatwg/html/pull/10456 and CSS Color, albeit not always in a straightforward manner:

I will leave this issue open to potentially allow for that (and other) wording to be improved if the CSS WG decides to add more primitives here.

svgeesus commented 2 days ago

Agreed about missing values (which includes NaN). CanvasText is opaque black in light mode, opaque white in dark mode.

annevk commented 2 days ago

Per https://github.com/whatwg/html/issues/9940#issuecomment-1820495890 the color-scheme property will always be in light mode when there's no context.

svgeesus commented 2 days ago

Oh right, the initial value.