26 KiB
CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.8
This proposal updates Sass's JavaScript (JS) API to match the color spaces proposal.
Table of Contents
- API
- Types
- Procedures
- Embedded Protocol
API
import {List} from 'immutable';
import {Value} from '../spec/js-api/value';
Types
Color Space Definitions
export type ColorSpaceHsl = 'hsl';
export type ChannelNameHsl = 'hue' | 'saturation' | 'lightness' | 'alpha';
export type ColorSpaceHwb = 'hwb';
export type ChannelNameHwb = 'hue' | 'whiteness' | 'blackness' | 'alpha';
export type ColorSpaceLab = 'lab' | 'oklab';
export type ChannelNameLab = 'lightness' | 'a' | 'b' | 'alpha';
export type ColorSpaceLch = 'lch' | 'oklch';
export type ChannelNameLch = 'lightness' | 'chroma' | 'hue' | 'alpha';
export type ColorSpaceRgb =
| 'a98-rgb'
| 'display-p3'
| 'prophoto-rgb'
| 'rec2020'
| 'rgb'
| 'srgb'
| 'srgb-linear';
export type ChannelNameRgb = 'red' | 'green' | 'blue' | 'alpha';
export type ColorSpaceXyz = 'xyz' | 'xyz-d50' | 'xyz-d65';
export type ChannelNameXyz = 'x' | 'y' | 'z' | 'alpha';
export type ChannelName =
| ChannelNameHsl
| ChannelNameHwb
| ChannelNameLab
| ChannelNameLch
| ChannelNameRgb
| ChannelNameXyz;
export type KnownColorSpace =
| ColorSpaceHsl
| ColorSpaceHwb
| ColorSpaceLab
| ColorSpaceLch
| ColorSpaceRgb
| ColorSpaceXyz;
export type PolarColorSpace = ColorSpaceHsl | ColorSpaceHwb | ColorSpaceLch;
export type RectangularColorSpace = Exclude<KnownColorSpace, PolarColorSpace>;
export type HueInterpolationMethod =
| 'decreasing'
| 'increasing'
| 'longer'
| 'shorter';
export type GamutMapMethod = 'clip' | 'local-minde';
New Color Functions
export class SassColor extends Value {
space
Returns the name of internal
's space.
get space(): KnownColorSpace;
toSpace
-
If
this.space
is equal tospace
, returnthis
. -
Otherwise, return the result of Converting a Color with
this
asorigin-color
andspace
astarget-space
.
toSpace(space: KnownColorSpace): SassColor;
isLegacy
Returns whether internal
is in a legacy color space (rgb
, hsl
, or
hwb
).
get isLegacy(): boolean;
isInGamut
Returns the result of color.is-in-gamut(internal, space)
as a JavaScript
boolean.
isInGamut(space?: KnownColorSpace): boolean;
toGamut
Returns the result of color.to-gamut(internal, space, method)
.
toGamut(options: {
space?: KnownColorSpace;
method: 'clip' | 'local-minde';
}): SassColor;
channelsOrNull
Returns a list of channel values (excluding alpha) for internal
, with
missing channels converted to null
.
-
Let
space
be the value ofthis.space
. -
Let
components
be the list of channels inspace
. -
Let
channels
be an empty list. -
For each
component
incomponents
:-
Let
value
be the channel value ininternal
with name ofcomponent
. -
If
value
isnone
, letvalue
benull
. -
Append
value
tochannels
.
-
-
Return
channels
.
get channelsOrNull(): List<number | null>;
channels
This algorithm returns a list of channel values (excluding alpha) for
internal
, with missing channels converted to 0
.
-
Let
channelsOrNull
be the value ofthis.channelsOrNull
. -
Let
channels
be an empty list. -
For each
channel
inchannelsOrNull
:-
If
channel
equalsnull
, letvalue
be 0. -
Append
value
tochannels
.
-
-
Return
channels
.
get channels(): List<number>;
channel
-
Let
initialSpace
be the value ofthis.space
. -
Let
space
beoptions.space
if it is defined, and the value ofinitialSpace
otherwise. -
If
channel
is not "alpha" or a channel inspace
, throw an error. -
Let
color
be the result ofthis.toSpace(space)
. -
Let
value
be the channel value incolor
with name ofcomponent
. -
If
value
isnull
, return 0. -
Otherwise, return
value
.
channel(channel: ChannelName): number;
channel(channel: ChannelNameHsl, options: {space: ColorSpaceHsl}): number;
channel(channel: ChannelNameHwb, options: {space: ColorSpaceHwb}): number;
channel(channel: ChannelNameLab, options: {space: ColorSpaceLab}): number;
channel(channel: ChannelNameLch, options: {space: ColorSpaceLch}): number;
channel(channel: ChannelNameRgb, options: {space: ColorSpaceRgb}): number;
channel(channel: ChannelNameXyz, options: {space: ColorSpaceXyz}): number;
alpha
Returns the result of calling this.channel('alpha')
.
get alpha(): number;
isChannelMissing
Returns the result of color.is-missing(internal, channel)
as a JavaScript boolean.
isChannelMissing(channel: ChannelName): boolean;
isChannelPowerless
Returns the result of color.is-powerless(internal, channel, space)
as a
JavaScript boolean.
isChannelPowerless(channel: ChannelName): boolean;
isChannelPowerless(
channel: ChannelNameHsl,
options?: {space: ColorSpaceHsl}
): boolean;
isChannelPowerless(
channel: ChannelNameHwb,
options?: {space: ColorSpaceHwb}
): boolean;
isChannelPowerless(
channel: ChannelNameLab,
options?: {space: ColorSpaceLab}
): boolean;
isChannelPowerless(
channel: ChannelNameLch,
options?: {space: ColorSpaceLch}
): boolean;
isChannelPowerless(
channel: ChannelNameRgb,
options?: {space: ColorSpaceRgb}
): boolean;
isChannelPowerless(
channel: ChannelNameXyz,
options?: {space: ColorSpaceXyz}
): boolean;
interpolate
-
Let
space
be the value ofthis.space
. -
If
options.method
is set, letinterpolationMethod
be a space separated list containing the value ofspace
, a space, and the value ofoptions.method
. -
Otherwise, if
space
is a rectangular color space, letinterpolationMethod
bespace
. -
Otherwise, let
interpolationMethod
be a space separated list containing the value ofspace
, a space, and the string "shorter". -
Return the result of
color.mix(internal, color2, options.weight, interpolationMethod)
.
interpolate(
color2: SassColor,
options?: {
weight?: number;
method?: HueInterpolationMethod;
}
): SassColor;
Updated Color Functions
change
Replace the definition of color.change with the following:
This algorithm takes a JavaScript object options
and returns a new SassColor
as the result of changing some of internal
's components.
The
space
value defaults to thespace
ofinternal
, and the caller may specify any combination of channels and alpha in that space to be changed.If
space
is not a legacy color space, a channel value ofnull
will result in a missing component value for that channel.
-
Let
initialSpace
be the value ofthis.space
. -
Let
spaceSetExplicitly
betrue
ifoptions.space
is defined, andfalse
otherwise. -
Let
space
beoptions.space
ifspaceSetExplicitly
is true, and the value ofinitialSpace
otherwise. -
If
initialSpace
is a legacy color space andspaceSetExplicitly
is false:-
If
options.whiteness
oroptions.blackness
is set, letspace
behwb
. -
Otherwise, if
options.hue
is set andinitialSpace
ishwb
, let space behwb
. -
Otherwise, if
options.hue
,options.saturation
, oroptions.lightness
is set, letspace
behsl
. -
Otherwise, if
options.red
,options.green
, oroptions.blue
is set, letspace
bergb
. -
If
initialSpace
is not equal tospace
, emit a deprecation warning namedcolor-4-api
.
-
-
Let
changes
be the objectoptions
withoutspace
and its value. -
Let
keys
be a list of the keys inchanges
. -
Let
components
be"alpha"
and the names of the channels inspace
. -
If any key in
keys
is not the name of a channel incomponents
, throw an error. -
If
options.alpha
is set, and isn't either null or a number between 0 and 1 (inclusive and fuzzy), throw an error. -
Let
color
be the result ofthis.toSpace(space)
. -
Let
changedValue
be a function that takes a string argument forchannel
and calls the procedureChanging a Component Value
withchanges
andcolor
asinitial
. -
If
space
equalshsl
andspaceSetExplicitly
isfalse
:-
If any of
options.hue
,options.saturation
oroptions.lightness
equalsnull
, emit a deprecation warning namedcolor-4-api
. -
If
options.alpha
equalsnull
, emit a deprecation warning namednull-alpha
. -
Let
changedColor
be the result of:new SassColor({ hue: options.hue ?? color.channel('hue'), saturation: options.saturation ?? color.channel('saturation'), lightness: options.lightness ?? color.channel('lightness'), alpha: options.alpha ?? color.channel('alpha'), space: space })
-
-
If
space
equalshsl
andspaceSetExplicitly
istrue
, letchangedColor
be the result of:new SassColor({ hue: changedValue('hue'), saturation: changedValue('saturation'), lightness: changedValue('lightness'), alpha: changedValue('alpha'), space: space })
-
If
space
equalshwb
andspaceSetExplicitly
isfalse
:-
If any of
options.hue
,options.whiteness
oroptions.blackness
equalsnull
, emit a deprecation warning namedcolor-4-api
. -
If
options.alpha
equalsnull
, emit a deprecation warning namednull-alpha
. -
Let
changedColor
be the result of:new SassColor({ hue: options.hue ?? color.channel('hue'), whiteness: options.whiteness ?? color.channel('whiteness'), blackness: options.blackness ?? color.channel('blackness'), alpha: options.alpha ?? color.channel('alpha'), space: space })
-
-
If
space
equalshwb
andspaceSetExplicitly
istrue
, letchangedColor
be the result of:new SassColor({ hue: changedValue('hue'), whiteness: changedValue('whiteness'), blackness: changedValue('blackness'), alpha: changedValue('alpha'), space: space })
-
If
space
equalsrgb
andspaceSetExplicitly
isfalse
:-
If any of
options.red
,options.green
oroptions.blue
equalsnull
, emit a deprecation warning namedcolor-4-api
. -
If
options.alpha
equalsnull
, emit a deprecation warning namednull-alpha
. -
Let
changedColor
be the result of:new SassColor({ red: options.red ?? color.channel('red'), green: options.green ?? color.channel('green'), blue: options.blue ?? color.channel('blue'), alpha: options.alpha ?? color.channel('alpha'), space: space })
-
-
If
space
equalsrgb
andspaceSetExplicitly
istrue
, letchangedColor
be the result of:new SassColor({ red: changedValue('red'), green: changedValue('green'), blue: changedValue('blue'), alpha: changedValue('alpha'), space: space })
-
If
space
equalslab
oroklab
, letchangedColor
be the result of:new SassColor({ lightness: changedValue('lightness'), a: changedValue('a'), b: changedValue('b'), alpha: changedValue('alpha'), space: space })
-
If
space
equalslch
oroklch
, letchangedColor
be the result of:new SassColor({ lightness: changedValue('lightness'), chroma: changedValue('chroma'), hue: changedValue('hue'), alpha: changedValue('alpha'), space: space })
-
If
space
equalsa98-rgb
,display-p3
,prophoto-rgb
,rec2020
,srgb
, orsrgb-linear
, letchangedColor
be the result of:new SassColor({ red: changedValue('red'), green: changedValue('green'), blue: changedValue('blue'), alpha: changedValue('alpha'), space: space })
-
If
space
equalsxyz
,xyz-d50
, orxyz-d65
, letchangedColor
be the result of:new SassColor({ y: changedValue('y'), x: changedValue('x'), z: changedValue('z'), alpha: changedValue('alpha'), space: space })
-
Return the result of
changedColor.toSpace(initialSpace)
.
change(
options: {
[key in ChannelNameHsl]?: number | null;
} & {
space?: ColorSpaceHsl;
}
): SassColor;
change(
options: {
[key in ChannelNameHwb]?: number | null;
} & {
space?: ColorSpaceHwb;
}
): SassColor;
change(
options: {
[key in ChannelNameLab]?: number | null;
} & {
space?: ColorSpaceLab;
}
): SassColor;
change(
options: {
[key in ChannelNameLch]?: number | null;
} & {
space?: ColorSpaceLch;
}
): SassColor;
change(
options: {
[key in ChannelNameRgb]?: number | null;
} & {
space?: ColorSpaceRgb;
}
): SassColor;
change(
options: {
[key in ChannelNameXyz]?: number | null;
} & {
space?: ColorSpaceXyz;
}
): SassColor;
New Constructors
-
Let
constructionSpace
be the result of Determining Construction Space with theoptions
object passed to the constructor. -
Use the constructor that matches
constructionSpace
.
Lab Channel Constructor
Create a new SassColor in a color space with Lab channels—lab
and oklab
.
-
If
options.space
equalslab
, letmaximum
be100
. Otherwise, letmaximum
be1
. -
Let
lightness
be the result of parsing a channel value withvalue
ofoptions.lightness
,minimum
of0
, andmaximum
ofmaximum
. -
Let
a
be the result of parsing a channel value with valueoptions.a
. -
Let
b
be the result of parsing a channel value with valueoptions.b
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value with valueoptions.alpha
,minimum
of 0, andmaximum
of 1. -
If
options.space
equalslab
, setinternal
to the result oflab(lightness a b / alpha)
. -
Otherwise, if
options.space
equalsoklab
, setinternal
to the result ofoklab(lightness a b / alpha)
.
constructor(options: {
lightness: number | null;
a: number | null;
b: number | null;
alpha?: number | null;
space: ColorSpaceLab;
});
LCH Channel Constructor
Create a new SassColor in a color space with LCH channels—lch
and oklch
.
-
If
options.space
equalslch
, letmaximum
be100
. Otherwise, letmaximum
be1
. -
Let
lightness
be the result of parsing a channel value withvalue
ofoptions.lightness
,minimum
of0
, andmaximum
ofmaximum
. -
Let
c
be the result of parsing a channel value with valueoptions.c
. -
Let
h
be the result of parsing a channel value with valueoptions.h
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value with valueoptions.alpha
,minimum
of 0, andmaximum
of 1. -
If
options.space
equalslch
, setinternal
to the result oflch(lightness a b / alpha)
. -
Otherwise, if
options.space
equalsoklch
, setinternal
to the result ofoklch(lightness a b / alpha)
.
constructor(options: {
lightness: number | null;
chroma: number | null;
hue: number | null;
alpha?: number | null;
space: ColorSpaceLch;
});
Predefined RGB Channel Constructor
Create a new SassColor in a color space with RGB channels—srgb
, srgb-linear
,
display-p3
, a98-rgb
, prophoto-rgb
, and rec2020
. rgb
is supported
through the modified RGB Constructor.
-
Let
red
be the result of parsing a channel value with valueoptions.red
. -
Let
green
be the result of parsing a channel value with valueoptions.green
. -
Let
blue
be the result of parsing a channel value with valueoptions.blue
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value with valueoptions.alpha
,minimum
of 0, andmaximum
of 1. -
Let
space
be the unquoted string value ofoptions.space
. -
Set
internal
to the result ofcolor(space red green blue / alpha)
.
constructor(options: {
red: number | null;
green: number | null;
blue: number | null;
alpha?: number | null;
space: Exclude<ColorSpaceRgb, 'rgb'>;
});
XYZ Channel Constructor
Create a new SassColor in a color space with XYZ channels—xyz
, xyz-d50
, and
xyz-d65
.
-
Let
x
be the result of parsing a channel value with valueoptions.x
. -
Let
y
be the result of parsing a channel value with valueoptions.y
. -
Let
z
be the result of parsing a channel value with valueoptions.z
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value with valueoptions.alpha
,minimum
of 0, andmaximum
of 1. -
Let
space
be the unquoted string value ofoptions.space
. -
Set
internal
to the result ofcolor(space x y z / alpha)
.
constructor(options: {
x: number | null;
y: number | null;
z: number | null;
alpha?: number | null;
space: ColorSpaceXyz;
});
Modified Legacy Color Constructors
These will replace the existing constructors for legacy colors.
HSL Constructor
Create a new SassColor in the hsl
color space.
-
If
options.alpha
isnull
andoptions.space
is not set, emit a deprecation warning namednull-alpha
. -
Let
hue
be the result of parsing a channel value with valueoptions.hue
. -
Let
saturation
be the result of parsing a channel value with valueoptions.saturation
. -
Let
lightness
be the result of parsing a channel value withvalue
ofoptions.lightness
,minimum
of0
, andmaximum
of100
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value withvalue
ofoptions.alpha
,minimum
of0
, andmaximum
of1
. -
Set
internal
to the result ofhsl(hue saturation lightness / alpha)
.
constructor(options: {
hue: number | null;
saturation: number | null;
lightness: number | null;
alpha?: number | null;
space?: ColorSpaceHsl;
});
HWB Constructor
Create a new SassColor in the hwb
color space.
-
If
options.alpha
isnull
andoptions.space
is not set, emit a deprecation warning namednull-alpha
. -
Let
hue
be the result of parsing a channel value with valueoptions.hue
. -
Let
whiteness
be the result of parsing a channel value with valueoptions.whiteness
. -
Let
blackness
be the result of parsing a channel value with valueoptions.blackness
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value withvalue
ofoptions.alpha
,minimum
of0
, andmaximum
of1
. -
Set
internal
to the result ofhwb(hue whiteness blackness / alpha)
.
constructor(options: {
hue: number | null;
whiteness: number | null;
blackness: number | null;
alpha?: number | null;
space?: ColorSpaceHwb;
});
RGB Constructor
Create a new SassColor in the rgb
color space.
-
If
options.alpha
isnull
andoptions.space
is not set, emit a deprecation warning namednull-alpha
. -
Let
red
be the result of parsing a channel value with valueoptions.red
. -
Let
green
be the result of parsing a channel value with valueoptions.green
. -
Let
blue
be the result of parsing a channel value with valueoptions.blue
. -
If
options.alpha
is not set, letalpha
be1
. Otherwise, letalpha
be the result of parsing a clamped channel value withvalue
ofoptions.alpha
,minimum
of0
, andmaximum
of1
. -
Set
internal
to the result ofrgb(red green blue / alpha)
.
constructor(options: {
red: number | null;
green: number | null;
blue: number | null;
alpha?: number | null;
space?: 'rgb';
});
}
Deprecations
A number of SassColor getters only make sense for legacy color space, and so
are being deprecated in favor of the new channel
function. This deprecation
is called color-4-api
.
The following deprecated getters return the result of
this.channel(channelName, { space: "rgb" })
where channelName
is the name of the respective getter.
red
green
blue
The following deprecated getters return the result of
this.channel(channelName, { space: "hsl" })
where channelName
is the name of the respective getter.
hue
saturation
lightness
The following deprecated getters return the result of
this.channel(channelName, { space: "hwb" })
where channelName
is the name of the respective getter.
whiteness
blackness
Procedures
Parsing a Channel Value
This procedure takes a channel value value
, and returns the special value
none
if the value is null
.
-
If
value
is a number, return a Sass number with a value ofvalue
. -
If
value
is the Javascript valuenull
, return the unquoted Sass stringnone
.
Parsing a Clamped Channel Value
This procedure takes a channel value value
and an inclusive range of minimum
and maximum
. It asserts the value is in the range, and returns the special
value none
if the value is null
.
-
If
value
is fuzzy less-thanminimum
, throw an error. -
If
value
is fuzzy greater-thanmaximum
, throw an error. -
Otherwise, return the result of Parsing a Channel Value.
Changing a Component Value
This procedure takes a channel
name, an object changes
and a SassColor
initial
and returns the result of applying the change for channel
to
initial
.
-
Let
initialValue
be the channel value ininitial
with name ofchannel
. -
If
channel
is not a key inchanges
, returninitialValue
. -
Let
changedValue
be the value forchannel
inchanges
. -
If
changedValue
isundefined
and notnull
, returninitialValue
. -
Otherwise, return
changedValue
.
Determining Construction Space
This procedure takes an object options
with unknown keys and returns a color
space for construction.
-
If
options.space
is set, returnoptions.space
. -
If
options.red
is set, return "rgb". -
If
options.saturation
is set, return "hsl". -
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 legacy SassScript values.
Color
message Color {
// The name of a known color space.
string space = 1;
// The value of the first channel associated with `space`.
double channel1 = 2;
// The value of the second channel associated with `space`.
double channel2 = 3;
// The value of the third channel associated with `space`.
double channel3 = 4;
// The color's alpha channel. Mandatory. Must be between 0 and 1, inclusive.
double alpha = 5;
}
Removed SassScript values
The RgbColor
, HslColor
and HwbColor
SassScript values will be removed from
the Embedded Protocol.