Re: [css-houdini-drafts] [css-typed-om] CSSColorValue section needs WG review (#1039)

In preparation for today's call, a few notes about why I think [`CSSColorValue`](https://drafts.css-houdini.org/css-typed-om/#colorvalue-objects) should **not** be used more broadly for color input and output on the Web Platform.

### Missing functionality and algorithms

Although there is a lot of missing functionality, we should not focus on that. 
Indeed, missing functionality can always be added (see #1040), though I do think that adding a bunch of color math methods and extensibility points to a Typed OM object is kinda *weird*. 

First, the extensibility needed for a Color object is broader than the kind of extensibility afforded by CSS itself (e.g. in JS adding new color spaces can be done by providing conversion code -- CSS's `@color-profile` cannot provide that).

Most algorithms in the spec are also currently very underspecified. Sure, we can point to the CSS Color module for certain things, but a JS API brings up different challenges compared to a declarative CSS one. How does conversion happen? When does gamut mapping happen and how? Sure, these can be fleshed out, but since part of Tab's argument was that work on a Color API is "still far off" so we should use this because it's supposed to be good enough and is already here, it should be mentioned.

But beyond functionality and algorithms, even in terms of just API shape, there are certain things that cannot change without reducing the utility of `CSSColorValue` as part of the Typed OM, but they make it quite painful for authors if used as a general Color API.

### Using CSSNumericValue objects instead of numbers 

Even though the constructor accepts `CSSNumberish`, so both `CSSNumericValue` and regular doubles are accepted as input, the getters still have to return a `CSSNumericValue` object, which makes manipulations painful, verbose, and confusing.

Let's take the most simple color manipulation case: where we want to increase color's lightness by 20% and we don't even care about the color space of the result.

The "easy" case, is if the object has been constructed from doubles, or `CSSUnitValue` values:

```js
let lchColor = color.to("lch");
color.l.value = color.l.value * 1.2;
```

Even in that case, every getter needs to have `.value` after it to get the actual number. Being unaware of this and trying something like `color.l + 10` will perform string concatenation, with results utterly baffling to authors. It honestly brings back memories of working with the SVG DOM.

If this was about hue, because these objects need to reflect the actual CSS syntax it means authors that want to handle hues need to first convert whatever `CSSUnitValue` object they get into a number of degrees (since most algorithms expect that).

But, like I said, this is the "easy" case. It's verbose, but at least workable. It gets *a lot* more complicated if any of the numbers can be `CSSMathValue`, or in the common case where we simply don't know and need to handle all possibilities. After looking at both the [`CSSNumericValue` superclass](https://drafts.css-houdini.org/css-typed-om/#numeric-value) and the [`CSSMathValue` subclass](https://drafts.css-houdini.org/css-typed-om/#complex-numeric), **I ...actually don't know how to perform a color manipulation even as simple as increasing LCH lightness, or even reading the current one.** Maybe by recursively calling the math methods to try and get a resolved value? And what happens when a resolved value is not possible, e.g. because of variable references? This means that every single color manipulation method on this API needs to take that into account, and every snippet of author code needs to also account for this. It cannot be guaranteed that any code that accepts colors and wishes to perform some math on them can actually produce a result!

All this complexity is required to represent CSS constructs (which is the goal of Typed OM), because in CSS you can have e.g. `lch(calc(var(--hue) + 5deg) 10 10)` and it needs to be preserved for the purposes of representing CSS syntax. However, it does *not* need to be preserved for a Color API. That would mean every API method, and every bit of author code that needs to handle colors needs to branch for this. A separate Color API can just accept `CSSColorValue` objects in its constructor and throw if their values cannot be resolved, then anything from then on operates on plain ol' numbers.

Actually, now that I think about it, how would any other API (e.g. Canvas) handle this, if they use `CSSColorValue` objects as inputs?!

### Abstract superclass + subclasses architecture

Because this is Typed OM and needs to closely represent CSS syntax, there is a number of subclasses that each have a different API. sRGB colors can either be `CSSRGB` or `CSSColor`. In the first case their red coordinate is read via `color.r.value`, in the second case via `color.channels[0].value`. Any code that needs to be able to handle any color value needs to do a lot of annoying conditional branching about what class they have.



-- 
GitHub Notification of comment by LeaVerou
Please view or discuss this issue at https://github.com/w3c/css-houdini-drafts/issues/1039#issuecomment-844185998 using your GitHub account


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

Received on Wednesday, 19 May 2021 14:56:20 UTC