Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
annevk opened this issue Oct 7, 2024 · 18 comments
Open

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

annevk opened this issue Oct 7, 2024 · 18 comments
Assignees
Labels
css-color-4 Current Work

Comments

@annevk
Copy link
Member

annevk commented Oct 7, 2024

For whatwg/html#10456 we allow <input type=color>'s internal color to be set as follows:

  • An arbitrary value that parses as a CSS color (value attribute).
  • An end user selected CSS color through some UI.

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

@annevk annevk added the css-color-4 Current Work label Oct 7, 2024
@weinig
Copy link
Contributor

weinig commented Oct 7, 2024

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
Copy link
Member Author

annevk commented Oct 8, 2024

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

@svgeesus
Copy link
Contributor

svgeesus commented Oct 9, 2024

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.

@svgeesus svgeesus self-assigned this Oct 9, 2024
@annevk
Copy link
Member Author

annevk commented Oct 10, 2024

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:

  • Aside from converting the color space, also have some way to convert the representation within a color space. This seems awfully generic given the need though.
  • Have some way to force color(srgb ...) output similar to how we can force #rrggbb output for sRGB colors.

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
Copy link
Member

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
Copy link
Contributor

svgeesus commented Oct 10, 2024

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
Copy link
Member Author

annevk commented Oct 11, 2024

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
Copy link
Contributor

weinig commented Oct 11, 2024

(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
Copy link
Contributor

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

@weinig
Copy link
Contributor

weinig commented Oct 12, 2024

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
Copy link
Member Author

annevk commented Oct 13, 2024

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
Copy link
Member Author

annevk commented Oct 16, 2024

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 whatwg/html#10456 and CSS Color, albeit not always in a straightforward manner:

  • I believe https://drafts.csswg.org/css-color/#missing states that missing becomes 0 due to color conversion (which we do).
  • That includes NaN.
  • Since we don't pass context currentcolor will use the initial value of the color property which is CanvasText which becomes opaque black.

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
Copy link
Contributor

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

@annevk
Copy link
Member Author

annevk commented Oct 16, 2024

Per whatwg/html#9940 (comment) the color-scheme property will always be in light mode when there's no context.

@svgeesus
Copy link
Contributor

Oh right, the initial value.

@svgeesus
Copy link
Contributor

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.

I see that the PR

is merged.

@annevk Is there more to do here? Or can this be closed?

@annevk
Copy link
Member Author

annevk commented Apr 17, 2025

I think that comment outlines the reasons for keeping this open, but if the CSS WG doesn't think further clarification is needed in its drafts then this can be closed. Seems worth carefully checking again though since it's not entirely clear to Sam that will go for others too.

@svgeesus
Copy link
Contributor

Perhaps a new subsection "Color in 2D Canvas" under the existing 3. Applying Color in CSS to explicitly state the consequences of no context, and also to point out that limited-srgb cannot contain extended colors (outside the sRGB gamut).

Would another new section about &ltinput type=color"> also be useful?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-color-4 Current Work
Projects
None yet
Development

No branches or pull requests

4 participants