mirror of
https://github.com/sass/sass.git
synced 2024-09-21 10:37:22 +00:00
Draft 1.1 changes for Color spaces JS API (#3704)
Co-authored-by: Jonny Gerig Meyer <jonny@oddbird.net>
This commit is contained in:
parent
83a9b01364
commit
50feffd28e
21
proposal/color-4-new-spaces-js.changes.md
Normal file
21
proposal/color-4-new-spaces-js.changes.md
Normal file
@ -0,0 +1,21 @@
|
||||
## Draft 1.1
|
||||
|
||||
* Clarify values in `channels` and `channelsOrNull`.
|
||||
|
||||
* Throw an error if construction space can not be determined.
|
||||
|
||||
* Remove `alpha` from list of deprecated getters.
|
||||
|
||||
* Rename types: `ColorSpaceLAB` to `ColorSpaceLab`, `ChannelNameLAB` to
|
||||
`ChannelNameLab`.
|
||||
|
||||
* Use `Exclude<>` instead of `Omit<>` for union types.
|
||||
|
||||
* Make procedure for determining space backwards compatible when using `change`
|
||||
for legacy colors.
|
||||
|
||||
* Fix channel names for `change` with `oklch` and `lch`.
|
||||
|
||||
## Draft 1
|
||||
|
||||
* Initial draft
|
@ -1,6 +1,7 @@
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.1
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2831))*
|
||||
*([Issue](https://github.com/sass/sass/issues/2831),
|
||||
[Changelog](color-4-new-spaces-js.changes.md))*
|
||||
|
||||
This proposal updates Sass's JavaScript (JS) API to match the [color spaces
|
||||
proposal].
|
||||
@ -29,7 +30,7 @@ proposal].
|
||||
* [Updated Color Functions](#updated-color-functions)
|
||||
* [`change`](#change)
|
||||
* [New Constructors](#new-constructors)
|
||||
* [LAB Channel Constructor](#lab-channel-constructor)
|
||||
* [Lab Channel Constructor](#lab-channel-constructor)
|
||||
* [LCH Channel Constructor](#lch-channel-constructor)
|
||||
* [Predefined RGB Channel Constructor](#predefined-rgb-channel-constructor)
|
||||
* [XYZ Channel Constructor](#xyz-channel-constructor)
|
||||
@ -57,7 +58,6 @@ import {Value} from '../spec/js-api/value';
|
||||
### Color Space Definitions
|
||||
|
||||
```ts
|
||||
|
||||
export type ColorSpaceHSL = 'hsl';
|
||||
|
||||
export type ChannelNameHSL = 'hue' | 'saturation' | 'lightness';
|
||||
@ -66,9 +66,9 @@ export type ColorSpaceHWB = 'hwb';
|
||||
|
||||
export type ChannelNameHWB = 'hue' | 'whiteness' | 'blackness';
|
||||
|
||||
export type ColorSpaceLAB = 'lab' | 'oklab';
|
||||
export type ColorSpaceLab = 'lab' | 'oklab';
|
||||
|
||||
export type ChannelNameLAB = 'lightness' | 'a' | 'b';
|
||||
export type ChannelNameLab = 'lightness' | 'a' | 'b';
|
||||
|
||||
export type ColorSpaceLCH = 'lch' | 'oklch';
|
||||
|
||||
@ -91,7 +91,7 @@ export type ChannelNameXYZ = 'x' | 'y' | 'z';
|
||||
export type ChannelName =
|
||||
| ChannelNameHSL
|
||||
| ChannelNameHWB
|
||||
| ChannelNameLAB
|
||||
| ChannelNameLab
|
||||
| ChannelNameLCH
|
||||
| ChannelNameRGB
|
||||
| ChannelNameXYZ;
|
||||
@ -99,14 +99,14 @@ export type ChannelName =
|
||||
export type KnownColorSpace =
|
||||
| ColorSpaceHSL
|
||||
| ColorSpaceHWB
|
||||
| ColorSpaceLAB
|
||||
| ColorSpaceLab
|
||||
| ColorSpaceLCH
|
||||
| ColorSpaceRGB
|
||||
| ColorSpaceXYZ;
|
||||
|
||||
export type PolarColorSpace = ColorSpaceHSL | ColorSpaceHWB | ColorSpaceLCH;
|
||||
|
||||
export type RectangularColorSpace = Omit<KnownColorSpace, PolarColorSpace>;
|
||||
export type RectangularColorSpace = Exclude<KnownColorSpace, PolarColorSpace>;
|
||||
|
||||
export type HueInterpolationMethod =
|
||||
| 'decreasing'
|
||||
@ -156,7 +156,8 @@ get isLegacy(): boolean;
|
||||
|
||||
#### `isInGamut`
|
||||
|
||||
Returns the result of [`color.is-in-gamut(internal, space)`] as a JavaScript boolean.
|
||||
Returns the result of [`color.is-in-gamut(internal, space)`] as a JavaScript
|
||||
boolean.
|
||||
|
||||
```ts
|
||||
isInGamut(space?: KnownColorSpace): boolean;
|
||||
@ -187,7 +188,7 @@ Returns an array of channel values (excluding alpha) for [`internal`], with
|
||||
|
||||
* For each `component` in `components`:
|
||||
|
||||
* Let `value` be the channel value in `color` with name of `component`.
|
||||
* Let `value` be the channel value in [`internal`] with name of `component`.
|
||||
|
||||
* If `value` is `none`, let `value` be `null`.
|
||||
|
||||
@ -207,11 +208,13 @@ get channelsOrNull(): [number | null, number | null, number | null];
|
||||
This algorithm returns an array of channel values (excluding alpha) for
|
||||
[`internal`], with [missing channels][missing components] converted to `0`.
|
||||
|
||||
* Let `channels` be the result of [`this.channelsOrNull`].
|
||||
* Let `channelsOrNull` be the result of [`this.channelsOrNull`].
|
||||
|
||||
* For each `channel` in `channels`:
|
||||
* Let `channels` be an empty array.
|
||||
|
||||
* If `value` equals `null`, let `value` be 0.
|
||||
* For each `channel` in `channelsOrNull`:
|
||||
|
||||
* If `channel` equals `null`, let `value` be 0.
|
||||
|
||||
* Append `value` to `channels`.
|
||||
|
||||
@ -251,8 +254,8 @@ channel(
|
||||
options: {space: ColorSpaceHWB}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameLAB | 'alpha',
|
||||
options: {space: ColorSpaceLAB}
|
||||
channel: ChannelNameLab | 'alpha',
|
||||
options: {space: ColorSpaceLab}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameLCH | 'alpha',
|
||||
@ -300,7 +303,8 @@ get isAlphaMissing(): boolean;
|
||||
|
||||
#### `isChannelPowerless`
|
||||
|
||||
Returns the result of [`color.is-powerless(internal, channel, space)`] as a JavaScript boolean.
|
||||
Returns the result of [`color.is-powerless(internal, channel, space)`] as a
|
||||
JavaScript boolean.
|
||||
|
||||
[`color.is-powerless(internal, channel, space)`]: ./color-4-new-spaces.md#coloris-powerless-1
|
||||
|
||||
@ -315,8 +319,8 @@ isChannelPowerless(
|
||||
options?: {space: ColorSpaceHWB}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameLAB,
|
||||
options?: {space: ColorSpaceLAB}
|
||||
channel: ChannelNameLab,
|
||||
options?: {space: ColorSpaceLab}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameLCH,
|
||||
@ -337,12 +341,13 @@ isChannelPowerless(
|
||||
* Let `space` be the value of [`this.space()`].
|
||||
|
||||
* If `options.method` is set, let `interpolationMethod` be a space separated
|
||||
list containing the value of `space`, a space, and the value of `options.method`.
|
||||
list containing the value of `space`, a space, and the value of
|
||||
`options.method`.
|
||||
|
||||
* Otherwise, if `space` is a rectangular color space, let `interpolationMethod` be
|
||||
`space`.
|
||||
* Otherwise, if `space` is a rectangular color space, let `interpolationMethod`
|
||||
be `space`.
|
||||
|
||||
* Otherwise, let `interpolationMethod` be a space separated list containing th
|
||||
* Otherwise, let `interpolationMethod` be a space separated list containing the
|
||||
value of `space`, a space, and the string "shorter".
|
||||
|
||||
* Return the result of [`color.mix(internal, options.color2, options.weight, interpolationMethod)`][`color.mix()`].
|
||||
@ -381,16 +386,18 @@ as the result of changing some of [`internal`]'s components.
|
||||
* Let `spaceSetExplicitly` be `true` if `options.space` is defined, and `false`
|
||||
otherwise.
|
||||
|
||||
* Let `space` be `options.space` if `spaceSetExplicitly` is true, and the value of
|
||||
`initialSpace` otherwise.
|
||||
* Let `space` be `options.space` if `spaceSetExplicitly` is true, and the value
|
||||
of `initialSpace` otherwise.
|
||||
|
||||
* If `initialSpace` is a [legacy color space] and `spaceSetExplicitly` is false:
|
||||
|
||||
* If `options.red` is set, let `space` be `rgb`.
|
||||
* If `options.whiteness` or `options.blackness` is set, let `space` be `hwb`.
|
||||
|
||||
* Otherwise, if `options.saturation` is set, let `space` be `hsl`.
|
||||
* Otherwise, if `options.hue`, `options.saturation`, or `options.lightness` is
|
||||
set, let `space` be `hsl`.
|
||||
|
||||
* Otherwise, if `options.whiteness` is set, let `space` be `hwb`.
|
||||
* Otherwise, if `options.red`, `options.green`, or `options.blue` is set, let
|
||||
`space` be `rgb`.
|
||||
|
||||
* If `initialSpace` is not equal to `space`, emit a deprecation warning named
|
||||
`color-4-api`.
|
||||
@ -407,8 +414,8 @@ as the result of changing some of [`internal`]'s components.
|
||||
* Let `color` be the result of [`this.toSpace(space)`].
|
||||
|
||||
* Let `changedValue` be a function that takes a string argument for `channel`
|
||||
and calls the procedure [`Changing a Component Value`] with `changes` and `this`
|
||||
as `initial`.
|
||||
and calls the procedure [`Changing a Component Value`] with `changes` and
|
||||
`this` as `initial`.
|
||||
|
||||
* If `space` equals `hsl` and `spaceSetExplicitly` is `false`:
|
||||
|
||||
@ -418,7 +425,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
hue: options.hue ?? color.channel('hue'),
|
||||
saturation: options.saturation ?? color.channel('saturation'),
|
||||
lightness: options.lightness ?? color.channel('lightness'),
|
||||
@ -431,7 +438,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
hue: changedValue('hue'),
|
||||
saturation: changedValue('saturation'),
|
||||
lightness: changedValue('lightness'),
|
||||
@ -448,7 +455,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
hue: options.hue ?? color.channel('hue'),
|
||||
whiteness: options.whiteness ?? color.channel('whiteness'),
|
||||
blackness: options.blackness ?? color.channel('blackness'),
|
||||
@ -461,7 +468,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
hue: changedValue('hue'),
|
||||
whiteness: changedValue('whiteness'),
|
||||
blackness: changedValue('blackness'),
|
||||
@ -470,7 +477,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
})
|
||||
```
|
||||
|
||||
* If space equals `rgb` and `spaceSetExplicitly` is `false`:
|
||||
* If `space` equals `rgb` and `spaceSetExplicitly` is `false`:
|
||||
|
||||
* If any of `options.red`, `options.green`, `options.blue` or `options.alpha`
|
||||
equals null, emit a deprecation warning named `null-alpha`.
|
||||
@ -478,7 +485,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
red: options.red ?? color.channel('red'),
|
||||
green: options.green ?? color.channel('green'),
|
||||
blue: options.blue ?? color.channel('blue'),
|
||||
@ -491,7 +498,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
red: changedValue('red'),
|
||||
green: changedValue('green'),
|
||||
blue: changedValue('blue'),
|
||||
@ -500,10 +507,10 @@ as the result of changing some of [`internal`]'s components.
|
||||
})
|
||||
```
|
||||
|
||||
* If space equals `lab` or `oklab`, let `changedColor` be the result of:
|
||||
* If `space` equals `lab` or `oklab`, let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
lightness: changedValue('lightness'),
|
||||
a: changedValue('a'),
|
||||
b: changedValue('b'),
|
||||
@ -512,13 +519,13 @@ as the result of changing some of [`internal`]'s components.
|
||||
})
|
||||
```
|
||||
|
||||
* If space equals `lch` or `oklch`, let `changedColor` be the result of:
|
||||
* If `space` equals `lch` or `oklch`, let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
lightness: changedValue('lightness'),
|
||||
c: changedValue('c'),
|
||||
h: changedValue('h'),
|
||||
chroma: changedValue('chroma'),
|
||||
hue: changedValue('hue'),
|
||||
alpha: changedValue('alpha'),
|
||||
space: space
|
||||
})
|
||||
@ -528,7 +535,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
`srgb-linear`, let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
red: changedValue('red'),
|
||||
green: changedValue('green'),
|
||||
blue: changedValue('blue'),
|
||||
@ -541,7 +548,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
result of:
|
||||
|
||||
```js
|
||||
SassColor({
|
||||
new SassColor({
|
||||
y: changedValue('y'),
|
||||
x: changedValue('x'),
|
||||
z: changedValue('z'),
|
||||
@ -584,10 +591,10 @@ change(
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameLAB]?: number | null;
|
||||
[key in ChannelNameLab]?: number | null;
|
||||
} & {
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceLAB;
|
||||
space: ColorSpaceLab;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
@ -628,9 +635,9 @@ change(
|
||||
|
||||
[Determining Construction Space]: #determining-construction-space
|
||||
|
||||
#### LAB Channel Constructor
|
||||
#### Lab Channel Constructor
|
||||
|
||||
Create a new SassColor in a color space with LAB channels -- `lab` and `oklab`.
|
||||
Create a new SassColor in a color space with Lab channels -- `lab` and `oklab`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a channel value] with value
|
||||
`options.lightness`.
|
||||
@ -658,7 +665,7 @@ constructor(options: {
|
||||
a: number | null;
|
||||
b: number | null;
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceLAB;
|
||||
space: ColorSpaceLab;
|
||||
});
|
||||
```
|
||||
|
||||
@ -699,7 +706,7 @@ constructor(options: {
|
||||
|
||||
Create a new SassColor in a color space with RGB channels -- `srgb`,
|
||||
`srgb-linear`, `display-p3`, `a98-rgb`, and `prophoto-rgb`. `rgb` is supported
|
||||
through the modifed [RGB Constructor].
|
||||
through the modified [RGB Constructor].
|
||||
|
||||
* Let `red` be the result of [parsing a channel value] with value `options.red`.
|
||||
|
||||
@ -725,7 +732,7 @@ constructor(options: {
|
||||
green: number | null;
|
||||
blue: number | null;
|
||||
alpha?: number | null;
|
||||
space: Omit<ColorSpaceRGB, 'rgb'>;
|
||||
space: Exclude<ColorSpaceRGB, 'rgb'>;
|
||||
});
|
||||
```
|
||||
|
||||
@ -774,9 +781,11 @@ Create a new SassColor in the `hsl` color space.
|
||||
|
||||
* Let `hue` be the result of [parsing a channel value] with value `options.hue`.
|
||||
|
||||
* Let `saturation` be the result of [parsing a channel value] with value `options.saturation`.
|
||||
* Let `saturation` be the result of [parsing a channel value] with value
|
||||
`options.saturation`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a channel value] with value `options.lightness`.
|
||||
* Let `lightness` be the result of [parsing a channel value] with value
|
||||
`options.lightness`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
@ -791,7 +800,7 @@ constructor(options: {
|
||||
saturation: number | null;
|
||||
lightness: number | null;
|
||||
alpha?: number | null;
|
||||
space?: 'hsl';
|
||||
space?: ColorSpaceHSL;
|
||||
});
|
||||
```
|
||||
|
||||
@ -804,9 +813,11 @@ Create a new SassColor in the `hwb` color space.
|
||||
|
||||
* Let `hue` be the result of [parsing a channel value] with value `options.hue`.
|
||||
|
||||
* Let `whiteness` be the result of [parsing a channel value] with value `options.whiteness`.
|
||||
* Let `whiteness` be the result of [parsing a channel value] with value
|
||||
`options.whiteness`.
|
||||
|
||||
* Let `blackness` be the result of [parsing a channel value] with value `options.blackness`.
|
||||
* Let `blackness` be the result of [parsing a channel value] with value
|
||||
`options.blackness`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
@ -821,7 +832,7 @@ constructor(options: {
|
||||
whiteness: number | null;
|
||||
blackness: number | null;
|
||||
alpha?: number | null;
|
||||
space?: 'hwb';
|
||||
space?: ColorSpaceHWB;
|
||||
});
|
||||
```
|
||||
|
||||
@ -864,7 +875,10 @@ constructor(options: {
|
||||
### Deprecations
|
||||
|
||||
A number of SassColor getters only make sense for [legacy color space], and so
|
||||
are being deprecated for `channel`. This deprecation is called `color-4-api`.
|
||||
are being deprecated in favor of the new [`channel`] function. This deprecation
|
||||
is called `color-4-api`.
|
||||
|
||||
[`channel`]: #channel
|
||||
|
||||
* `red`
|
||||
* `green`
|
||||
@ -874,7 +888,6 @@ are being deprecated for `channel`. This deprecation is called `color-4-api`.
|
||||
* `lightness`
|
||||
* `whiteness`
|
||||
* `blackness`
|
||||
* `alpha`
|
||||
|
||||
## Procedures
|
||||
|
||||
@ -913,6 +926,8 @@ space for construction.
|
||||
|
||||
* If `options.whiteness` is set, return "hwb".
|
||||
|
||||
* Otherwise, throw an error.
|
||||
|
||||
## Embedded Protocol
|
||||
|
||||
This introduces a breaking change in the Embedded Protocol, as it removes the
|
||||
|
@ -117,7 +117,7 @@ wider RGB gamuts.
|
||||
|
||||
* A *color model* is a mathematical approach to representing colors and their
|
||||
relationships. Historically, RGB has been the dominant color model for both
|
||||
computer monitors and web browsers. Lately, CIELab and OKLab models have
|
||||
computer monitors and web browsers. Lately, CIELab and Oklab models have
|
||||
shown significant benefits by providing a more *perceptually uniform*
|
||||
distribution of colors, so that similar mathematical adjustments achieve
|
||||
visually similar results.
|
||||
@ -128,7 +128,7 @@ wider RGB gamuts.
|
||||
project the RGB color model into cubic coordinate systems, while `hsl()`
|
||||
projects the same color model into a cylindrical (polar-angle) space.
|
||||
Similarly, `oklab()` and `oklch()` provide different coordinate projections
|
||||
of the OKLab model.
|
||||
of the Oklab model.
|
||||
|
||||
* A *color gamut* is the full range of colors that can be described in a color
|
||||
space. Historically, all CSS syntaxes have been limited to the sRGB gamut.
|
||||
@ -235,8 +235,8 @@ such, `oklch` is often the best space for consistent transforms.
|
||||
#### `lab()` and `lch()`
|
||||
|
||||
The `lab()` and `lch()` functions provide access to an unbounded gamut of colors
|
||||
in a space that's less perpetually-uniform but more widely-adopted than OKLab
|
||||
and OKLCH.
|
||||
in a space that's less perpetually-uniform but more widely-adopted than Oklab
|
||||
and Oklch.
|
||||
|
||||
#### `hwb()`
|
||||
|
||||
@ -902,15 +902,15 @@ The individual conversion algorithms are:
|
||||
|
||||
* [sRGB to HWB](https://www.w3.org/TR/css-color-4/#rgb-to-hwb)
|
||||
|
||||
* [Lab to LCH, OKLab to OKLCH](https://www.w3.org/TR/css-color-4/#lab-to-lch)
|
||||
* [Lab to LCH, Oklab to Oklch](https://www.w3.org/TR/css-color-4/#lab-to-lch)
|
||||
|
||||
* [LCH to Lab, OKLCH to OKLab](https://www.w3.org/TR/css-color-4/#lch-to-lab)
|
||||
* [LCH to Lab, Oklch to Oklab](https://www.w3.org/TR/css-color-4/#lch-to-lab)
|
||||
|
||||
* [Between predefined RGB spaces](https://www.w3.org/TR/css-color-4/#predefined-to-predefined)
|
||||
|
||||
* [Any RGB to Lab/OKLab](https://www.w3.org/TR/css-color-4/#predefined-to-lab-oklab)
|
||||
* [Any RGB to Lab/Oklab](https://www.w3.org/TR/css-color-4/#predefined-to-lab-oklab)
|
||||
|
||||
* [Lab/OKLab to any RGB](https://www.w3.org/TR/css-color-4/#oklab-lab-to-predefined)
|
||||
* [Lab/Oklab to any RGB](https://www.w3.org/TR/css-color-4/#oklab-lab-to-predefined)
|
||||
|
||||
> For additional details, see the [Sample code for color conversions][convert].
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user