Sass Color Spaces Proposal

Table of Contents


This proposal adds Sass support for several new CSS color spaces defined in CSS Color Level 4 – including access to non-RGB color models and colors outside the sRGB gamut.

Table of Contents


This section is non-normative.

When working with color on the web, there are a few important terms:

New CSS Color Spaces

The CSS Color Level 4 specification defines a number of new color spaces, each with its own syntax, but representing both new color models and wider color gamuts.

CSS color manipulation & interpolation functions will use the OKLab color space by default, unless otherwise defined for specific functions, or unless legacy behavior needs to be maintained. The CIE model will act a central reference for moving between spaces. Both models can be accessed in either cubic (LAB) or cylindrical (LCH) space:

The new color() function provides access to a number of less common spaces. The CIE XYZ spaces act as a central reference for conversion, with a gamut that covers all human-visible colors.

The remaining spaces are all extensions of the RGB color model, providing wider gamuts, improved bit-depth precision, or removed gamma-encoding:

Since the display-p3 color space represents a common space for wide-gamut monitors, that is likely to be one of the more popular color spaces for authors who simply want access to a wider range of colors – and don’t require the improved uniformity of manipulation or ease-of-use provided by CIE & OK spaces. The sRGB syntax is also useful as a way to ‘opt out’ of legacy color handling for RGB colors.

Missing & Powerless Color Components

CSS Color Level 4 also adds support for ‘missing’ & ‘powerless’ color components. For example, when converting a grayscale color to a polar-angle space like hsl, the resulting hue is unknown. Similarly, at the extremes of lightness (eg black or white), both hue and saturation become ‘powerless’ – changing them has no impact.

This can now be represented in CSS & Sass by using a value of none, so that the color white becomes effectively hsl(none none 100%). The none value is treated as 0 in most cases, but when interpolating two colors, a none value takes it’s value from the other color involved.

This also allows interpolating specific channels of a color. For example, any color can be moved towards ‘grayscale’ by mixing it with the color oklch(none 0% none).

Color Conversion and Gamut Mapping

The CSS Color Level 4 specification defines algorithms for conversion between all the new and existing color spaces. Still, since some spaces provide access to wider gamuts than others, it is possible for a color defined or manipulated in one color space to be ‘out-of-gamut’ when converted to another space.

There are various possible ways to ‘map’ an out-of-gamut color to its nearest in-gamut relative, and the spec provides some advice on how that can be done. In order to avoid data-loss, it’s often best to leave gamut-mapping as a detail for the browser to manage, based on the gamut of the display. Still, it would be good for Sass to provide some explicit gamut-mapping tools for authors to use when converting newer color-systems into backwards-compatible output.

The primary approach is to reduce the chroma value of a color in OKlch space, until it is reasonably ‘close’, and then clamping channel values in the destination color space. Chroma reduction helps avoid more noticeable shifts in lightness and hue, while the final clamping helps avoid dramatic chroma shifts when a more subtle movement is possible.

Browser Support

WebKit/Safari is already shipping support for all these new color spaces, and Gecko/Firefox has released some early drafts behind an experimental flag. These features are also included as a goal of [Interop 2022][interop], which makes them likely to roll out in all browsers by the end of the year.


This section is non-normative.

This proposal defines global (un-prefixed) Sass support for all of the color functions in CSS Color Level 4 (hwb(), lab()/lch(), oklab()/oklch(), color()). All (new and existing) color functions are also extended to support both:

Additionally, this proposal provides new functions in the sass color module for inspecting a color’s space, as well as converting & gamut-mapping across different spaces.

Design Decisions

Because all previously-available CSS color spaces provided the same gamut of colors using the same color model, both CSS & Sass have considered them interchangeable, converting between them silently, and using RGB/a internally. In order to move forward while maintaining backwards compatibility, both CSS & Sass will need to maintain some legacy handling for these legacy colors, while providing a way to opt into the new defaults used by newer color syntax.

The proposed solution in CSS is that:

CSS serialization converts all hex, rgb(a), hsl(a), hwb, and named colors to their rgb(a) equivalent, as part of a shared srgb space. However:

We have attempted to match this behavior.



A color is an object with several parts:

Legacy Color

Both Sass and CSS have similar legacy behavior that relies on all colors being interchangeable as part of a shared srgb color space. While the new color formats will opt users into new default behavior, some legacy color formats behave differently for the sake of backwards-compatibility.

Colors that are defined using the CSS color names, hex syntax, rgb(), rgba(), hsl(), hsla(), or hwb() – along with colors that result from legacy interpolation – are considered legacy colors. All legacy colors use the srgb color space, with red, green, and blue channels. The output of a legacy color is not required to match the input syntax.

Color Space

Every color is stored internally as part of a defined color space. Each space has a name, and an ordered list of associated channels that can be accessed and manipulated in that space. Each channel value can be any number, or the keyword none.

Bounded channels have a clearly-defined range that can be mapped to percentage values and scaled, even if those channels are not clamped to the range given.

This follows the CSS specification, which defines percentage-mapping onto several channels that are technically unbounded. However, some channels (marked below) are percentage-mapped without a clear boundary for scaling.

Legacy colors are converted to srgb internally, with any missing channels (specified using the none keyword) set to 0.

The color spaces and their channels are:

Predefined Color Spaces

‘Predefined color spaces’ can be described using the color() function.

The predefined RGB spaces are:

The predefined XYZ spaces are:

Missing Components

In some cases, a color can have one or more missing components (channel or alpha values). Missing components are represented by the keyword none. When interpolating between colors, the missing component is replaced by the value of that same component in the other color. In all other cases, the missing value is treated as 0.

Powerless Components

In some color spaces, it is possible for a channel value to become ‘powerless’ in certain circumstances. If a powerless channel value is produced as the result of color-space conversion, then that value is considered to be missing, and is replaced by the keyword none.

Color Gamuts

A color gamut is a range of colors that can be displayed by a given device, or described in a given color space. The predefined RGB gamuts are:

There are several color spaces that are associated with the srgb gamut:

All other color spaces describe unknown or theoretically infinite gamuts.

Color Interpolation Method

**ColorInterpolationMethod** ::= 'in' (
&#32;                                 RectangularColorSpace
&#32;                               | PolarColorSpace HueInterpolationMethod?
&#32;                             )
**RectangularColorSpace**    ::= 'srgb'
&#32;                          | 'srgb-linear'
&#32;                          | 'lab'
&#32;                          | 'oklab'
&#32;                          | 'xyz'
&#32;                          | 'xyz-d50'
&#32;                          | 'xyz-d65'
**PolarColorSpace**          ::= 'hsl'
&#32;                          | 'hwb'
&#32;                          | 'lch'
&#32;                          | 'oklch'
**HueInterpolationMethod**   ::= (
&#32;                                'shorter'
&#32;                              | 'longer'
&#32;                              | 'increasing'
&#32;                              | 'decreasing'
&#32;                              | 'specified'
&#32;                            ) 'hue'

Different color interpolation methods provide different advantages. For that reason, individual color procedures and functions (the host syntax) can establish their own color interpolation defaults, or provide a syntax for authors to explicitly choose the method that best fits their need.

The host syntax for a given interpolation procedure is the color syntax or function that instigates that interpolation. When selecting a color interpolation method:

Hue Interpolation Methods

When interpolating between polar-angle hue channels, there are multiple ‘directions’ the interpolation could move, following different logical rules.

The hue interpolation methods below are defined in CSS Color Level 4. Unless the type of hue interpolation is the value specified, both angles need to be constrained to [0, 360) prior to interpolation.

One way to do this is n = ((n % 360) + 360) % 360.

When no hue interpolation method is given, the default is shorter.


Converting a Color

Colors can be converted from one [color space][] to another. Algorithms for color conversion are defined in the CSS Color Level 4 specification. Each algorithm takes a color origin-color, and a string target-space, and returns a color output-color.

The algorithms are:

For additional details, see the Sample code for color conversions.

Gamut Mapping

Some color spaces describe limited color gamuts. If a color is ‘out of gamut’ for a particular space (most often because of conversion from a larger-gamut color-space), it can be useful to ‘map’ that color to the nearest 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. This procedure accepts a color origin in the color space origin color space, and a destination color space destination. It returns the result of a CSS gamut map procedure, which is a color in the destination color space.

This algorithm implements a relative colorimetric intent, and colors inside the destination gamut are unchanged.

Parsing Color Components

This procedure accepts an input parameter to parse, along with a space parameter representing the [color space][] if known. It throws common parse errors if necessary, and returns either null (if the input contains special CSS values), or a list of parsed values. The return value is in the format <color-space>? (<channel>+) / <alpha>, where the color space is included in the return value if it was not passed in initially.

This supports both the known color formats like hsl() and rgb(), where the space is determined by the function, as well as the syntax of color(), where the space is included as one of the input arguments (and may be a user-defined space).

The procedure is:

Normalizing Hue

This process accepts a hue angle, and boolean convert-none arguments. It returns the hue normalized to degrees when possible, and converting none to 0 when requested. Otherwise it throws an error for invalid hue.

Interpolating Colors

This procedure accepts two color arguments (color1 and color2), an optional [color interpolation method][] method, and a percentage weight for color1 in the mix. It returns a new color mix that represents the appropriate mix of input colors.

todo: finish this process

Premultiply Transparent Colors

When the colors being interpolated are not fully opaque, they are transformed into premultiplied color values. This process accepts a single color and updates the channel values if necessary, returning a new color with premultiplied channels.

The same process can be run in reverse, to un-premultiply the channels of a given color:

New Color Module Functions

These new functions are part of the built-in sass:color module.








New Global Functions

These new CSS functions are provided globally.







Modified Color Module Functions


mix($color1, $color2,
  $weight: 50%, $method: null)


Modified Global Functions

Temporary notes