mirror of
https://github.com/sass/sass.git
synced 2024-09-21 10:37:22 +00:00
[Color 4] Require an explicit gamut mapping method (#3836)
This commit is contained in:
parent
f4cf151046
commit
465f0db7d1
@ -1,3 +1,9 @@
|
||||
## Draft 1.8
|
||||
|
||||
* Change `SassColor.toGamut()` to take named parameters instead of positional.
|
||||
|
||||
* Add a mandatory `method` parameter to `SassColor.toGamut()`.
|
||||
|
||||
## Draft 1.7
|
||||
|
||||
* Don't throw errors for out-of-gamut lightness values.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.7
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.8
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2831),
|
||||
[Changelog](color-4-new-spaces-js.changes.md))*
|
||||
@ -116,6 +116,8 @@ export type HueInterpolationMethod =
|
||||
| 'increasing'
|
||||
| 'longer'
|
||||
| 'shorter';
|
||||
|
||||
export type GamutMapMethod = 'clip' | 'local-minde';
|
||||
```
|
||||
|
||||
### New Color Functions
|
||||
@ -171,13 +173,16 @@ isInGamut(space?: KnownColorSpace): boolean;
|
||||
|
||||
#### `toGamut`
|
||||
|
||||
Returns the result of [`color.to-gamut(internal, space)`].
|
||||
Returns the result of [`color.to-gamut(internal, space, method)`].
|
||||
|
||||
```ts
|
||||
toGamut(space?: KnownColorSpace): SassColor;
|
||||
toGamut(options: {
|
||||
space?: KnownColorSpace;
|
||||
method: 'clip' | 'local-minde';
|
||||
}): SassColor;
|
||||
```
|
||||
|
||||
[`color.to-gamut(internal, space)`]: ./color-4-new-spaces.md#colorto-gamut-1
|
||||
[`color.to-gamut(internal, space, method)`]: ./color-4-new-spaces.md#colorto-gamut-1
|
||||
|
||||
#### `channelsOrNull`
|
||||
|
||||
|
@ -1,3 +1,10 @@
|
||||
## Draft 1.15
|
||||
|
||||
* Add a mandatory `$method` parameter to `color.to-gamut()` for
|
||||
forwards-compatibility with better gamut-mapping algorithms.
|
||||
|
||||
* Add `clip` as a gamut-mapping algorithm.
|
||||
|
||||
## Draft 1.14
|
||||
|
||||
* Update the definition of powerless for HWB to match [the latest CSS
|
||||
|
@ -1,4 +1,4 @@
|
||||
# CSS Color Level 4, New Color Spaces: Draft 1.14
|
||||
# CSS Color Level 4, New Color Spaces: Draft 1.15
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2831))*
|
||||
|
||||
@ -54,6 +54,8 @@ colors outside the sRGB gamut.
|
||||
* [Converting a Color](#converting-a-color)
|
||||
* [CSS-Converting a Color Space](#css-converting-a-color-space)
|
||||
* [Gamut Mapping](#gamut-mapping-1)
|
||||
* [`local-minde`](#local-minde)
|
||||
* [`clip`](#clip)
|
||||
* [Parsing Color Components](#parsing-color-components)
|
||||
* [Percent-Converting a Number](#percent-converting-a-number)
|
||||
* [Validating a Color Channel](#validating-a-color-channel)
|
||||
@ -325,20 +327,21 @@ one or more of its channels out of bounds, like `rgb(300 0 0)`).
|
||||
|
||||
#### `color.to-gamut()`
|
||||
|
||||
This function returns a color that is in the given gamut, using the recommended
|
||||
[CSS Gamut Mapping Algorithm][css-mapping] to 'map' out-of-gamut colors into
|
||||
the desired gamut with as little perceptual change as possible. In many cases
|
||||
this can be more reliable for generating fallback values, rather than the
|
||||
'channel clipping' approach used by current browsers.
|
||||
This function returns a color that is in the given gamut, using the given
|
||||
mapping algorithm to convert out-of-gamut colors into the desired gamut with as
|
||||
little perceptual change as possible. The current recommended algorithm is
|
||||
`local-minde`, which is defined in the current candidate recommendation of CSS
|
||||
Color 4, which can in many cases be more reliable for generating fallback values
|
||||
than the "channel clipping" approach used by current browsers.
|
||||
|
||||
```scss
|
||||
$green: oklch(0.8 2 150);
|
||||
|
||||
// oklch(0.91 0.14 164)
|
||||
$rgb: color.to-gamut($green, "srgb");
|
||||
$rgb: color.to-gamut($green, "srgb", $method: local-minde);
|
||||
|
||||
// oklch(0.91 0.16 163)
|
||||
$p3: color.to-gamut($green, "display-p3");
|
||||
$p3: color.to-gamut($green, "display-p3", $method: local-minde);
|
||||
```
|
||||
|
||||
#### `color.is-powerless()`
|
||||
@ -508,14 +511,27 @@ and mapping in Sass color functions:
|
||||
|
||||
#### Gamut Mapping
|
||||
|
||||
Browsers currently use channel-clipping rather than the proposed
|
||||
[css gamut mapping algorithm][css-mapping] to handle colors that cannot be
|
||||
shown correctly on a given display. We've decided to provide `color.to-gamut()`
|
||||
as a way for authors to opt-into the proposed behavior, aware that browsers
|
||||
may eventually choose to provide a different algorithm. If that happens, we
|
||||
will consider adding an additional algorithm-selection argument. However, the
|
||||
primary goal of this function is not to match CSS behavior, but to provide a
|
||||
better mapping than the default channel-clipping.
|
||||
Browsers currently use channel-clipping rather than the proposed [css gamut
|
||||
mapping algorithm][css-mapping] to handle colors that cannot be shown correctly
|
||||
on a given display. Moreover, there is still active discussion among the CSS
|
||||
working group and browser vendors about what the best gamut mapping algorithm
|
||||
is, with the currently-specified algorithm widely considered to be sub-optimal.
|
||||
As such, we want to avoid baking it in as the default for Sass such that any
|
||||
change would require a deprecation period.
|
||||
|
||||
At the same time, we expect gamut-mapping to be very useful for Sass authors
|
||||
working with wide-gamut color spaces, enough so that we want it to be available
|
||||
without needing to wait on the CSSWG to come to a consensus on the best
|
||||
algorithm. To that end, we've decided to provide `color.to-gamut()` but
|
||||
*require* a `$method` argument for forwards-compatibility with better gamut
|
||||
mapping algorithms.
|
||||
|
||||
Initially, there will only be two available arguments for `$method`:
|
||||
`local-minde`, the gamut mapping algorithm specified in Color 4 at the time this
|
||||
spec is written, and `clip` which will just clip any channel values that are
|
||||
out-of-range for a given color space. However, we expect to expand this to
|
||||
additional algorithms in the future, and to eventually make it optional and have
|
||||
its default match the behavior of CSS.
|
||||
|
||||
#### CSS Color 5
|
||||
|
||||
@ -1016,28 +1032,48 @@ The individual conversion algorithms are:
|
||||
> available 'in-gamut' color. Gamut mapping is the process of finding an
|
||||
> in-gamut color with the least objectionable change in visual appearance.
|
||||
|
||||
Gamut mapping in Sass follows the [CSS gamut mapping algorithm][css-mapping].
|
||||
This procedure accepts a color `origin`, and a [known color space]
|
||||
`destination`. It returns the result of a [CSS gamut map][css-map] procedure,
|
||||
converted back into the original color space.
|
||||
Gamut mapping color `origin`, a [known color space] `destination`, and an
|
||||
unquoted string `method`. It returns a color in `origin`'s color space.
|
||||
|
||||
* Let `origin-space` be `origin`'s color space.
|
||||
|
||||
* If either `origin-space` or `destination` is not a [known color space], throw
|
||||
an error.
|
||||
|
||||
* Let `mapped` be the result of [CSS gamut mapping][css-mapping] `origin`
|
||||
color, with an origin color space of `origin-space`, and destination of
|
||||
`destination`.
|
||||
* Let `color` be the result of [converting] `origin` into `destination`.
|
||||
|
||||
* If `method` is not one of the methods defined below, throw an error.
|
||||
|
||||
* Let `mapped` be the result of running the method defined below whose name
|
||||
matches `method`. If no such method matches, throw an error.
|
||||
|
||||
* Return the result of [converting] `mapped` into `origin-space`.
|
||||
|
||||
> This algorithm implements a relative colorimetric intent, and colors inside
|
||||
> the destination gamut are unchanged. Since the process is lossy, authors
|
||||
> should be encouraged to let the browser handle gamut mapping when possible.
|
||||
#### `local-minde`
|
||||
|
||||
[css-mapping]: https://www.w3.org/TR/css-color-4/#css-gamut-mapping-algorithm
|
||||
[css-map]: https://www.w3.org/TR/css-color-4/#css-gamut-map
|
||||
The `local-minde` gamut mapping procedure in Sass follows the 13 February 2024
|
||||
draft of CSS Color Module Level 4. It returns the result of [CSS gamut
|
||||
mapping][css-mapping] `origin` with an origin color space of `origin-space` and
|
||||
destination of `destination`.
|
||||
|
||||
[css-mapping]: https://www.w3.org/TR/2024/CRD-css-color-4-20240213/#css-gamut-mapping-algorithm
|
||||
|
||||
> This algorithm implements a relative colorimetric intent, and colors inside
|
||||
> the destination gamut are unchanged.
|
||||
|
||||
#### `clip`
|
||||
|
||||
The `clip` gamut mapping procedure is not expected to produce good-looking
|
||||
results, but it can be useful to match the current behavior of browsers.
|
||||
|
||||
* Let `new-color` be a copy of `color`.
|
||||
|
||||
* For each `channel` in `destination`:
|
||||
|
||||
* If `channel` is bounded, set `new-color`'s `channel` value to the result of
|
||||
clamping the original value within `channel`'s minimum and maximum values.
|
||||
|
||||
* Return `new-color`.
|
||||
|
||||
### Parsing Color Components
|
||||
|
||||
@ -1626,11 +1662,14 @@ is-in-gamut($color, $space: null)
|
||||
### `color.to-gamut()`
|
||||
|
||||
```
|
||||
to-gamut($color, $space: null)
|
||||
to-gamut($color, $space: null, $method: null)
|
||||
```
|
||||
|
||||
* If `$color` is not a color, throw an error.
|
||||
|
||||
* If `$method` is not an unquoted string whose value is either `local-minde` or
|
||||
`clip`, throw an error.
|
||||
|
||||
* If `$space` is null:
|
||||
|
||||
* Let `origin-space` be the result of calling `color.space($color)`.
|
||||
@ -1641,8 +1680,8 @@ to-gamut($color, $space: null)
|
||||
* Otherwise, let `target-space` be the result of [looking up a known color
|
||||
space] named `$space`.
|
||||
|
||||
* Return the result of [gamut mapping] `$color` with a `target-space`
|
||||
destination.
|
||||
* Return the result of [gamut mapping] `$color` with destination `target-space`
|
||||
and method `$method`.
|
||||
|
||||
[gamut mapping]: #gamut-mapping
|
||||
|
||||
|
1
js-api-doc/index.d.ts
vendored
1
js-api-doc/index.d.ts
vendored
@ -58,6 +58,7 @@ export {
|
||||
ColorSpaceLab,
|
||||
ColorSpaceRgb,
|
||||
ColorSpaceXyz,
|
||||
GamutMapMethod,
|
||||
HueInterpolationMethod,
|
||||
KnownColorSpace,
|
||||
ListSeparator,
|
||||
|
32
js-api-doc/value/color.d.ts
vendored
32
js-api-doc/value/color.d.ts
vendored
@ -79,6 +79,25 @@ export type HueInterpolationMethod =
|
||||
| 'longer'
|
||||
| 'shorter';
|
||||
|
||||
/**
|
||||
* Methods by which colors in bounded spaces can be mapped to within their
|
||||
* gamut.
|
||||
*
|
||||
* * `local-minde`: The algorithm specified in [the original Color Level 4
|
||||
* candidate recommendation]. This maps in the Oklch color space, using the
|
||||
* [deltaEOK] color difference formula and the [local-MINDE] improvement.
|
||||
*
|
||||
* * `clip`: Clamp each color channel that's outside the gamut to the minimum or
|
||||
* maximum value for that channel. This algorithm will produce poor visual
|
||||
* results, but it may be useful to match the behavior of other situations in
|
||||
* which a color can be clipped.
|
||||
*
|
||||
* [the original Color Level 4 candidate recommendation]: https://www.w3.org/TR/2024/CRD-css-color-4-20240213/#css-gamut-mapping
|
||||
* [deltaEOK]: https://www.w3.org/TR/2024/CRD-css-color-4-20240213/#color-difference-OK
|
||||
* [local-MINDE]: https://www.w3.org/TR/2024/CRD-css-color-4-20240213/#GM-chroma-local-MINDE
|
||||
*/
|
||||
export type GamutMapMethod = 'clip' | 'local-minde';
|
||||
|
||||
/**
|
||||
* Sass's [color type](https://sass-lang.com/documentation/values/colors).
|
||||
*
|
||||
@ -269,14 +288,13 @@ export class SassColor extends Value {
|
||||
|
||||
/**
|
||||
* Returns a copy of this color, modified so it is in-gamut for the specified
|
||||
* `space`—or the current color space if `space` is not specified—using the
|
||||
* recommended [CSS Gamut Mapping Algorithm][css-mapping] to map out-of-gamut
|
||||
* colors into the desired gamut with as little perceptual change as possible.
|
||||
*
|
||||
* [css-mapping]:
|
||||
* https://www.w3.org/TR/css-color-4/#css-gamut-mapping-algorithm
|
||||
* `space`—or the current color space if `space` is not specified—using
|
||||
* `method` to map out-of-gamut colors into the desired gamut.
|
||||
*/
|
||||
toGamut(space?: KnownColorSpace): SassColor;
|
||||
toGamut(options: {
|
||||
space?: KnownColorSpace;
|
||||
method: GamutMapMethod;
|
||||
}): SassColor;
|
||||
|
||||
/**
|
||||
* A list of this color's channel values (excluding alpha), with [missing
|
||||
|
1
js-api-doc/value/index.d.ts
vendored
1
js-api-doc/value/index.d.ts
vendored
@ -34,6 +34,7 @@ export {
|
||||
ColorSpaceXyz,
|
||||
ChannelNameXyz,
|
||||
ChannelName,
|
||||
GamutMapMethod,
|
||||
KnownColorSpace,
|
||||
PolarColorSpace,
|
||||
RectangularColorSpace,
|
||||
|
@ -95,6 +95,7 @@ export {
|
||||
ColorSpaceLab,
|
||||
ColorSpaceRgb,
|
||||
ColorSpaceXyz,
|
||||
GamutMapMethod,
|
||||
HueInterpolationMethod,
|
||||
KnownColorSpace,
|
||||
ListSeparator,
|
||||
|
@ -26,6 +26,7 @@ import {Value} from './index';
|
||||
* [`PolarColorSpace`](#polarcolorspace)
|
||||
* [`RectangularColorSpace`](#rectangularcolorspace)
|
||||
* [`HueInterpolationMethod`](#hueinterpolationmethod)
|
||||
* [`GamutMapMethod`](#gamutmapmethod)
|
||||
* [`SassColor`](#sasscolor)
|
||||
* [`internal`](#internal)
|
||||
* [Constructor](#constructor)
|
||||
@ -224,6 +225,14 @@ export type HueInterpolationMethod =
|
||||
| 'shorter';
|
||||
```
|
||||
|
||||
### `GamutMapMethod`
|
||||
|
||||
Methods by which colors in bounded spaces can be mapped to within their gamut.
|
||||
|
||||
```ts
|
||||
export type GamutMapMethod = 'clip' | 'local-minde';
|
||||
```
|
||||
|
||||
### `SassColor`
|
||||
|
||||
The JS API representation of a Sass color.
|
||||
@ -552,13 +561,16 @@ isInGamut(space?: KnownColorSpace): boolean;
|
||||
|
||||
#### `toGamut`
|
||||
|
||||
Returns the result of [`color.to-gamut(internal, space)`].
|
||||
Returns the result of [`color.to-gamut(internal, space, method)`].
|
||||
|
||||
```ts
|
||||
toGamut(space?: KnownColorSpace): SassColor;
|
||||
toGamut(options: {
|
||||
space?: KnownColorSpace;
|
||||
method: GamutMapMethod;
|
||||
}): SassColor;
|
||||
```
|
||||
|
||||
[`color.to-gamut(internal, space)`]: ../../../accepted/color-4-new-spaces.md#colorto-gamut-1
|
||||
[`color.to-gamut(internal, space, method)`]: ../../../accepted/color-4-new-spaces.md#colorto-gamut-1
|
||||
|
||||
#### `channelsOrNull`
|
||||
|
||||
|
@ -37,6 +37,7 @@ export {
|
||||
ColorSpaceXyz,
|
||||
ChannelNameXyz,
|
||||
ChannelName,
|
||||
GamutMapMethod,
|
||||
KnownColorSpace,
|
||||
PolarColorSpace,
|
||||
RectangularColorSpace,
|
||||
|
Loading…
Reference in New Issue
Block a user