Skip to content

Commit

Permalink
Document new color spaces (#1055) (#1115)
Browse files Browse the repository at this point in the history
Document new color spaces (#1055)

See #672

Co-authored-by: Miriam Suzanne <miriam@oddbird.net>
  • Loading branch information
nex3 and mirisuzanne committed Sep 18, 2024
1 parent 173c0a3 commit 3097289
Show file tree
Hide file tree
Showing 14 changed files with 1,658 additions and 461 deletions.
Binary file added source/assets/img/blog/042-blue-yellow.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-p3-hsl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-p3-oklch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-p3-srgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-srgb-hsl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-srgb-hwb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/assets/img/blog/042-srgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions source/assets/sass/visual-design/_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ body {
color: var(--text, var(--sl-color--pale-sky));
}

.fade {
opacity: 0.7;
}

::selection {
background: var(--sl-color--iron);
}
Expand Down
378 changes: 378 additions & 0 deletions source/blog/042-wide-gamut-colors-in-sass.md

Large diffs are not rendered by default.

1,109 changes: 732 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
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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 %}

Expand All @@ -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:
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion source/documentation/operators/equality.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
Loading

0 comments on commit 3097289

Please sign in to comment.