color.js icon indicating copy to clipboard operation
color.js copied to clipboard

Provide way for display() and serialize() to delegate gamut mapping to Color.js instead of the browser

Open johannesodland opened this issue 2 years ago • 3 comments

The display method returns color as is if the browser supports the serialized color, even if it is outside the current display space.

Gamut mapping is left to the browser as a result. In the current Safari that means gamut clipping, which in turn can lead to shifts in hue or lightness.

It would be nice if the display method had an option to always use color.js gamut mapping to the current display space. Even better if we could control the gamut mapping method.

Example:

// The following color is outside the p3 gamut
const color = 'oklch(75% 0.4 80)'; 
// But the serialised format is supported in Safari 15.5
console.log(CSS.supports('color', color));
// true in Safari 15.5

// As a result 'display'returns the color as is, and gamut mapping is left to the browser
console.log(display(color));
// 'oklch(75% 0.4 80)'

// The color will not be modified, even if we specify a display space
console.log(display(color, {space:p3})):
// 'oklch(75% 0.4 80)'

// It would be nice if color.js could handle gamut mapping to the widest supported display space:
console.log(display(color, {gamutMap: true})):
// color(display-p3 0.8404 0.6218 0)

Side note: I think the current display_space detection will detect Lab as the widest display space even though the screen only supports P3. CSS.supports('color', 'lab(0% 0 0)') returns true, while window.matchMedia('(color-gamut: rec2020)') returns false in Safari 15.5 on a p3 display.

johannesodland avatar Jul 08 '22 06:07 johannesodland

display() takes the same options as serialize(), one of them is inGamut, so your example basically works almost as-is:

console.log(display(color, {inGamut: true})):

Side note: I think the current display_space detection will detect Lab as the widest display space even though the screen only supports P3. CSS.supports('color', 'lab(0% 0 0)') returns true, while window.matchMedia('(color-gamut: rec2020)') returns false in Safari 15.5 on a p3 display.

display() deals with what output formats the browser supports. The idea being that you'd rather let the browser do the gamut mapping if they are able. I wonder if there should be some syntax to do what you expected and check the results of the MQ too.

LeaVerou avatar Jul 08 '22 09:07 LeaVerou

display() is great as it is, I didn't mean to file this as a bug :) I should probably have marked this as a feature request. Forgive my English, it's not my main language 😅

display() deals with what output formats the browser supports. The idea being that you'd rather let the browser do the gamut mapping if they are able.

I agree in theory, but current browsers aren't that great at mapping colors that are outside the screen/display space atm. Safari seems to clip the colors, and does not follow the standardized CSS gamut mapping method yet. That's why it would be nice to have a syntax to have display() use one of the color.js gamut mapping methods in stead :)

That way we could have nice gamut mapping, even if browsers haven't implemented the standardised gamut mapping.

It is in no way critical. It is possible to do this already, by converting to the supported device space before serializing.

I wonder if there should be some syntax to do what you expected and check the results of the MQ too.

That would be great 🙏

Awesome library, btw. Can't thank you guys enough 👏

johannesodland avatar Jul 08 '22 10:07 johannesodland

Happy to add such syntax to display(), the only blocker is that we'd need to come up with a good syntax :)

LeaVerou avatar Jul 08 '22 11:07 LeaVerou