Re: [csswg-drafts] [css-color-4][Editorial?] Clarify that `none` is preserved in calculations (#10211)

The CSS Working Group just discussed ``[css-color-4][Editorial?] Clarify that `none` is preserved in calculations``, and agreed to the following:

* `RESOLVED: None is preserved in calculations involving css math functions`
* ``RESOLVED: When interpolating between two none-containing values, the result is a calc-mix() expression preserving the `none` keywords in both values``

<details><summary>The full IRC log of that discussion</summary>
&lt;fantasai> lea: Awhile back we invented 'none' value for color components to represent achromatic colors (that don't have a particular hue)<br>
&lt;fantasai> lea: when converting gray to a polar format, which hue do you use? none are relevant<br>
&lt;fantasai> lea: then we expanded to chroma and saturation for same reason<br>
&lt;fantasai> lea: eventually it turns out once it was deployed, very useful in many more use cases<br>
&lt;fantasai> lea: lets you represent colors that are parameterizable, that get meaning based on where you use them<br>
&lt;fantasai> lea: e.g. color with nones and lightness, and mix with it to get tints<br>
&lt;fantasai> lea: with relative color syntax becomes even more useful<br>
&lt;fantasai> lea: when we wrote the spec, what do you output a color that has 'none'?<br>
&lt;fantasai> lea: so we decided to make it convert to zero<br>
&lt;dholbert> scribe+<br>
&lt;dholbert> lea: when you're converting from one color space to another, e.g. rgb to lch<br>
&lt;dholbert> ... you don't want to expose color space conversion math<br>
&lt;ChrisL> q+ to agree that coercing none to 0 should be deferred as much as possible, ie used value time<br>
&lt;dholbert> ... this conversion to zero is like a last ditch effort. last thing you do if there's nothing else reasonable to do<br>
&lt;dholbert> ... the way this was interpreted by impls, you also convert to zero when you can't do a computation, e.g. calc(h)<br>
&lt;dholbert> ... this is against author intent; h and calc(h) should really be the same<br>
&lt;dholbert> ... you could imagine having a color in relative color syntax and using calc or calc-mix to ?<br>
&lt;dholbert> ... all of these would be solved if we resolved that 'none' was preserved during calculations. don't convert nones to anything, nones remain nones<br>
&lt;dholbert> ... I've written details about how this might work in the issue<br>
&lt;dholbert> ... interpolation operations have the behavior of the current value of none, only have this behavior if the other value is not "none-containing"<br>
&lt;dholbert> ... if you're interpolating between a value with none to some other value, you get the non-none-containing value<br>
&lt;dholbert> ... [missed some]<br>
&lt;dholbert> ... this reduces magic/discontinuities.<br>
&lt;Rossen__> q?<br>
&lt;dholbert> ... Also need to sort out how to handle this for a syntax that allows 'none', e.g. clamp's 1st or 3rd argument. Is it the 'none' that means no bound, or 'none' that means the color meaning of 'none'?<br>
&lt;dholbert> ... we just need to define precedence. I'd argue for using the color meaning of 'none', but I don't care too much<br>
&lt;Romain> q+<br>
&lt;dholbert> ... main thing I care about is that 'none' values are not dropped<br>
&lt;dholbert> .... ChrisL suggests that this is just an editorial change, but Romain said it's editorial; hence, running by the group<br>
&lt;emilio> q+<br>
&lt;astearns> ack ChrisL<br>
&lt;Zakim> ChrisL, you wanted to agree that coercing none to 0 should be deferred as much as possible, ie used value time<br>
&lt;dholbert> ChrisL: I no longer thing this is editorial. main thing is preserving author intent. Coercing none to zero is an option of last resort if you're literally trying to draw 'none'<br>
&lt;dholbert> ChrisL: other cases, hue of 'none', does that mean they really want a bright magenta red? no<br>
&lt;Rossen__> ack ChrisL<br>
&lt;dholbert> ChrisL: we're seeing people comment on this in implementations, people complaining about why-are-they-getting-a-red-arbitrarily<br>
&lt;TabAtkins> +1 to preserving none. I think it's probably fine to treat `clamp()`'s none as the color none in RCS.<br>
&lt;lea> +q to say let's not conflate preserving none in calculations with preserve none during color space conversions<br>
&lt;TabAtkins> (Because, as noted, you can always write `clamp(-infinity, val, infinity)` instead.)<br>
&lt;dholbert> ChrisL: regardless of the mechanics, the general thing of "we need to preserve this value unless we can get rid of it" is the main thing. we can talk about ways to achieve it separately if needed<br>
&lt;Rossen__> ack Romain<br>
&lt;lea> qq+ to reply to romain<br>
&lt;dholbert> Romain: I was wondering if this is implementable. I assume browsers have an internal representation of color that's channel values, but with this proposal you need to preserve much more information about how the color was specified<br>
&lt;Romain> calc(none + 0.2)<br>
&lt;dholbert> Romain: I think this would be very surprising when you move this across color spaces. When you write: [...]<br>
&lt;dholbert> Romain: if you write this^ in LCH or oklch, the 0.2 has a different scale in both. But if you switch color spaces, it gets preserved and not converted, and that can be surprising<br>
&lt;dholbert> Romain: not an absolute blocker, but does have sharp edges<br>
&lt;dholbert> lea: RE implementability, browsers have to do this with variable references already<br>
&lt;dholbert> emilio: that's not how variable reference works; they're substituted before parsing<br>
&lt;dholbert> emilio: you don't have a concept of a calc with variable references; you have a string with variable references substituted<br>
&lt;dholbert> lea: my point is that not every component actually resolves. Even in rel color syntax, you keep the values of the base color [??]<br>
&lt;ChrisL> right but you don't have an immediately displayable color<br>
&lt;dholbert> lea: let's not conflate preserving none in color space conversions &amp; in calculations<br>
&lt;dholbert> lea: converting btwn color spaces is a whole different beast; that's one of the reasons resolving to 0 was invented<br>
&lt;dholbert> lea: don't want to convert none in a way that exposes color space conversion math<br>
&lt;Romain> q+<br>
&lt;Rossen__> ack lea<br>
&lt;Zakim> lea, you wanted to react to Romain to reply to romain<br>
&lt;ChrisL> https://drafts.csswg.org/css-color-4/#analogous-components<br>
&lt;dholbert> lea: the spec also has a part where if you're converting between two color spaces, where one component of first color space is largely orthogonal to the rest and related to a component in color space b, then swap the none out for zero, and then replace the none in that component [??]<br>
&lt;chrishtr> again+<br>
&lt;chrishtr> q+<br>
&lt;dholbert> lea: let's discuss the color space conversion separately; it's hairy<br>
&lt;dholbert> emilio: I agree that preserving 'none' in some way makes sense<br>
&lt;Rossen__> ack emilio<br>
&lt;chrishtr> q-<br>
&lt;dholbert> emilio: I was going to say something similar to Romain<br>
&lt;dholbert> emilio: when you mix 'none' in more complex calculations... have you given thought to making 'none' more similar to how NaN works? as soon as you mix something with 'none', it remains 'none' instead of being an expression involving 'None'?<br>
&lt;chrishtr> q+<br>
&lt;ChrisL> We invented none because the spec said NaN and tab told us not to :)<br>
&lt;lea> q+<br>
&lt;dholbert> emilio: that would be less complex to implement. Interpolating without having to [...] transforms have such a thing, where you have to keep them as a mix that isn't simplified<br>
&lt;ChrisL> qq+<br>
&lt;dholbert> emilio: that seems like a less confusing model, where doing math with 'None' gives you 'None'<br>
&lt;dholbert> ChrisL: That's what the spec used to say; it used NaN explicitly<br>
&lt;dholbert> ChrisL: Tab suggested we use a special value, which is where None came from<br>
&lt;dholbert> TabAtkins: My argument against NaN was for other reasons, unrelated to infectiousness<br>
&lt;Rossen__> ack ChrisL<br>
&lt;Zakim> ChrisL, you wanted to react to emilio<br>
&lt;lea> Lea: infectious none is actually worse than converting it to 0. E.g. clamp(50, none, 60) is conceptually very different than none, you're basically saying "I want the component of the other color, but it needs to be within this range. Converting the whole component to none in that case could even harm accessibility, if the constraint was there to e.g. ensure sufficient contrast.<br>
&lt;Rossen__> ack lea<br>
&lt;Zakim> lea, you wanted to say let's not conflate preserving none in calculations with preserve none during color space conversions and to<br>
&lt;dholbert> lea: infections 'none' is worse than converting it to zero<br>
&lt;dholbert> lea: if you have an expression clamping 'none' to 50 or 60, the author wants it to be in this range<br>
&lt;ChrisL> https://github.com/w3c/csswg-drafts/issues/6107<br>
&lt;dholbert> lea: if you convert the whole thing to none, then that can harm a11y<br>
&lt;dholbert> Romain: this is still related to color space conversion because the spec says you have to convert to the same color space when doing interpolation<br>
&lt;dholbert> ChrisL: that's correct, because of missing components<br>
&lt;TabAtkins> (We shouldn't use NaN to indicate "no channel value" because NaN *also* comes from simple math errors. We didn't want "author made a math error" to accidentally also trigger "this channel is powerless and should be taken from the opposing color". The infectiousness wasn't a part of this.)<br>
&lt;Rossen__> ack chrishtr<br>
&lt;Rossen__> ack Rossen__<br>
&lt;Rossen__> ack Romain<br>
&lt;dholbert> chris-harrelson: what does calc(none  + 20) resolve to?<br>
&lt;dholbert> lea: that's well-defined in the issue. calc(none+0) -- the none would become the value of that component in another color, and then you add 20<br>
&lt;dholbert> lea: get the lightness of the other color, and then subtract 20 from it<br>
&lt;dholbert> lea: e.g. if you're trying to interpolate from a certain color to a darker version of it, regardless of what color you're starting with<br>
&lt;dholbert> chris-harrelson: so that means zero is not the right number, in those use-cases<br>
&lt;ChrisL> So setting hue to calc(none+180) gives you the complement of the other color, when interpolating for example<br>
&lt;dholbert> lea: correct, zero is almost never the right number<br>
&lt;emilio> q+<br>
&lt;emilio> ack emilio<br>
&lt;dholbert> ChrisL: I've got a suggested resolution for what we want to achieve, it sounds like we have consensus on that?<br>
&lt;dholbert> ChrisL: "don't get rid of none and replace it with zero unless you absolutely have to, e.g. if you're forced to use it as a used value"<br>
&lt;dholbert> lea: that might be all we need?<br>
&lt;dholbert> emilio: I agree<br>
&lt;lea> I wonder if implementations could even internally rewrite to RCS, in some ways it's like a late-resolving RCS<br>
&lt;dholbert> emilio: I sort of want to review the proposed changes with more detail; the details of how you do that might be a bit tricky<br>
&lt;dholbert> lea: proposed: None is preserved in calculations involved in css math functions<br>
&lt;ChrisL> s/none+0/none+20<br>
&lt;dholbert> s/involved in/involving/<br>
&lt;dholbert> lea: ...and also when interpolating between values containing 'none' and values not-containing-none<br>
&lt;ChrisL> +1<br>
&lt;dholbert> RESOLVED: None is preserved in calculations involving css math functions<br>
&lt;lea> s/None is preserved in calculations involved in css math functions/`none` is preserved in calculations involving CSS math functions<br>
&lt;lea> When interpolating between two none-containing values, the result is a calc-mix() expression preserving the `none` values in both values<br>
&lt;dholbert> PROPOSED: ^<br>
&lt;ChrisL> +1<br>
&lt;lea> PROPOSED: When interpolating between two none-containing values, the result is a calc-mix() expression preserving the `none` keywords in both values<br>
&lt;dholbert> dholbert: do we need to cover interpolating between none/not-none?<br>
&lt;dholbert> lea: that's already defined<br>
&lt;flackr> q+<br>
&lt;Rossen__> ack flackr<br>
&lt;dholbert> flackr: I see in the issue description that there's also a proposal that 'none' resolves to the other component, if the other component is not none-containing<br>
&lt;Romain> q+<br>
&lt;dholbert> lea: that's already how none works / what none does<br>
&lt;dholbert> Romain: that's not true; it's not a placeholder for the other channel. It makes a channel missing<br>
&lt;dholbert> Romain: it's not like the nesting selector where it's a placeholder for the parent<br>
&lt;dholbert> Romain: this would be a new mechanic. I'm fine with it being a new mechanic, but we should call it that<br>
&lt;dholbert> ChrisL: that's already in the spec?<br>
&lt;dholbert> Romain: that's related to color components<br>
&lt;dholbert> flackr: I'd like to know what's used in the other end of the interpolation. pulling the color from the other end isn't what authors would expect<br>
&lt;TabAtkins> That is actually intended behavior, yes<br>
&lt;dholbert> flackr: if you're pulling from 2 color values and a none in the middle, pulling from two color values at the ends isn't what authors would expect<br>
&lt;dholbert> lea: that's an orthogonal issue and what happens today<br>
&lt;dholbert> Rossen__: objections?<br>
&lt;dholbert> RESOLVED: When interpolating between two none-containing values, the result is a calc-mix() expression preserving the `none` keywords in both values<br>
&lt;schenney> present -<br>
&lt;dholbert> [end of meeting]<br>
</details>


-- 
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/10211#issuecomment-2075435167 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 24 April 2024 17:05:35 UTC