|
1 | 1 | ---
|
2 | 2 | title: Colors
|
| 3 | +table_of_contents: true |
3 | 4 | ---
|
4 | 5 |
|
5 |
| -{% compatibility 'dart: "1.14.0"', 'libsass: "3.6.0"', 'ruby: "3.6.0"', 'feature: "Level 4 Syntax"' %} |
| 6 | +{% compatibility 'dart: "1.78.0"', 'libsass: false', 'ruby: false', 'feature: "Color Spaces"' %} |
| 7 | + LibSass, Ruby Sass, and older versions of Dart Sass don't support color spaces |
| 8 | + other than `rgb` and `hsl`. |
| 9 | +{% endcompatibility %} |
| 10 | + |
| 11 | +{% compatibility 'dart: "1.14.0"', 'libsass: false', 'ruby: "3.6.0"', 'feature: "Level 4 Syntax"' %} |
6 | 12 | LibSass and older versions of Dart or Ruby Sass don't support [hex colors with
|
7 | 13 | an alpha channel][].
|
8 | 14 |
|
9 | 15 | [hex colors with an alpha channel]: https://drafts.csswg.org/css-color/#hex-notation
|
10 | 16 | {% endcompatibility %}
|
11 | 17 |
|
12 |
| -Sass has built-in support for color values. Just like CSS colors, they represent |
13 |
| -points in the [sRGB color space][], although many Sass [color functions][] |
14 |
| -operate using [HSL coordinates][] (which are just another way of expressing sRGB |
15 |
| -colors). Sass colors can be written as hex codes (`#f2ece4` or `#b37399aa`), |
16 |
| -[CSS color names][] (`midnightblue`, `transparent`), or the functions |
17 |
| -[`rgb()`][], [`rgba()`][], [`hsl()`][], and [`hsla()`][]. |
| 18 | +Sass has built-in support for color values. Just like CSS colors, each color |
| 19 | +represents a point in a particular color space such as `rgb` or `lab`. Sass |
| 20 | +colors can be written as hex codes (`#f2ece4` or `#b37399aa`), [CSS color names] |
| 21 | +(`midnightblue`, `transparent`), or color functions like [`rgb()`], [`lab()`], |
| 22 | +or [`color()`]. |
18 | 23 |
|
19 | 24 | [sRGB color space]: https://en.wikipedia.org/wiki/SRGB
|
20 | 25 | [color functions]: /documentation/modules/color
|
21 |
| -[HSL coordinates]: https://en.wikipedia.org/wiki/HSL_and_HSV |
22 | 26 | [CSS color names]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords
|
23 | 27 | [`rgb()`]: /documentation/modules#rgb
|
24 |
| -[`rgba()`]: /documentation/modules#rgba |
25 |
| -[`hsl()`]: /documentation/modules#hsl |
26 |
| -[`hsla()`]: /documentation/modules#hsla |
| 28 | +[`lab()`]: /documentation/modules#lab |
| 29 | +[`color()`]: /documentation/modules#color |
27 | 30 |
|
28 | 31 | {% codeExample 'colors', false %}
|
29 | 32 | @debug #f2ece4; // #f2ece4
|
30 | 33 | @debug #b37399aa; // rgba(179, 115, 153, 67%)
|
31 | 34 | @debug midnightblue; // #191970
|
32 |
| - @debug rgb(204, 102, 153); // #c69 |
33 |
| - @debug rgba(107, 113, 127, 0.8); // rgba(107, 113, 127, 0.8) |
34 |
| - @debug hsl(228, 7%, 86%); // #dadbdf |
35 |
| - @debug hsla(20, 20%, 85%, 0.7); // rgb(225, 215, 210, 0.7) |
| 35 | + @debug rgb(204 102 153); // #c69 |
| 36 | + @debug lab(32.4% 38.4 -47.7 / 0.7); // lab(32.4% 38.4 -47.7 / 0.7) |
| 37 | + @debug color(display-p3 0.597 0.732 0.576); // color(display-p3 0.597 0.732 0.576) |
36 | 38 | ===
|
37 | 39 | @debug #f2ece4 // #f2ece4
|
38 | 40 | @debug #b37399aa // rgba(179, 115, 153, 67%)
|
39 | 41 | @debug midnightblue // #191970
|
40 |
| - @debug rgb(204, 102, 153) // #c69 |
41 |
| - @debug rgba(107, 113, 127, 0.8) // rgba(107, 113, 127, 0.8) |
42 |
| - @debug hsl(228, 7%, 86%) // #dadbdf |
43 |
| - @debug hsla(20, 20%, 85%, 0.7) // rgb(225, 215, 210, 0.7) |
| 42 | + @debug rgb(204 102 153) // #c69 |
| 43 | + @debug lab(32.4% 38.4 -47.7 / 0.7) // lab(32.4% 38.4 -47.7 / 0.7) |
| 44 | + @debug color(display-p3 0.597 0.732 0.576) // color(display-p3 0.597 0.732 0.576) |
44 | 45 | {% endcodeExample %}
|
45 | 46 |
|
46 |
| -{% funFact %} |
47 |
| - No matter how a Sass color is originally written, it can be used with both |
48 |
| - HSL-based and RGB-based functions! |
49 |
| -{% endfunFact %} |
| 47 | +## Color Spaces |
| 48 | + |
| 49 | +Sass supports the same set of color spaces as CSS. A Sass color will always be |
| 50 | +emitted in the same color space it was written in unless it's in a [legacy color |
| 51 | +space] or you convert it to another space using [`color.to-space()`]. All the |
| 52 | +other color functions in Sass will always return a color in the same spaces as |
| 53 | +the original color, even if the function made changes to that color in another |
| 54 | +space. |
| 55 | + |
| 56 | +[legacy color space]: #legacy-color-spaces |
| 57 | +[`color.to-space()`]: /documentation/modules/color#to-space |
| 58 | + |
| 59 | +Although each color space has bounds on the gamut it expects for its channels, |
| 60 | +Sass can represent out-of-gamut values for any color space. This allows a color |
| 61 | +from a wide-gamut space to be safely converted into and back out of a |
| 62 | +narrow-gamut space without losing information. |
| 63 | + |
| 64 | +{% headsUp %} |
| 65 | + CSS requires that some color functions clip their input channels. For example, |
| 66 | + `rgb(500 0 0)` clips its red channel to be within [0, 255] and so is |
| 67 | + equivalent to `rgb(255 0 0)` even though `rgb(500 0 0)` is a distinct value |
| 68 | + that Sass can represent. You can always use Sass's [`color.change()`] function |
| 69 | + to set an out-of-gamut value for any space. |
| 70 | + |
| 71 | + [`color.change()`]: /documentation/modules/color#change |
| 72 | +{% endheadsUp %} |
| 73 | + |
| 74 | +Following is a full list of all the color spaces Sass supports. You can read |
| 75 | +learn about these spaces [on MDN]. |
| 76 | + |
| 77 | +[on MDN]: https://developer.mozilla.org/en-US/docs/Glossary/Color_space |
| 78 | + |
| 79 | +<table class="sl-c-table"> |
| 80 | + <tr> |
| 81 | + <th scope="col">Space</th> |
| 82 | + <th scope="col">Syntax</th> |
| 83 | + <th scope="col">Channels [min, max]</th> |
| 84 | + </tr> |
| 85 | + <tr> |
| 86 | + <th scope="row"><code>rgb</code>*</th> |
| 87 | + <td> |
| 88 | + <code>rgb(102 51 153)</code><br> |
| 89 | + <code>#663399</code><br> |
| 90 | + <code>rebeccapurple</code> |
| 91 | + </td> |
| 92 | + <td> |
| 93 | + red <span class="fade">[0, 255]</span>; |
| 94 | + green <span class="fade">[0, 255]</span>; |
| 95 | + blue <span class="fade">[0, 255]</span> |
| 96 | + </td> |
| 97 | + </tr> |
| 98 | + <tr> |
| 99 | + <th scope="row"><code>hsl</code>*</th> |
| 100 | + <td><code>hsl(270 50% 40%)</code></td> |
| 101 | + <td> |
| 102 | + hue <span class="fade">[0, 360]</span>; |
| 103 | + saturation <span class="fade">[0%, 100%]</span>; |
| 104 | + lightness <span class="fade">[0%, 100%]</span> |
| 105 | + </td> |
| 106 | + </tr> |
| 107 | + <tr> |
| 108 | + <th scope="row"><code>hwb</code>*</th> |
| 109 | + <td><code>hwb(270 20% 40%)</code></td> |
| 110 | + <td> |
| 111 | + hue <span class="fade">[0, 360]</span>; |
| 112 | + whiteness <span class="fade">[0%, 100%]</span>; |
| 113 | + blackness <span class="fade">[0%, 100%]</span> |
| 114 | + </td> |
| 115 | + </tr> |
| 116 | + <tr> |
| 117 | + <th scope="row"><code>srgb</code></th> |
| 118 | + <td><code>color(srgb 0.4 0.2 0.6)</code></td> |
| 119 | + <td> |
| 120 | + red <span class="fade">[0, 1]</span>; |
| 121 | + green <span class="fade">[0, 1]</span>; |
| 122 | + blue <span class="fade">[0, 1]</span> |
| 123 | + </td> |
| 124 | + </tr> |
| 125 | + <tr> |
| 126 | + <th scope="row"><code>srgb-linear</code></th> |
| 127 | + <td><code>color(srgb-linear 0.133 0.033 0.319)</code></td> |
| 128 | + <td> |
| 129 | + red <span class="fade">[0, 1]</span>; |
| 130 | + green <span class="fade">[0, 1]</span>; |
| 131 | + blue <span class="fade">[0, 1]</span> |
| 132 | + </td> |
| 133 | + </tr> |
| 134 | + <tr> |
| 135 | + <th scope="row"><code>display-p3</code></th> |
| 136 | + <td><code>color(display-p3 0.374 0.21 0.579)</code></td> |
| 137 | + <td> |
| 138 | + red <span class="fade">[0, 1]</span>; |
| 139 | + green <span class="fade">[0, 1]</span>; |
| 140 | + blue <span class="fade">[0, 1]</span> |
| 141 | + </td> |
| 142 | + </tr> |
| 143 | + <tr> |
| 144 | + <th scope="row"><code>a98-rgb</code></th> |
| 145 | + <td><code>color(a98-rgb 0.358 0.212 0.584)</code></td> |
| 146 | + <td> |
| 147 | + red <span class="fade">[0, 1]</span>; |
| 148 | + green <span class="fade">[0, 1]</span>; |
| 149 | + blue <span class="fade">[0, 1]</span> |
| 150 | + </td> |
| 151 | + </tr> |
| 152 | + <tr> |
| 153 | + <th scope="row"><code>prophoto-rgb</code></th> |
| 154 | + <td><code>color(prophoto-rgb 0.316 0.191 0.495)</code></td> |
| 155 | + <td> |
| 156 | + red <span class="fade">[0, 1]</span>; |
| 157 | + green <span class="fade">[0, 1]</span>; |
| 158 | + blue <span class="fade">[0, 1]</span> |
| 159 | + </td> |
| 160 | + </tr> |
| 161 | + <tr> |
| 162 | + <th scope="row"><code>rec2020</code></th> |
| 163 | + <td><code>color(rec2020 0.305 0.168 0.531)</code></td> |
| 164 | + <td> |
| 165 | + red <span class="fade">[0, 1]</span>; |
| 166 | + green <span class="fade">[0, 1]</span>; |
| 167 | + blue <span class="fade">[0, 1]</span> |
| 168 | + </td> |
| 169 | + </tr> |
| 170 | + <tr> |
| 171 | + <th scope="row"><code>xyz</code>, <code>xyz-d65</code></th> |
| 172 | + <td> |
| 173 | + <code>color(xyz 0.124 0.075 0.309)</code><br> |
| 174 | + <code>color(xyz-d65 0.124 0.075 0.309)</code> |
| 175 | + </td> |
| 176 | + <td> |
| 177 | + x <span class="fade">[0, 1]</span>; |
| 178 | + y <span class="fade">[0, 1]</span>; |
| 179 | + z <span class="fade">[0, 1]</span> |
| 180 | + </td> |
| 181 | + </tr> |
| 182 | + <tr> |
| 183 | + <th scope="row"><code>xyz-d50</code></th> |
| 184 | + <td><code>color(xyz-d50 0.116 0.073 0.233)</code></td> |
| 185 | + <td> |
| 186 | + x <span class="fade">[0, 1]</span>; |
| 187 | + y <span class="fade">[0, 1]</span>; |
| 188 | + z <span class="fade">[0, 1]</span> |
| 189 | + </td> |
| 190 | + </tr> |
| 191 | + <tr> |
| 192 | + <th scope="row"><code>lab</code></th> |
| 193 | + <td><code>lab(32.4% 38.4 -47.7)</code></td> |
| 194 | + <td> |
| 195 | + lightness <span class="fade">[0%, 100%]</span>; |
| 196 | + a <span class="fade">[-125, 125]</span>; |
| 197 | + b <span class="fade">[-125, 125]</span> |
| 198 | + </td> |
| 199 | + </tr> |
| 200 | + <tr> |
| 201 | + <th scope="row"><code>lch</code></th> |
| 202 | + <td><code>lch(32.4% 61.2 308.9deg)</code></td> |
| 203 | + <td> |
| 204 | + lightness <span class="fade">[0%, 100%]</span>; |
| 205 | + chroma <span class="fade">[0, 150]</span>; |
| 206 | + hue <span class="fade">[0deg, 360deg]</span> |
| 207 | + </td> |
| 208 | + </tr> |
| 209 | + <tr> |
| 210 | + <th scope="row"><code>oklab</code></th> |
| 211 | + <td><code>oklab(44% 0.088 -0.134)</code></td> |
| 212 | + <td> |
| 213 | + lightness <span class="fade">[0%, 100%]</span>; |
| 214 | + a <span class="fade">[-0.4, 0.4]</span>; |
| 215 | + b <span class="fade">[-0.4, 0.4]</span> |
| 216 | + </td> |
| 217 | + </tr> |
| 218 | + <tr> |
| 219 | + <th scope="row"><code>oklch</code></th> |
| 220 | + <td><code>oklch(44% 0.16 303.4deg)</code></td> |
| 221 | + <td> |
| 222 | + lightness <span class="fade">[0%, 100%]</span>; |
| 223 | + chroma <span class="fade">[0, 0.4]</span>; |
| 224 | + hue <span class="fade">[0deg, 360deg]</span> |
| 225 | + </td> |
| 226 | + </tr> |
| 227 | +</table> |
| 228 | + |
| 229 | +Spaces marked with * are [legacy color spaces]. |
| 230 | + |
| 231 | +[legacy color spaces]: #legacy-color-spaces |
| 232 | + |
| 233 | +## Missing Channels |
| 234 | + |
| 235 | +Colors in CSS and Sass can have "missing channels", which are written `none` and |
| 236 | +represent a channel whose value isn't known or doesn't affect the way the color |
| 237 | +is rendered. For example, you might write `hsl(none 0% 50%)`, because the hue |
| 238 | +doesn't matter if the saturation is `0%`. In most cases, missing channels are |
| 239 | +just treated as 0 values, but they do come up occasionally: |
| 240 | + |
| 241 | +* If you're mixing colors together, either as part of CSS interpolation for |
| 242 | + something like an animation or using Sass's [`color.mix()`] function, missing |
| 243 | + channels always take on the other color's value for that channel if possible. |
| 244 | + |
| 245 | + [`color.mix()`]: /documentation/modules/color#mix |
| 246 | + |
| 247 | +* If you convert a color with a missing channel to another space that has an |
| 248 | + analogous channel, that channel will be set to `none` after the conversion is |
| 249 | + complete. |
50 | 250 |
|
51 |
| -CSS supports many different formats that can all represent the same color: its |
52 |
| -name, its hex code, and [functional notation][]. Which format Sass chooses to |
53 |
| -compile a color to depends on the color itself, how it was written in the |
54 |
| -original stylesheet, and the current output mode. Because it can vary so much, |
55 |
| -stylesheet authors shouldn't rely on any particular output format for colors |
56 |
| -they write. |
| 251 | +{% codeExample 'missing-channels', false %} |
| 252 | + @use 'sass:color'; |
57 | 253 |
|
58 |
| -[functional notation]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value |
| 254 | + $grey: hsl(none 0% 50%); |
59 | 255 |
|
60 |
| -Sass supports many useful [color functions][] that can be used to create new |
61 |
| -colors based on existing ones by [mixing colors together][] or [scaling their |
62 |
| -hue, saturation, or lightness][]. |
| 256 | + @debug color.mix($grey, blue, $method: hsl); // hsl(240, 50%, 50%) |
| 257 | + @debug color.to-space($grey, lch); // lch(53.3889647411% 0 none) |
| 258 | + === |
| 259 | + @use 'sass:color' |
| 260 | + |
| 261 | + $grey: hsl(none 0% 50%) |
| 262 | + |
| 263 | + @debug color.mix($grey, blue, $method: hsl) // hsl(240, 50%, 50%) |
| 264 | + @debug color.to-space($grey, lch) // lch(53.3889647411% 0 none) |
| 265 | +{% endcodeExample %} |
| 266 | + |
| 267 | +### Powerless Channels |
| 268 | + |
| 269 | +A color channel is considered "powerless" under certain circumstances its value |
| 270 | +doesn't affect the way the color is rendered on screen. The CSS spec requires |
| 271 | +that when a color is converted to a new space, any powerless channels are |
| 272 | +replaced by `none`. Sass does this in all cases except conversions to legacy |
| 273 | +spaces, to guarantee that converting to a legacy space always produces a color |
| 274 | +that's compatible with older browsers. |
| 275 | + |
| 276 | +For more details on powerless channels, see [`color.is-powerless()`]. |
| 277 | + |
| 278 | +[`color.is-powerless()`]: /documentation/modules/color#is-powerless |
| 279 | + |
| 280 | +## Legacy Color Spaces |
| 281 | + |
| 282 | +Historically, CSS and Sass only supported the standard RGB gamut, and only |
| 283 | +supported the `rgb`, `hsl`, and `hwb` functions for defining colors. Because at |
| 284 | +the time all colors used the same gamut, every color function worked with every |
| 285 | +color regardless of its color space. Sass still preserves this behavior, but |
| 286 | +only for older functions and only for colors in these three "legacy" color |
| 287 | +spaces. Even so, it's still a good practice to explicitly specify the `$space` |
| 288 | +you want to work in when using color functions. |
| 289 | + |
| 290 | +Sass will also freely convert between different legacy color spaces when |
| 291 | +converting legacy color values to CSS. This is always safe, because they all use |
| 292 | +the same underlying color model, and this helps ensure that Sass emits colors in |
| 293 | +as compatible a format as possible. |
| 294 | + |
| 295 | +## Color Functions |
| 296 | + |
| 297 | +Sass supports many useful [color functions] that can be used to create new |
| 298 | +colors based on existing ones by [mixing colors together] or [scaling their |
| 299 | +channel values]. |
63 | 300 |
|
64 | 301 | [mixing colors together]: /documentation/modules/color#mix
|
65 |
| -[scaling their hue, saturation, or lightness]: /documentation/modules/color#scale |
| 302 | +[scaling their channel values]: /documentation/modules/color#scale |
| 303 | + |
| 304 | +{% funFact %} |
| 305 | + Sass color functions can automatically convert colors between spaces, which |
| 306 | + makes it easy to do transformations in perceptually-uniform color spaces like |
| 307 | + Oklch. But they'll *always* return a color in the same space you gave it, |
| 308 | + unless you explicitly call [`color.to-space()`] to convert it. |
| 309 | + |
| 310 | + [`color.to-space()`]: /documentation/modules/color#to-space |
| 311 | +{% endfunFact %} |
66 | 312 |
|
67 | 313 | {% codeExample 'color-formats', false %}
|
| 314 | + @use 'sass:color'; |
| 315 | + |
68 | 316 | $venus: #998099;
|
69 | 317 |
|
70 |
| - @debug scale-color($venus, $lightness: +15%); // #a893a8 |
71 |
| - @debug mix($venus, midnightblue); // #594d85 |
| 318 | + @debug color.scale($venus, $lightness: +15%, $space: oklch); |
| 319 | + // rgb(170.1523703626, 144.612080603, 170.1172627174) |
| 320 | + @debug color.mix($venus, midnightblue, $method: oklch); |
| 321 | + // rgb(95.9363315581, 74.5687109346, 133.2082569526) |
72 | 322 | ===
|
| 323 | + @use 'sass:color' |
| 324 | + |
73 | 325 | $venus: #998099
|
74 | 326 |
|
75 |
| - @debug scale-color($venus, $lightness: +15%) // #a893a8 |
76 |
| - @debug mix($venus, midnightblue) // #594d85 |
| 327 | + @debug color.scale($venus, $lightness: +15%, $space: oklch) |
| 328 | + // rgb(170.1523703626, 144.612080603, 170.1172627174) |
| 329 | + @debug color.mix($venus, midnightblue, $method: oklch) |
| 330 | + // rgb(95.9363315581, 74.5687109346, 133.2082569526) |
77 | 331 | {% endcodeExample %}
|
0 commit comments