color-js / color.js

Color conversion & manipulation library by the editors of the CSS Color specifications
https://colorjs.io
MIT License
1.93k stars 81 forks source link

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

Open johannesodland opened 2 years ago

johannesodland commented 2 years ago

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.

LeaVerou commented 2 years ago

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.

johannesodland commented 2 years ago

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 👏

LeaVerou commented 2 years ago

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