Closed LeaVerou closed 1 year ago
Another complication: what does color.toJSON()
return for anonymous color spaces?
Perhaps it can just use the color space's name, even if it's not registered?
I think having it always return an object is fine. Object identity will allow comparison just as easily as strings.
Comparing with a built-in is slightly more verbose:
if(color.colorSpace == ColorSpace.get("lch")) {...}
but honestly that doesn't feel too bad? And if it's used a bunch, author code can always stash it with const lch = ColorSpace.get("lch");
.
We could also expose the built-in spaces as statics, so they could type ColorSpace.lch
, if those character savings seem worthwhile.
Another complication: what does color.toJSON() return for anonymous color spaces?
Hrm. The point of toJSON() is to give you an object that, well, is JSON, but also that can be passed directly to the Color
constructor to recreate the color. You can't satisfy both of those with an anonymous space; one has to give. (Either the object has a ColorSpace value, which isn't JSON, or the object has a string like "[anonymous] foobar"
, which doesn't recreate the color.)
So, an alternative might be that if you .toJSON() a color using an anonymous space, it automatically converts to the nearest registered ancestor space and outputs in that. You won't get an exact recreation of the color when you pass it to new Color()
, but you'll get a precise equivalent (assuming it's in-gamut, I guess).
Comparing with a built-in is slightly more verbose:
It's a bit better than that:
if (color.colorSpace.name === "lch") { ... }
Another complication: what does color.toJSON() return for anonymous color spaces?
Hrm. The point of toJSON() is to give you an object that, well, is JSON, but also that can be passed directly to the
Color
constructor to recreate the color. You can't satisfy both of those with an anonymous space; one has to give. (Either the object has a ColorSpace value, which isn't JSON, or the object has a string like"[anonymous] foobar"
, which doesn't recreate the color.)So, an alternative might be that if you .toJSON() a color using an anonymous space, it automatically converts to the nearest registered ancestor space and outputs in that. You won't get an exact recreation of the color when you pass it to
new Color()
, but you'll get a precise equivalent (assuming it's in-gamut, I guess).
Another alternative is to keep the anonymous ColorSpace around as long as possible so it can be fed back to the Color
constructor if need be, and just rely on the regular stringification if converted to JSON that will just turn it into {}
, which is useless, but just as useless as "(anonymous)"
.
I need to think more about the idea of silently converting to an ancestor space, is it helpful or too much unpredictable magic?
Using the name isn't quite reliable (an unregistered space could also say its name is "lch") but yeah, in practice that's just fine. ^_^
just rely on the regular stringification if converted to JSON that will just turn it into {}
Oooh, I didn't realize it would do this. Yes, this seems perfectly fine to me, let's go with that for now.
@tabatkins pointed out that with the current design, anonymous color spaces are possible, by just creating
ColorSpace
objects that are never registered, and passing them around instead of string ids.This would allow web components to use color spaces without polluting the global name space.
However, supporting this use case does bring up some questions.
Currently,
color.colorSpace
is a string, which facilitates easy comparison with strings, i.e. authors can do things likeif (color.colorSpace === "lch") ...
. However, if we want to support anonymous color spaces, there is no string to return in those cases. There are several options here, none ideal:color.colorSpace
would sometimes return a string and sometimes a ColorSpace object. This is not ideal because it's inconsistent, and thus author code would either not take the latter into account and break with such colors, or would need to branch, which is a hasslecolor.colorSpace
always return an object