Skip to content
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

[css-color] Clamping component values at computed-value time vs parsing time #7677

Closed
mysteryDate opened this issue Sep 2, 2022 · 21 comments
Labels
Closed Accepted by Editor Discretion Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-color-4 Current Work

Comments

@mysteryDate
Copy link

mysteryDate commented Sep 2, 2022

From https://www.w3.org/TR/css-color-4/#rgb-functions:

The final argument, the <alpha-value>, specifies the alpha of the color. If given as a <number>, the useful range of the value is 0 (representing a fully transparent color) to 1 (representing a fully opaque color). If given as a <percentage>, 0% represents a fully transparent color, while 100% represents a fully opaque color. If omitted, it defaults to 100%.
Values outside these ranges are not invalid, but are clamped to the ranges defined here at computed-value time.

All user agents I've tested in fact do the clamping at parsing time:

  <!doctype html>
  <style>
    #target {
          color: rgba(400, 400, 300, -300);
    }
  </style>
  <div id="target"></div>
  <pre id="res"></pre>
  <script>
    res.innerText = "specified: " + document.styleSheets[0].cssRules[0].style.color + "\ncomputed: " + getComputedStyle(target).color;
  </script>

Firefox, Safari and Chrome all give "specified: rgba(255, 255, 255, 0)"

Is there a disadvantage to just doing the clamping at parsing time? It is easier to implement and is already the behavior that we're living with.

@svgeesus svgeesus added the css-color-4 Current Work label Sep 2, 2022
@cdoublev
Copy link
Collaborator

cdoublev commented Sep 3, 2022

Clamping at parse time may be an exception for sRGB color functions. CSSOM defines how to serialize <alphavalue> (aside: note the missing - between "alpha" and "value") as an 8-bit integer (between 0 and 255). This might be the (historical) reason why browsers do clamping at parse time, as noted in 15.2 Serializing sRGB values:

For compatibility, the sRGB component values are serialized in <number> form, not <percentage>). Also for compatibility, the component values are serialized in base 10, with a range of [0-255], regardless of the bit depth with which they are stored.

Aside: there is an extra ) in the first sentence.

But this same section also has... (EDIT: this section does not apply to alpha but only to rgb/hsl components)

The serialized form of the following sRGB values [...] is derived from the computed value and thus, uses either the rgb() or rgba() form (depending on whether the alpha is exactly 1, or not)

... and I am very confused that I have to use a computed value to serialize a specified value. And 15. Serializing <color> Values starts with:

This section updates and replaces that part of CSS Object Model, section Serializing CSS Values, which relates to serializing <color> values.

Apart from this, there are also math functions, whose value is clamped at computed value time, eg. rgb(0, 0, 0, calc(-1)), and opacity is also defined with <alpha-value> and its specified value is not clamped.

@lilles
Copy link
Member

lilles commented Sep 5, 2022

Looking at the serialization part of the spec, it looks like it's focused on computed values and that serializing specified values is overlooked?

https://drafts.csswg.org/css-color-4/#serializing-sRGB-values

@svgeesus
Copy link
Contributor

svgeesus commented Sep 5, 2022

Looking at the serialization part of the spec, it looks like it's focused on computed values and that serializing specified values is overlooked?

It is, indeed.

@svgeesus
Copy link
Contributor

svgeesus commented Sep 5, 2022

CSSOM defines how to serialize (aside: note the missing - between "alpha" and "value") as an 8-bit integer (between 0 and 255). This might be the (historical) reason why browsers do clamping at parse time, as noted in 15.2 Serializing sRGB values:

All the color serialization stuff got moved from CSSOM to Color 4. It makes sense to move the alpha serialization as well.

The legacy forms and the newer color(), oklab() etc have different compatibility constraints of course.

@lilles
Copy link
Member

lilles commented Dec 2, 2022

This is also an issue for lightness. Per the current spec, lab()/lch(), for instance, should clamp negative lightness to 0 at computed value time, not specified/parse time: https://drafts.csswg.org/css-color/#specifying-lab-lch

There are several tests in e.g. https://wpt.fyi/results/css/css-color/parsing/color-valid-lab.html that expects clamping at parse time and conflicts with the current spec.

Also, both WebKit and Blink currently clamp at parse time.

@Juanmihd
Copy link

Is there any update regarding this?

Should we change the spec to match the tests and assume clamping at parse time? Or are the tests wrong?

@lilles lilles added the Agenda+ label Dec 16, 2022
@lilles lilles changed the title [css-color] Clamping alpha at computed-value time vs parsing time [css-color] Clamping component values at computed-value time vs parsing time Jan 5, 2023
@lilles
Copy link
Member

lilles commented Jan 5, 2023

Summary:

  • Clamping at parse time instead of computed time should just affect specified style serialization
  • wpt tests expect parse time clamping and do not match current spec
  • Safari ships with clamping at parse time
  • Chrome wants to ship and currently implements clamping at parse time
  • Implementing clamping at computed value time means range checking and clamping has to happen every time we apply a color during style resolution, which is an implementation/performance burden

I propose to resolve on changing the spec to clamp at parse time for the components that currently are specfied to clamp at computed value time.

@svgeesus
Copy link
Contributor

svgeesus commented Jan 5, 2023

@lilles do you have any specific text suggestions for a new sub-section covering serializing specified values?

@mysteryDate
Copy link
Author

@svgeesus Isn't simply changing the text:

Values outside these ranges are not invalid, but are clamped to the ranges defined here at computed-value time.

to:

Values outside these ranges are not invalid, but are clamped to the ranges defined here at parsed-value time.

sufficient?

@svgeesus
Copy link
Contributor

It looks like implementations are clamping under-range Lab l and alpha at parse time:
lab(-150% -30 40 / -30) specified value is lab(0 -30 40 / 0)

but over-range Lab l is left unclamped (the spec used to require this, but it was changed a while ago):
lab(600% -30 40 / 300); specified value is lab(600 -30 40)

@svgeesus
Copy link
Contributor

For HWB, both the initial clamp of w and b to [0%, 100%] but also the further normalization on the sum of w and b should be at parse time, right?

If the sum of these two arguments is greater than 100%, then at computed-value time they are further normalized to add up to 100%, with the same relative ratio.

This isn't directly observable, but the specified value (which is in rgb()) shows that this normalization must have taken place.

@svgeesus
Copy link
Contributor

@mysteryDate @lilles please have a look at this commit

I was slightly concerned that "parse-time" doesn't have a convenient spec link, but then again uses of "computed-value time" in this spec weren't linking anywhere either.

Where are these terms defined?

@fantasai
Copy link
Collaborator

fantasai commented Jan 12, 2023

You probably want to just use the terms “computed value” and “specified value”, e.g. “the specified value is clamped to [range]” or whatever. https://www.w3.org/TR/css-cascade-5/#specified Fundamentally the question isn't about what time they get clamped, but what stage’s value gets clamped. :)

@mysteryDate
Copy link
Author

@svgeesus Looks good to me!

@lilles
Copy link
Member

lilles commented Jan 13, 2023

Adding @emilio for Gecko concerns.

@fantasai Isn't specified to late as well? There is something called declared value. I don't even know if that is the correct term if we want it serialized clamped in the stylesheet serialization.

Is the serialization for in the CSSOM spec relevant?:

https://drafts.csswg.org/cssom/#serialize-a-css-component-value

Regarding <alpha-value>, we have existing interop on the opacity property not being clamped for declared values. But also, we have interop on clamping <alpha-value> on the declared rbga() values.

See https://codepen.io/lilles/pen/YzjQEzj

@svgeesus
Copy link
Contributor

svgeesus commented Jan 13, 2023

It is relevant, but 2/3 of it points to CSS Color 4 and I have been discussing with @emilio about moving over the remaining portion as well:

If is a component of a specified value, then return the color as follows:

If the color was explicitly specified by the author, then return the original, author specified color value.
Otherwise, return the value that would be returned if the color were a component of a computed value.

Particularly because that needs to be changed for named colors.

@svgeesus
Copy link
Contributor

There is something called declared value. I don't even know if that is the correct term if we want it serialized clamped in the stylesheet serialization.

The differences between the declared value, the cascaded value, and the specified value seem to boil down to:

  • resolving aliases such as vendor-prefixed properties
  • defaulting properties with no declared value (inheritance, initial values)

So in the case that there is a declared value on a given element, it seems that will be the specified value. Right?

@svgeesus
Copy link
Contributor

svgeesus commented Jan 14, 2023

Particularly because that needs to be changed for named colors.

Now done see
14.1. Resolving sRGB values

@svgeesus
Copy link
Contributor

Not clear to me whether further clarifications are needed or if this can be closed. Can we get consensus on this before Weds 8 Feb?

@mysteryDate
Copy link
Author

At chromium we're happy with the language as it.

@svgeesus svgeesus added Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. Closed Accepted by Editor Discretion and removed Agenda+ labels Jan 31, 2023
@cdoublev
Copy link
Collaborator

cdoublev commented May 21, 2024

  • Clamping at parse time instead of computed time should just affect specified style serialization

It seems to affect color functions taking other colors as arguments, as reported in #10328.

edit: I am wrong (sorry): the spec requires not clamping channel values in a relative color

For example, color(from rgb(306 0 0) srgb r 0 0) would serialize (as a component of a declared value with) color(from rgb(255 0 0) r 0 0) and the computed value would be color(srgb 1 0 0) instead of color(srgb 1.2 0 0), if I am not mistaken.

It may be safer to clamp the serialized value (when not nested in a color function) rather than affect the parsed value. Maybe it would still be needed for a negative saturation in hsl(), negative whiteness/blackness in hwb() (which is now specified in a hidden comment), lightness in <ok?lab()>/<ok?lch()>, negative chromaticity in <ok?lch()>, I am not sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closed Accepted by Editor Discretion Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-color-4 Current Work
Projects
None yet
Development

No branches or pull requests

6 participants