w3c / csswg-drafts

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

[css-color-5] Edge cases for color-mix() percentage serialization questions #10772

Open weinig opened 3 months ago

weinig commented 3 months ago

The current text for serializing color-mix() states:

The serialization of the declared value of a color-mix() function is the string "color-mix(in ", followed by the specified <color-space> in all-lowercase, followed by ", ", followed by the first specified color, followed by a space, followed by the specified (un-normalized) first percentage (unless both percentages are 50%), followed by ", ", followed by the second specified color, followed by the specified (un-normalized) second percentage (unless the two specified percentages add to 100%), followed by ")".

Following the principle of shortest serialization, the second percentage is typically omitted, even if explicitly specified.

Directly after that, in example 43, the first example seems to contradict this:

For example, the serialized declared value of

  • color-mix(in oklab, teal, peru 40%)

would be the string "color-mix(in oklab, teal 60%, peru)".

The text states it should use the "un-normalized" values. The value of 60% would only be discovered during the normalization (specifically, step 4)).

So it seems like we need a slightly more complicated serialization rule. The following wording needs work, but I think mostly gets to the intent:

The serialization of the declared value of a color-mix() function is the string "color-mix(in ", followed by the specified in all-lowercase, followed by ", ", followed by the first specified color, followed by a space, followed by the serialization of the first percentage (see below), followed by ", ", followed by the second specified color, followed by the serialization of the second percentage (see below), followed by ")".

(note for below, calc() values are consider to be unknown, so are NEVER equal 50% and never sum with something else to equal 100%.)

The serialization of the first percentage of a declared value of a color-mix() function is defined to:

  • If BOTH the first percentage p1 and second percentages p2 are specified:
    • if both p1 equals 50% and p2 equals 50%, nothing is serialized. (a)
    • else, p1 is serialized as is. (b)
  • else if ONLY the first percentage p1 is specified:
    • if p1 is equal 50%, nothing is serialized. (c)
    • else, p1 is serialized as is. (d)
  • else if ONLY the second percentage p2 is specified:
    • if p2 equals 50%, nothing is serialized. (e)
    • if p2 is not calc(), the value of 100% - p2 is serialized. (f)
    • else, nothing is serialized. (g)
  • else if NEITHER is specified:
    • nothing is serialized. (h)

The serialization of the second percentage of a declared value of a color-mix() function is defined to:

  • If BOTH the first percentage p1 and second percentages p2 are specified:
    • if neither p1 nor p2 is calc(), and p1 + p2 equals 100%, nothing is serialized. (i)
    • else, p2 is serialized as is. (j)
  • else if ONLY the first percentage p1 is specified:
    • nothing is serialized. (k)
  • else if ONLY the second percentage p2 is specified:
    • if p2 equals 50%, nothing is serialized. (l)
    • if p2 is not calc(), nothing is serialized. (m)
    • else, p2 is serialized as is. (n)
  • else if NEITHER is specified:
    • nothing is serialized. (o)

(to make this somewhat easier to grok, below is a pretty comprehensive list of specified value serializations with a comment to the right explaining which rules it is used for each percentage).

color-mix(in srgb, red 50%,       blue 50%)          -serializes to-     color-mix(in srgb, red, blue)                     // [a & i] both 50%
color-mix(in srgb, red 10%,       blue 90%)          -serializes to-     color-mix(in srgb, red 10%, blue)                 // [b & i] sum to 100%
color-mix(in srgb, red 50%,       blue 40%)          -serializes to-     color-mix(in srgb, red 50%, blue 40%)             // [b & j] only one is 50% (p1)
color-mix(in srgb, red 40%,       blue 50%)          -serializes to-     color-mix(in srgb, red 40%, blue 50%)             // [b & j] only one is 50% (p2)
color-mix(in srgb, red 30%,       blue 40%)          -serializes to-     color-mix(in srgb, red 30%, blue 40%)             // [b & j] sum to less than 100
color-mix(in srgb, red 75%,       blue 75%)          -serializes to-     color-mix(in srgb, red 75%, blue 75%)             // [b & j] sum to more than 100
color-mix(in srgb, red calc(10%), blue 50%)          -serializes to-     color-mix(in srgb, red calc(10%), blue 50%)       // [b & j] one is `calc()` (p1)
color-mix(in srgb, red 50%,       blue calc(10%))    -serializes to-     color-mix(in srgb, red 50%, blue calc(10%))       // [b & j] one is `calc()` (p2)
color-mix(in srgb, red calc(10%), blue calc(90%))    -serializes to-     color-mix(in srgb, red calc(10%), blue calc(90%)) // [b & j] both are `calc()`

color-mix(in srgb, red 50%,       blue)              -serializes to-     color-mix(in srgb, red, blue)                     // [c & k] only p1 specified, is 50%
color-mix(in srgb, red 10%,       blue)              -serializes to-     color-mix(in srgb, red 10%, blue)                 // [d & k] only p1 specified
color-mix(in srgb, red calc(50%), blue)              -serializes to-     color-mix(in srgb, red calc(50%), blue)           // [d & k] only p1 specified, is `calc()`

color-mix(in srgb, red,           blue 50%)          -serializes to-     color-mix(in srgb, red, blue)                     // [e & l] only p2 specified, is 50% 
color-mix(in srgb, red,           blue 90%)          -serializes to-     color-mix(in srgb, red 10%, blue)                 // [f & m] only p2 specified 
color-mix(in srgb, red,           blue calc(50%))    -serializes to-     color-mix(in srgb, red, blue calc(50%))           // [g & n] only p2 specified, is `calc()` 

color-mix(in srgb, red,           blue)              -serializes to-     color-mix(in srgb, red, blue)                     // [g & o] neither is specified
weinig commented 3 months ago

cc @svgeesus, @LeaVerou

svgeesus commented 3 months ago

Thanks @weinig both for the suggested wording and also especially for the set of worked examples!

svgeesus commented 3 months ago

The text states it should use the "un-normalized" values. The value of 60% would only be discovered during the normalization (specifically, step 4)).

This is true.

Because this section evolved over time, I think that my mental model was whether a number had changed, like color-mix(in oklab, red 75%, blue 150%) becoming 33.33% and 66.67% respectively; and I originally wrote that those modified values would be the ones serialized. But then there were problems, like knowing if they had originally added to less than 100%, so we went for the un-normalized wording without noticing that unspecified values only get a value during normalization.

Your suggested wording is much better.

svgeesus commented 2 months ago

@emilio @kbabbitt @nt1m are we good with these suggested changes? And if so we should be get a WG resolution.

svgeesus commented 2 months ago

@emilio @kbabbitt @nt1m it would be great to get any concerns logged here, before TPAC, so that we can get a quick resolution to adopt the wording from @weinig and also get the examples into WPT tests.

nt1m commented 2 months ago

I trust @weinig :)

kbabbitt commented 2 months ago

@weinig @svgeesus

Thanks for the writeup and the ping. I looked at the proposed text and existing test cases in color-valid-color-mix-function.html. Am I understanding correctly that the changes would only clarify the existing behavior and not introduce any changes to expected serializations? If that's the case, I'm happy with the new wording.

If there are behavior changes in the wording that I missed, could you please point them out?

svgeesus commented 2 months ago

Am I understanding correctly that the changes would only clarify the existing behavior and not introduce any changes to expected serializations?

Exactly.

svgeesus commented 2 months ago

@astearns it seems consensus is being reached, and also this issue is on the TPAC overflow area so unlikely to be discussed.

Suggested async resolution: Adopt the suggested wording from @weinig regarding color-mix() edge cases

emilio commented 2 months ago

This sounds good to me too fwiw, sorry for the lag.

astearns commented 2 months ago

The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.

Proposed Resolution: Adopt the color-mix() serialization suggestions from @weinig

astearns commented 1 month ago

RESOLVED: Adopt the color-mix() serialization suggestions from @weinig