Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Document new color spaces
Browse files Browse the repository at this point in the history
See #672
nex3 committed May 30, 2024
1 parent 3a50fc0 commit 73663e3
Showing 5 changed files with 1,271 additions and 460 deletions.
4 changes: 4 additions & 0 deletions source/assets/sass/visual-design/_theme.scss
Original file line number Diff line number Diff line change
@@ -32,6 +32,10 @@ body {
color: var(--text, var(--sl-color--pale-sky));
}

.fade {
opacity: 0.7;
}

::selection {
background: var(--sl-color--iron);
}
1,101 changes: 724 additions & 377 deletions source/documentation/modules/color.md

Large diffs are not rendered by default.

274 changes: 230 additions & 44 deletions source/documentation/modules/index.md
Original file line number Diff line number Diff line change
@@ -83,6 +83,46 @@ Sass provides the following built-in modules:

## Global Functions

{% funFact %}
You can pass [special functions] like `calc()` or `var()` in place of any
argument to a global color constructor. You can even use `var()` in place of
multiple arguments, since it might be replaced by multiple values! When a
color function is called this way, it returns an unquoted string using the
same signature it was called with.

[special functions]: /documentation/syntax/special-functions

{% codeExample 'color-special', false %}
@debug rgb(0 51 102 / var(--opacity)); // rgb(0 51 102 / var(--opacity))
@debug color(display-p3 var(--peach)); // color(display-p3 var(--peach))
===
@debug rgb(0 51 102 / var(--opacity)) // rgb(0 51 102 / var(--opacity))
@debug color(display-p3 var(--peach)) // color(display-p3 var(--peach))
{% endcodeExample %}
{% endfunFact %}

{% function 'color($space $channel1 $channel2 $channel3)', 'color($space $channel1 $channel2 $channel3 / $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color in the given color space with the given channel values.

This supports the color spaces `srgb`, `srgb-linear`, `display-p3`, `a98-rgb`,
`prophoto-rgb`, `rec2020`, `xyz`, and `xyz-d50`, as well as `xyz-d65` which is
an alias for `xyz`. For all spaces, the channels are numbers between 0 and 1
(inclusive) or percentages between `0%` and `100%` (inclusive).

If any color channel is outside the range 0 to 1, this represents a color
outside the standard gamut for its color space.

{% codeExample 'hsl', false %}
@debug color(srgb 0.1 0.6 1); // color(srgb 0.1 0.6 1)
@debug color(xyz 30% 0% 90% / 50%); // color(xyz 0.3 0 0.9 / 50%)
===
@debug color(srgb 0.1 0.6 1) // color(srgb 0.1 0.6 1)
@debug color(xyz 30% 0% 90% / 50%) // color(xyz 0.3 0 0.9 / 50%)
{% endcodeExample %}
{% endfunction %}

{% function 'hsl($hue $saturation $lightness)', 'hsl($hue $saturation $lightness / $alpha)', 'hsl($hue, $saturation, $lightness, $alpha: 1)', 'hsla($hue $saturation $lightness)', 'hsla($hue $saturation $lightness / $alpha)', 'hsla($hue, $saturation, $lightness, $alpha: 1)', 'returns:color' %}
{% compatibility 'dart: "1.15.0"', 'libsass: false', 'ruby: false', 'feature: "Level 4 Syntax"' %}
LibSass and Ruby Sass only support the following signatures:
@@ -106,28 +146,15 @@ Sass provides the following built-in modules:
[hue, saturation, and lightness]: https://en.wikipedia.org/wiki/HSL_and_HSV

The hue is a number between `0deg` and `360deg` (inclusive) and may be
unitless. The saturation and lightness are numbers between `0%` and `100%`
(inclusive) and may *not* be unitless. The alpha channel can be specified as
either a unitless number between 0 and 1 (inclusive), or a percentage between
`0%` and `100%` (inclusive).

{% funFact %}
You can pass [special functions][] like `calc()` or `var()` in place of any
argument to `hsl()`. You can even use `var()` in place of multiple
arguments, since it might be replaced by multiple values! When a color
function is called this way, it returns an unquoted string using the same
signature it was called with.

[special functions]: /documentation/syntax/special-functions

{% codeExample 'hsl-special', false %}
@debug hsl(210deg 100% 20% / var(--opacity)); // hsl(210deg 100% 20% / var(--opacity))
@debug hsla(var(--peach), 20%); // hsla(var(--peach), 20%)
===
@debug hsl(210deg 100% 20% / var(--opacity)) // hsl(210deg 100% 20% / var(--opacity))
@debug hsla(var(--peach), 20%) // hsla(var(--peach), 20%)
{% endcodeExample %}
{% endfunFact %}
unitless. The saturation and lightness are typically numbers between `0%` and
`100%` (inclusive) and may *not* be unitless. The alpha channel can be
specified as either a unitless number between 0 and 1 (inclusive), or a
percentage between `0%` and `100%` (inclusive).

A hue outside `0deg` and `360deg` is equivalent to `$hue % 360deg`. A
saturation less than `0%` is clamped to `0%`. A saturation above `100%` or a
lightness outside `0%` and `100%` are both allowed, and represent colors
outside the standard RGB gamut.

{% headsUp %}
Sass's [special parsing rules][] for slash-separated values make it
@@ -140,14 +167,65 @@ Sass provides the following built-in modules:

{% codeExample 'hsl', false %}
@debug hsl(210deg 100% 20%); // #036
@debug hsl(34, 35%, 92%); // #f2ece4
@debug hsl(210deg 100% 20% / 50%); // rgba(0, 51, 102, 0.5)
@debug hsla(34, 35%, 92%, 0.2); // rgba(242, 236, 228, 0.2)
@debug hsla(34, 35%, 92%, 0.2); // rgba(241.74, 235.552, 227.46, 0.2)
===
@debug hsl(210deg 100% 20%) // #036
@debug hsl(34, 35%, 92%) // #f2ece4
@debug hsl(210deg 100% 20% / 50%) // rgba(0, 51, 102, 0.5)
@debug hsla(34, 35%, 92%, 0.2) // rgba(242, 236, 228, 0.2)
@debug hsla(34, 35%, 92%, 0.2) // rgba(241.74, 235.552, 227.46, 0.2)
{% endcodeExample %}
{% endfunction %}

{% function 'if($condition, $if-true, $if-false)' %}
Returns `$if-true` if `$condition` is [truthy][], and `$if-false` otherwise.

This function is special in that it doesn't even evaluate the argument that
isn't returned, so it's safe to call even if the unused argument would throw
an error.

[truthy]: /documentation/at-rules/control/if#truthiness-and-falsiness

{% codeExample 'debug', false %}
@debug if(true, 10px, 15px); // 10px
@debug if(false, 10px, 15px); // 15px
@debug if(variable-defined($var), $var, null); // null
===
@debug if(true, 10px, 15px) // 10px
@debug if(false, 10px, 15px) // 15px
@debug if(variable-defined($var), $var, null) // null
{% endcodeExample %}
{% endfunction %}

{% function 'hwb($hue $whiteness $blackness)', 'hwb($hue $whiteness $blackness / $alpha)', 'color.hwb($hue $whiteness $blackness)', 'color.hwb($hue $whiteness $blackness / $alpha)', 'color.hwb($hue, $whiteness, $blackness, $alpha: 1)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color with the given [hue, whiteness, and blackness] and the
given alpha channel.

[hue, whiteness, and blackness]: https://en.wikipedia.org/wiki/HWB_color_model

The hue is a number between `0deg` and `360deg` (inclusive) and may be
unitless. The whiteness and blackness are numbers typically between `0%` and
`100%` (inclusive) and may *not* be unitless. The alpha channel can be
specified as either a unitless number between 0 and 1 (inclusive), or a
percentage between `0%` and `100%` (inclusive).

A hue outside `0deg` and `360deg` is equivalent to `$hue % 360deg`. If
`$whiteness + $blackness > 100%`, the two values are scaled so that they add
up to `100%`. If `$whiteness`, `$blackness`, or both are less than `0%`, this
represents a color outside the standard RGB gamut.

{% headsUp %}
The `color.hwb()` variants are deprecated. New Sass code should use the
global `hwb()` function instead.
{% endheadsUp %}

{% codeExample 'hwb', false %}
@debug hwb(210deg 0% 60%); // #036
@debug hwb(210 0% 60% / 0.5); // rgba(0, 51, 102, 0.5)
===
@debug hwb(210deg 0% 60%) // #036
@debug hwb(210 0% 60% / 0.5) // rgba(0, 51, 102, 0.5)
{% endcodeExample %}
{% endfunction %}

@@ -171,6 +249,129 @@ Sass provides the following built-in modules:
{% endcodeExample %}
{% endfunction %}

{% function 'lab($lightness $a $b)', 'lab($lightness $a $b / $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color with the given [lightness, a, b], and alpha channels.

[hue, whiteness, and blackness]: https://en.wikipedia.org/wiki/CIELAB_color_space

The lightness is a number between `0%` and `100%` (inclusive) and may be
unitless. The a and b channels can be specified as either [unitless] numbers
between -125 and 125 (inclusive), or percentages between `-100%` and `100%`
(inclusive). The alpha channel can be specified as either a unitless number
between 0 and 1 (inclusive), or a percentage between `0%` and `100%`
(inclusive).

[unitless]: /documentation/values/numbers#units

A lightness outside the range `0%` and `100%` is clamped to be within that
range. If the a or b channels are outside the range `-125` to `125`, this
represents a color outside the standard CIELAB gamut.

{% codeExample 'lab', false %}
@debug lab(50% -20 30); // lab(50% -20 30)
@debug lab(80% 0% 20% / 0.5); // lab(80% 0 25 / 0.5);
===
@debug lab(50% -20 30) // lab(50% -20 30)
@debug lab(80% 0% 20% / 0.5) // lab(80% 0 25 / 0.5);
{% endcodeExample %}
{% endfunction %}

{% function 'lch($lightness $chroma $hue)', 'lch($lightness $chroma $hue / $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color with the given [lightness, chroma, and hue], and the given
alpha channel.

[hue, whiteness, and blackness]: https://en.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_model

The lightness is a number between `0%` and `100%` (inclusive) and may be
unitless. The chroma channel can be specified as either a [unitless] number
between 0 and 150 (inclusive), or a percentage between `0%` and `100%`
(inclusive). The hue is a number between `0deg` and `360deg` (inclusive) and
may be unitless. The alpha channel can be specified as either a unitless
number between 0 and 1 (inclusive), or a percentage between `0%` and `100%`
(inclusive).

[unitless]: /documentation/values/numbers#units

A lightness outside the range `0%` and `100%` is clamped to be within that
range. A chroma below 0 is clamped to 0, and a chroma above 150 represents a
color outside the standard CIELAB gamut. A hue outside `0deg` and `360deg` is
equivalent to `$hue % 360deg`.

{% codeExample 'lch', false %}
@debug lch(50% 10 270deg); // lch(50% 10 270deg)
@debug lch(80% 50% 0.2turn / 0.5); // lch(80% 75 72deg / 0.5);
===
@debug lch(50% 10 270deg) // lch(50% 10 270deg)
@debug lch(80% 50% 0.2turn / 0.5) // lch(80% 75 72deg / 0.5);
{% endcodeExample %}
{% endfunction %}

{% function 'oklab($lightness $a $b)', 'oklab($lightness $a $b / $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color with the given [perceptually-uniform lightness, a, b], and
alpha channels.

[perceptually-uniform lightness, a, b]: https://bottosson.github.io/posts/oklab/

The lightness is a number between `0%` and `100%` (inclusive) and may be
unitless. The a and b channels can be specified as either [unitless] numbers
between -0.4 and 0.4 (inclusive), or percentages between `-100%` and `100%`
(inclusive). The alpha channel can be specified as either a unitless number
between 0 and 1 (inclusive), or a percentage between `0%` and `100%`
(inclusive).

[unitless]: /documentation/values/numbers#units

A lightness outside the range `0%` and `100%` is clamped to be within that
range. If the a or b channels are outside the range `-0.4` to `0.4`, this
represents a color outside the standard Oklab gamut.

{% codeExample 'oklab', false %}
@debug oklab(50% -0.1 0.15); // oklab(50% -0.1 0.15)
@debug oklab(80% 0% 20% / 0.5); // oklab(80% 0 0.08 / 0.5)
===
@debug oklab(50% -0.1 0.15) // oklab(50% -0.1 0.15)
@debug oklab(80% 0% 20% / 0.5) // oklab(80% 0 0.08 / 0.5)
{% endcodeExample %}
{% endfunction %}

{% function 'oklch($lightness $chroma $hue)', 'oklch($lightness $chroma $hue / $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false' %}{% endcompatibility %}

Returns a color with the given [perceptually-uniform lightness, chroma, and
hue], and the given alpha channel.

[hue, whiteness, and blackness]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch

The lightness is a number between `0%` and `100%` (inclusive) and may be
unitless. The chroma channel can be specified as either a [unitless] number
between 0 and 0.4 (inclusive), or a percentage between `0%` and `100%`
(inclusive). The hue is a number between `0deg` and `360deg` (inclusive) and
may be unitless. The alpha channel can be specified as either a unitless
number between 0 and 1 (inclusive), or a percentage between `0%` and `100%`
(inclusive).

[unitless]: /documentation/values/numbers#units

A lightness outside the range `0%` and `100%` is clamped to be within that
range. A chroma below 0 is clamped to 0, and a chroma above 0.4 represents a
color outside the standard Oklab gamut. A hue outside `0deg` and `360deg` is
equivalent to `$hue % 360deg`.

{% codeExample 'oklch', false %}
@debug oklch(50% 0.3 270deg); // oklch(50% 0.3 270deg)
@debug oklch(80% 50% 0.2turn / 0.5); // oklch(80% 0.2 72deg / 0.5);
===
@debug oklch(50% 0.3 270deg) // oklch(50% 0.3 270deg)
@debug oklch(80% 50% 0.2turn / 0.5) // oklch(80% 0.2 72deg / 0.5);
{% endcodeExample %}
{% endfunction %}

{% function 'rgb($red $green $blue)', 'rgb($red $green $blue / $alpha)', 'rgb($red, $green, $blue, $alpha: 1)', 'rgb($color, $alpha)', 'rgba($red $green $blue)', 'rgba($red $green $blue / $alpha)', 'rgba($red, $green, $blue, $alpha: 1)', 'rgba($color, $alpha)', 'returns:color' %}
{% compatibility 'dart: "1.15.0"', 'libsass: false', 'ruby: false', 'feature: "Level 4 Syntax"' %}
LibSass and Ruby Sass only support the following signatures:
@@ -192,30 +393,15 @@ Sass provides the following built-in modules:
If `$red`, `$green`, `$blue`, and optionally `$alpha` are passed, returns a
color with the given red, green, blue, and alpha channels.

Each channel can be specified as either a [unitless][] number between 0 and
Each channel can be specified as either a [unitless] number between 0 and
255 (inclusive), or a percentage between `0%` and `100%` (inclusive). The
alpha channel can be specified as either a unitless number between 0 and 1
(inclusive), or a percentage between `0%` and `100%` (inclusive).

[unitless]: /documentation/values/numbers#units

{% funFact %}
You can pass [special functions][] like `calc()` or `var()` in place of any
argument to `rgb()`. You can even use `var()` in place of multiple
arguments, since it might be replaced by multiple values! When a color
function is called this way, it returns an unquoted string using the same
signature it was called with.

[special functions]: /documentation/syntax/special-functions

{% codeExample 'rgb-special', false %}
@debug rgb(0 51 102 / var(--opacity)); // rgb(0 51 102 / var(--opacity))
@debug rgba(var(--peach), 0.2); // rgba(var(--peach), 0.2)
===
@debug rgb(0 51 102 / var(--opacity)) // rgb(0 51 102 / var(--opacity))
@debug rgba(var(--peach), 0.2) // rgba(var(--peach), 0.2)
{% endcodeExample %}
{% endfunFact %}
If any color channel is outside the range 0 to 255, this represents a color
outside the standard RGB gamut.

{% headsUp %}
Sass's [special parsing rules][] for slash-separated values make it
6 changes: 5 additions & 1 deletion source/documentation/operators/equality.md
Original file line number Diff line number Diff line change
@@ -23,7 +23,9 @@ different types:
their values are equal when their units are converted between one another.
* [Strings][] are unusual in that [unquoted][] and [quoted][] strings with the
same contents are considered equal.
* [Colors][] are equal if they have the same red, green, blue, and alpha values.
* [Colors] are equal if they're in the same [color space] and have the same
channel values, *or* if they're both in [legacy color spaces] and have the
same RGBA channel values.
* [Lists][] are equal if their contents are equal. Comma-separated lists aren't
equal to space-separated lists, and bracketed lists aren't equal to
unbracketed lists.
@@ -40,6 +42,8 @@ different types:
[quoted]: /documentation/values/strings#quoted
[unquoted]: /documentation/values/strings#unquoted
[Colors]: /documentation/values/colors
[color space]: /documentation/values/colors#color-spaces
[legacy color spaces]: /documentation/values/colors#legacy-color-spaces
[Lists]: /documentation/values/lists
[`true`, `false`]: /documentation/values/booleans
[`null`]: /documentation/values/null
346 changes: 308 additions & 38 deletions source/documentation/values/colors.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,347 @@
---
title: Colors
table_of_contents: true
---

{% compatibility 'dart: "1.14.0"', 'libsass: "3.6.0"', 'ruby: "3.6.0"', 'feature: "Level 4 Syntax"' %}
{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false', 'feature: "Color Spaces"' %}
LibSass, Ruby Sass, and older versions of Dart Sass don't support color spaces
other than `rgb` and `hsl`.

As well as to adding support for new color spaces, this release changed some
details of the way colors were handled. In particular, even the legacy `rgb`
and `hsl` color spaces are no longer clamped to their gamuts; it's now
possible to represent `rgb(500 0 0)` or other out-of-bounds values. In
addition, `rgb` colors are no longer rounded to the nearest integer because
the CSS spec now requires implementations to maintain precision wherever
possible.
{% endcompatibility %}

{% compatibility 'dart: "1.14.0"', 'libsass: false', 'ruby: "3.6.0"', 'feature: "Level 4 Syntax"' %}
LibSass and older versions of Dart or Ruby Sass don't support [hex colors with
an alpha channel][].

[hex colors with an alpha channel]: https://drafts.csswg.org/css-color/#hex-notation
{% endcompatibility %}

Sass has built-in support for color values. Just like CSS colors, they represent
points in the [sRGB color space][], although many Sass [color functions][]
operate using [HSL coordinates][] (which are just another way of expressing sRGB
colors). Sass colors can be written as hex codes (`#f2ece4` or `#b37399aa`),
[CSS color names][] (`midnightblue`, `transparent`), or the functions
[`rgb()`][], [`rgba()`][], [`hsl()`][], and [`hsla()`][].
Sass has built-in support for color values. Just like CSS colors, each color
represents a point in a particular color space such as `rgb` or `lab`. Sass
colors can be written as hex codes (`#f2ece4` or `#b37399aa`), [CSS color names]
(`midnightblue`, `transparent`), or color functions like [`rgb()`], [`lab()`],
or [`color()`].

[sRGB color space]: https://en.wikipedia.org/wiki/SRGB
[color functions]: /documentation/modules/color
[HSL coordinates]: https://en.wikipedia.org/wiki/HSL_and_HSV
[CSS color names]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords
[`rgb()`]: /documentation/modules#rgb
[`rgba()`]: /documentation/modules#rgba
[`hsl()`]: /documentation/modules#hsl
[`hsla()`]: /documentation/modules#hsla
[`lab()`]: /documentation/modules#lab
[`color()`]: /documentation/modules#color

{% codeExample 'colors', false %}
@debug #f2ece4; // #f2ece4
@debug #b37399aa; // rgba(179, 115, 153, 67%)
@debug midnightblue; // #191970
@debug rgb(204, 102, 153); // #c69
@debug rgba(107, 113, 127, 0.8); // rgba(107, 113, 127, 0.8)
@debug hsl(228, 7%, 86%); // #dadbdf
@debug hsla(20, 20%, 85%, 0.7); // rgb(225, 215, 210, 0.7)
@debug rgb(204 102 153); // #c69
@debug lab(32.4% 38.4 -47.7 / 0.7); // lab(32.4% 38.4 -47.7 / 0.7)
@debug color(display-p3 0.597 0.732 0.576); // color(display-p3 0.597 0.732 0.576)
===
@debug #f2ece4 // #f2ece4
@debug #b37399aa // rgba(179, 115, 153, 67%)
@debug midnightblue // #191970
@debug rgb(204, 102, 153) // #c69
@debug rgba(107, 113, 127, 0.8) // rgba(107, 113, 127, 0.8)
@debug hsl(228, 7%, 86%) // #dadbdf
@debug hsla(20, 20%, 85%, 0.7) // rgb(225, 215, 210, 0.7)
@debug rgb(204 102 153) // #c69
@debug lab(32.4% 38.4 -47.7 / 0.7) // lab(32.4% 38.4 -47.7 / 0.7)
@debug color(display-p3 0.597 0.732 0.576) // color(display-p3 0.597 0.732 0.576)
{% endcodeExample %}

{% funFact %}
No matter how a Sass color is originally written, it can be used with both
HSL-based and RGB-based functions!
{% endfunFact %}
## Color Spaces

Sass supports the same set of color spaces as CSS. A Sass color will always be
emitted in the same color space it was written in unless it's in a [legacy color
space] or you convert it to another space using [`color.to-space()`]. All the
other color functions in Sass will always return a color in the same spaces as
the original color, even if the function made changes to that color in another
space.

[legacy color space]: #legacy-color-spaces
[`color.to-space()`]: /documentation/modules/color#to-space

Although each color space has bounds on the gamut it expects for its channels,
Sass can represent out-of-gamut values for any color space. This allows a color
from a wide-gamut space to be safely converted into and back out of a
narrow-gamut space without losing information.

{% headsUp %}
CSS requires that some color functions clip their input channels. For example,
`rgb(500 0 0)` clips its red channel to be within [0, 255] and so is
equivalent to `rgb(255 0 0)` even though `rgb(500 0 0)` is a distinct value
that Sass can represent. You can always use Sass's [`color.change()`] function
to set an out-of-gamut value for any space.

[`color.change()`]: /documentation/modules/color#change
{% endheadsUp %}

Following is a full list of all the color spaces Sass supports. You can read
learn about these spaces [on MDN].

[on MDN]: https://developer.mozilla.org/en-US/docs/Glossary/Color_space

<table class="sl-c-table">
<tr>
<th scope="col">Space</th>
<th scope="col">Syntax</th>
<th scope="col">Channels [min, max]</th>
</tr>
<tr>
<th scope="row"><code>rgb</code>*</th>
<td>
<code>rgb(102 51 153)</code><br>
<code>#663399</code><br>
<code>rebeccapurple</code>
</td>
<td>
red <span class="fade">[0, 255]</span>;
green <span class="fade">[0, 255]</span>;
blue <span class="fade">[0, 255]</span>
</td>
</tr>
<tr>
<th scope="row"><code>hsl</code>*</th>
<td><code>hsl(270 50% 40%)</code></td>
<td>
hue <span class="fade">[0, 360]</span>;
saturation <span class="fade">[0%, 100%]</span>;
lightness <span class="fade">[0%, 100%]</span>
</td>
</tr>
<tr>
<th scope="row"><code>hwb</code>*</th>
<td><code>hwb(270 20% 40%)</code></td>
<td>
hue <span class="fade">[0, 360]</span>;
whiteness <span class="fade">[0%, 100%]</span>;
blackness <span class="fade">[0%, 100%]</span>
</td>
</tr>
<tr>
<th scope="row"><code>srgb</code></th>
<td><code>color(srgb 0.4 0.2 0.6)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>srgb-linear</code></th>
<td><code>color(srgb-linear 0.133 0.033 0.319)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>display-p3</code></th>
<td><code>color(display-p3 0.374 0.21 0.579)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>a98-rgb</code></th>
<td><code>color(a98-rgb 0.358 0.212 0.584)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>prophoto-rgb</code></th>
<td><code>color(prophoto-rgb 0.316 0.191 0.495)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>rec2020</code></th>
<td><code>color(rec2020 0.305 0.168 0.531)</code></td>
<td>
red <span class="fade">[0, 1]</span>;
green <span class="fade">[0, 1]</span>;
blue <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>xyz</code>, <code>xyz-d65</code></th>
<td>
<code>color(xyz 0.124 0.075 0.309)</code><br>
<code>color(xyz-d65 0.124 0.075 0.309)</code>
</td>
<td>
x <span class="fade">[0, 1]</span>;
y <span class="fade">[0, 1]</span>;
z <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>xyz-d50</code></th>
<td><code>color(xyz-d50 0.116 0.073 0.233)</code></td>
<td>
x <span class="fade">[0, 1]</span>;
y <span class="fade">[0, 1]</span>;
z <span class="fade">[0, 1]</span>
</td>
</tr>
<tr>
<th scope="row"><code>lab</code></th>
<td><code>lab(32.4% 38.4 -47.7)</code></td>
<td>
lightness <span class="fade">[0%, 100%]</span>;
a <span class="fade">[-125, 125]</span>;
b <span class="fade">[-125, 125]</span>
</td>
</tr>
<tr>
<th scope="row"><code>lch</code></th>
<td><code>lch(32.4% 61.2 308.9deg)</code></td>
<td>
lightness <span class="fade">[0%, 100%]</span>;
chroma <span class="fade">[0, 150]</span>;
hue <span class="fade">[0deg, 360deg]</span>
</td>
</tr>
<tr>
<th scope="row"><code>oklab</code></th>
<td><code>oklab(44% 0.088 -0.134)</code></td>
<td>
lightness <span class="fade">[0%, 100%]</span>;
a <span class="fade">[-0.4, 0.4]</span>;
b <span class="fade">[-0.4, 0.4]</span>
</td>
</tr>
<tr>
<th scope="row"><code>oklch</code></th>
<td><code>oklch(44% 0.16 303.4deg)</code></td>
<td>
lightness <span class="fade">[0%, 100%]</span>;
chroma <span class="fade">[0, 0.4]</span>;
hue <span class="fade">[0deg, 360deg]</span>
</td>
</tr>
</table>

Spaces marked with * are [legacy color spaces].

[legacy color spaces]: #legacy-color-spaces

## Missing Channels

Colors in CSS and Sass can have "missing channels", which are written `none` and
represent a channel whose value isn't known or doesn't affect the way the color
is rendered. For example, you might write `hsl(none 0% 50%)`, because the hue
doesn't matter if the saturation is `0%`. In most cases, missing channels are
just treated as 0 values, but they do come up occasionally:

* If you're mixing colors together, either as part of CSS interpolation for
something like an animation or using Sass's [`color.mix()`] function, missing
channels always take on the other color's value for that channel if possible.

[`color.mix()`]: /documentation/modules/color#mix

* If you convert a color with a missing channel to another space that has an
analogous channel, that channel will be set to `none` after the conversion is
complete.

Although [`color.channel()`] will return 0 for missing channels, you can always
check for them using [`color.is-missing()`].

CSS supports many different formats that can all represent the same color: its
name, its hex code, and [functional notation][]. Which format Sass chooses to
compile a color to depends on the color itself, how it was written in the
original stylesheet, and the current output mode. Because it can vary so much,
stylesheet authors shouldn't rely on any particular output format for colors
they write.
[`color.channel()`]: /documentation/modules/color#channel
[`color.is-missing()`]: /documentation/modules/color#is-missing

[functional notation]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
{% codeExample 'missing-channels', false %}
@use 'sass:color';

Sass supports many useful [color functions][] that can be used to create new
colors based on existing ones by [mixing colors together][] or [scaling their
hue, saturation, or lightness][].
$grey: hsl(none 0% 50%);

@debug color.mix($grey, blue, $method: hsl); // hsl(240, 50%, 50%)
@debug color.to-space($grey, lch); // lch(53.3889647411% 0 none)
===
@use 'sass:color'

$grey: hsl(none 0% 50%)

@debug color.mix($grey, blue, $method: hsl) // hsl(240, 50%, 50%)
@debug color.to-space($grey, lch) // lch(53.3889647411% 0 none)
{% endcodeExample %}

### Powerless Channels

A color channel is considered "powerless" under certain circumstances its value
doesn't affect the way the color is rendered on screen. The CSS spec requires
that when a color is converted to a new space, any powerless channels are
replaced by `none`. Sass does this in all cases except conversions to legacy
spaces, to guarantee that converting to a legacy space always produces a color
that's compatible with older browsers.

For more details on powerless channels, see [`color.is-powerless()`].

[`color.is-powerless()`]: /documentation/modules/color#is-powerless

## Legacy Color Spaces

Historically, CSS and Sass only supported the standard RGB gamut, and only
supported the `rgb`, `hsl`, and `hwb` functions for defining colors. Because at
the time all colors used the same gamut, every color function worked with every
color regardless of its color space. Sass still preserves this behavior, but
only for older functions and only for colors in these three "legacy" color
spaces. Even so, it's still a good practice to explicitly specify the `$space`
you want to work in when using color functions.

Sass will also freely convert between different legacy color spaces when
converting legacy color values to CSS. This is always safe, because they all use
the same underlying color model, and this helps ensure that Sass emits colors in
as compatible a format as possible.

## Color Functions

Sass supports many useful [color functions] that can be used to create new
colors based on existing ones by [mixing colors together] or [scaling their
channel values]. When calling color functions, color spaces should always be
written as unquoted strings to match CSS, while channel names should be written
as quoted strings so that channels like `"red"` aren't parsed as color values.

[mixing colors together]: /documentation/modules/color#mix
[scaling their hue, saturation, or lightness]: /documentation/modules/color#scale
[scaling their channel values]: /documentation/modules/color#scale

{% funFact %}
Sass color functions can automatically convert colors between spaces, which
makes it easy to do transformations in perceptually-uniform color spaces like
Oklch. But they'll *always* return a color in the same space you gave it,
unless you explicitly call [`color.to-space()`] to convert it.

[`color.to-space()`]: /documentation/modules/color#to-space
{% endfunFact %}

{% codeExample 'color-formats', false %}
@use 'sass:color';

$venus: #998099;

@debug scale-color($venus, $lightness: +15%); // #a893a8
@debug mix($venus, midnightblue); // #594d85
@debug color.scale($venus, $lightness: +15%, $space: oklch);
// rgb(170.1523703626, 144.612080603, 170.1172627174)
@debug color.mix($venus, midnightblue, $method: oklch);
// rgb(95.9363315581, 74.5687109346, 133.2082569526)
===
@use 'sass:color'

$venus: #998099

@debug scale-color($venus, $lightness: +15%) // #a893a8
@debug mix($venus, midnightblue) // #594d85
@debug color.scale($venus, $lightness: +15%, $space: oklch)
// rgb(170.1523703626, 144.612080603, 170.1172627174)
@debug color.mix($venus, midnightblue, $method: oklch)
// rgb(95.9363315581, 74.5687109346, 133.2082569526)
{% endcodeExample %}

0 comments on commit 73663e3

Please sign in to comment.