CSS Color Module Level 5
CSS Color Module Level 5
W3C Working Draft
13 April 2026
More details about this document
This version:
Latest published version:
Editor's Draft:
Previous Versions:
History:
Feedback:
CSSWG Issues Repository
Editors:
Chris Lilley
W3C
Una Kravets
Google
Lea Verou
Invited Expert
Former Editor:
Adam Argyle
Google
Suggest an Edit for this Spec:
GitHub Editor
Delta Spec:
yes
Test Suite:
World Wide Web Consortium
W3C
liability
trademark
and
permissive document license
rules apply.
Abstract
This module extends CSS Color
[css-color-4]
to add color modification functions, custom color spaces (ICC profiles), contrast-color(), light-dark() and device-cmyk().
CSS
is a language for describing the rendering of structured documents
(such as HTML and XML)
on screen, on paper, etc.
Status of this document
This section describes the status of this document at the time of its publication.
A list of current W3C publications
and the latest revision of this technical report
can be found in the
W3C standards and drafts index.
This document was published
by the
CSS Working Group
as a
Working Draft
using the
Recommendation
track
Publication as a Working Draft
does not imply endorsement by
W3C
and its Members.
This is a draft document
and may be updated, replaced
or obsoleted by other documents at any time.
It is inappropriate to cite this document as other than a work in progress.
Please send feedback
by
filing issues in GitHub
(preferred),
including the spec code “css-color” in the title, like this:
“[css-color]
…summary of comment…
”.
All issues and comments are
archived
Alternately, feedback can be sent to the (
archived
) public mailing list
www-style@w3.org
This document is governed by the
18 August 2025 W3C Process Document
This document was produced by a group operating under the
W3C Patent Policy
W3C maintains a
public list of any patent disclosures
made in connection with the deliverables of the group;
that page also includes instructions for disclosing a patent.
An individual who has actual knowledge of a patent that the individual believes
contains
Essential Claim(s)
must disclose the information in accordance with
section 6 of the W3C Patent Policy
1.
Introduction
This section is not normative.
This module adds the new functions
contrast-color()
color-mix()
and
light-dark()
and extends existing ones with
relative color syntax
It also extends the
color()
function
so that not only predefined color spaces,
but also custom color spaces defined by ICC profiles
(including calibrated CMYK)
can be used in CSS.
It also adds
device-cmyk
a representation of
uncalibrated cmyk color.
2.
The
syntax
Colors in CSS are represented by the
type:
currentColor
transparent
An
absolute color
is a
whose computed value
has an absolute, colorimetric interpretation.
This means that the value is not:
currentColor
(which depends on the value of the
color
property)
(which depends on the color mode)
(which depends on the color mode)
(which depends on the color mode)
(which has no colorimetric basis)
Nor are any of those values used inside
or in relative color syntax.
The colors that
resolve to sRGB
are:
hex
colors
rgb()
and
rgba()
values, including relative colors
hsl()
and
hsla()
values, including relative colors
hwb()
values, including relative colors
named
colors
The functions that
support legacy color syntax
are:
rgb()
and
rgba()
hsl()
and
hsla()
The
, and
color functions
are
cylindrical polar color
representations using a
angle;
the other
color functions
use
rectangular orthogonal color
representations.
3.
Mixing Colors: the
color-mix()
Function
Web developers, design tools and design system developers
often use color functions to assist in scaling the design
of their component color relations.
With the increasing usage of design systems that support multiple platforms
and multiple user preferences, like the increased capability of Dark Mode in UI,
this becomes even more useful to not need to manually set color,
and to instead have a single source from which schemes are calculated.
Above, a color picker operating in CIE LCH space.
Here, a pair of colors are being used
to define a color scale
on the Chroma-Lightness plane (constant Hue).
Below, the color scale in use on a choropleth map.
Currently Sass, calc() on HSL values, or PostCSS is used to do this.
However, preprocessors are unable to work on dynamically adjusted colors;
all current solutions are restricted to the sRGB gamut
and to the perceptual limitations of HSL
(colors are bunched up in the color wheel,
and two colors with visually different lightness,
like yellow and blue, can have the same HSL lightness).
To meet this need, the color-mix() function takes
a list of one or more
specifications
and returns the result of mixing them,
in a given
in the specified amounts.
color-mix()
= color-mix(
&&
Tests
color-computed-color-mix-function.html
(live test)
(source)
color-valid-color-mix-function.html
(live test)
(source)
3.1.
Colorspace for mixing
If no color interpolation method is specified, assume Oklab.
Otherwise, use the specified colorspace for mixing.
For example, these two are exactly equivalent:
color-mix
in oklab
firebrick
goldenrod
color-mix
firebrick
goldenrod
3.2.
Percentage Normalization
Percentages are required to be in the range 0% to 100%.
Negative percentages are specifically disallowed.
Percentages are normalized by
normalizing mix percentages
Tests
color-mix-percents-01.html
(live test)
(source)
color-mix-percents-02.html
(live test)
(source)
These syntactic forms are thus all equivalent:
color-mix
in lch
purple
50
plum
50
color-mix
in lch
purple
50
plum
color-mix
in lch
purple
plum
50
color-mix
in lch
purple
plum
color-mix
in lch
plum
purple
color-mix
in lch
purple
80
plum
80
All produce a 50-50 mix of
purple and
plum,
in lch:
lch(51.51% 52.21 325.8) which is
rgb(68.51% 36.01% 68.29%).
However, this form is
not
the same, as the alpha is less than one:
color-mix
in lch
purple
30
plum
30
This produces
lch(51.51% 52.21 325.8 / 0.6) which is
rgb(68.51% 36.01% 68.29% / 0.6).
3.3.
Calculating the Result of color-mix
To
calculate a color-mix()
Normalize mix percentages
from the list of
mix items
passed to the function,
with the "forced normalization" flag set to true,
letting
items
and
leftover
be the result.
If
leftover
is 100%,
return
transparent black
converted to the specified interpolation
Let
alpha mult
be
1 -
leftover
interpreting
leftover
as a number between 0 and 1.
If
items
is length 1,
set
color
to the color of that sole item,
converted to the specified interpolation
Otherwise:
Let
item stack
be a
stack
made by reversing
items
(Thus, with the first item at the top of the stack.)
While
item stack
has length 2 or greater:
Pop
from
item stack
twice,
letting
and
be the two results in order.
Let
combined percentage
be the sum of
and
’s percentages.
Interpolate
and
’s colors
as described in
CSS Color 4
§ 12. Color Interpolation
with a progress percentage equal to
’s percentage) /
combined percentage
If the specified color space is a
cylindrical polar color
space,
then the
controls the
interpolation of hue, as described in
CSS Color 4
§ 12.4 Hue Interpolation
If no
is specified,
assume
shorter
Create a new
mix item
with the resulting color
and a percentage of
combined percentage
and
push
it onto
item stack
Set
color
to the color of the sole remaining item in
item stack
Multiply the alpha component of
color
by
alpha mult
Return
color
Note:
In
cylindrical polar color
spaces,
mixing is order-dependent,
as which direction is “shorter” or “longer” around the hue circle
can change depending on what other mixes have already been performed.
This algorithm mixes each color in the specified order,
mixing the result with the next in the list.
For
rectangular orthogonal color
spaces,
the order doesn’t matter,
and the process can be simplified.
Tests
color-mix-basic-001.html
(live test)
(source)
color-mix-missing-components.html
(live test)
(source)
color-mix-non-srgb-001.html
(live test)
(source)
color-computed-color-mix-function.html
(live test)
(source)
color-invalid-color-mix-function.html
(live test)
(source)
color-valid-color-mix-function.html
(live test)
(source)
color-mix-out-of-gamut.html
(live test)
(source)
2d.fillStyle.colormix.html
(live test)
(source)
2d.fillStyle.colormix.currentcolor.html
(live test)
(source)
2d.strokeStyle.colormix.html
(live test)
(source)
This example produces a mixture of 40%
peru
and 60%
palegoldenrod.
color-mix
in lch
peru
40
palegoldenrod
The mixing is done in
lch
color space.
Here is a top-down view, looking along the neutral L axis:
A mixture of two colors, and the mixed output.
We are looking down the CIE L axis onto the ab plane.
There are two axes, labelled
and
which cross at the origin,
which is in the centre of the plot.
Mixtures of peru and palegoldenrod in CIE LCH.
Peru has a hue angle, measured from the positive a axis,
of 63.677 degrees
while palegoldenrod has a hue angle of 98.834 degrees.
Peru has a chroma, or distance from the central neutral axis, of 54.011
while palegoldenrod has a chroma of 31.406.
All possible mixtures lie along the curve. A 40%/60% mixture is shown.
The calculation is as follows:
peru is lch(62.253% 54.011 63.677)
palegoldenrod is lch(91.374% 31.406 98.834)
the mixed lightness is 62.253 * 40/100 + 91.374 * (100-40)/100 = 79.7256
the mixed chroma is 54.011 * 40/100 + 31.406 * (100-40)/100 = 40.448
the mixed hue is 63.677 * 40/100 + 98.834 * (100-40)/100 = 84.771
the mixed result is
lch(79.7256% 40.448 84.771)
This example produces the mixture of teal and olive,
in
lch
color space,
with each lch component being 65% of the value for teal
and 35% of the value for olive.
Note:
interpolating on hue and chroma
keeps the intermediate colors
as saturated as the endpoint colors.
color-mix
in lch
teal
65
olive
);
A mixture of two colors, and the mixed output.
We are looking down the CIE L axis onto the ab plane.
There are two axes, labelled
and
which cross at the origin,
which is in the centre of the plot.
Mixtures of teal and olive.
Teal has a hue angle, measured from the positive a axis,
of 196.4524 degrees
while olive has a hue angle of 99.5746 degrees.
Teal has a chroma, or distance from the central neutral axis, of 31.6903
while olive has a chroma of 56.8124.
Mixtures lie along the dashed curve. A 65%/35% mixture is shown.
The calculation is as follows:
sRGB
teal (#008080) is lch(47.9855% 31.6903 196.4524)
sRGB
olive (#808000) is lch(52.1496% 56.8124 99.5746)
mixed lightness is 47.9855 * 0.65 + 52.1496 * 0.35 = 49.4429
mixed chroma is 31.6903 * 0.65 + 56.8124 * 0.35 = 40.4830
mixed hue is 196.4524 * 0.65 + 99.5746 * 0.35 = 162.5452
mixed result is lch(49.4429% 40.4830 162.5452)
which is a slightly-blueish green:
rgb(7.7377% 52.5730% 37.3213%)
In this example, both percentages are zero, so their sum is also zero:
color-mix
in oklch
teal
olive
);
Thus, the result is transparent black, in the
oklch
color space:
oklch(0% 0 none / 0)
In this example three colors are mixed,
and no percentages are given
so each color contributes one-third of the final result.
color-mix
in oklab
teal
olive
blue
);
The calculation is as follows:
teal (#008080) is oklab(54.31% -0.0896 -0.0236)
olive (#808000) is oklab(58.07% -0.0428 0.1191)
blue (#0000FF) is oklab(45.20% -0.0325 -0.3115)
mixed lightness is (54.31 + 58.07 + 45.20) / 3 = 52.53%
mixed a is (-0.0896 + -0.0428 + -0.0325) / 3 = -0.0550
mixed b is (-0.0236 + 0.1191 + -0.3115) / 3 = -0.0720
mixed result is
oklab(52.53% -0.0550 -0.0720)
3.4.
Effect of Mixing Color Space on color-mix
The choice of mixing color space can have a large effect on the end result.
This example is a 50% mix of white and black,
in three different color spaces.
color-mix
in lch
white
black
);
color-mix
in xyz
white
black
);
color-mix
in srgb
white
black
);
The calculation is as follows:
sRGB
white (#FFF) is lch(100% 0 0)
sRGB
black (#000) is lch(0% 0 0)
The mix in LCH is
lch(50% 0 0)
The mix in XYZ is
lch(76% 0 0)
The mix in sRGB is
lch(53.4% 0 0)
The mix in LCH gives an L value of 50%,
a perfect mid gray, exactly as expected
(mixing in Lab would do the same,
as the Lightness axis is the same in LCH and Lab).
The mix in XYZ gives a result that is too light;
XYZ is linear-light but is not perceptually uniform.
The mix in sRGB gives a result that is a bit too light;
sRGB is neither perceptually uniform nor linear-light.
This example produces the mixture of
the a red and a sky blue,
in
xyz
color space,
with the mixture being 75.23% of that of the red
(and thus, 24.77% of that of the blue).
color-mix
in xyz
rgb
82.02
30.21
35.02
75.23
rgb
5.64
55.94
85.31
));
The calculation is as follows:
rgb(82.02% 30.21% 35.02%) is lch(52% 58.1 22.7) which is X=0.3214, Y=0.2014, Z=0.0879.
rgb(5.64% 55.94% 85.31%) is lch(56% 49.1 257.1) which is X=0.2070, Y=0.2391, Z=0.5249.
mixed result X=(0.3214 * 0.7523) + (0.2070 * (1 - 0.7523)) = 0.29306.
mixed result Y=(0.2014 * 0.7523) + (0.2391 * (1 - 0.7523)) = 0.21074.
mixed result Z=(0.0879 * 0.7523) + (0.5249 * (1 - 0.7523)) = 0.19614.
mix result is
lch(53.0304% 38.9346 352.8138) which is rgb(72.300% 38.639% 53.557%)
This example is a 50% mix of white and blue,
in three different color spaces.
color-mix
in lch
white
blue
);
color-mix
in oklch
white
blue
);
color-mix
in srgb
white
blue
);
The calcuation is as follows:
white
is rgb(100% 100% 100%)
which is lch(100% 0 none)
which is oklch(100% 0 none)
blue
is rgb(0% 0% 100%)
which is lch(29.5683% 131.201 301.364)
which is oklch(45.201% 0.31321 264.052)
mix
in lch is lch(64.7841% 65.6008 301.364) which is quite purple
mix
in oklch is oklch(72.601% 0.15661 264.052)
mix
in srgb is rgb(50% 50% 100%) which is also a bit purple
This example is a mix of two colors,
in
hsl
color space,
where one of the colors to be mixed
is outside the
sRGB
gamut.
color-mix
in hsl
color
display-p3
80
yellow
);
The calcuation is as follows:
color(display-p3 0 1 0)
is color(srgb -0.5116 1.01827 -0.3107) which is outside the sRGB gamut
Converted to
hsl
hsl(127.879 301.946 25.334)
yellow is
hsl(60 100% 50%)
the hue is 127.879 × 0.8 + 60 × 0.2 = 114.3032
the saturation is 301.946 × 0.8 + 100 × 0.2 = 261.5568
the lightness is 25.334 × 0.8 + 50 × 0.2 = 30.2672
the mixed result is
hsl(114.3032 261.5568 30.2672) which is
color(srgb -0.3387 1.0943 -0.48899)
device-cmyk()
can be used in
color-mix()
but the result will depend on how the implementation
chooses to obtain a computed value.
color-mix
in lab
device-cmyk
0.091777
0.043303
0.312816
0.000000
100
yellow
);
Since the first color is at 100%,
the second color is 0% and does not affect the mixed result in any way.
The result is thus the computed value of the first color,
in CIE Lab.
To visualize the result,
let us say that the device CMYK values
are in fact to be printed using SWOP 2006 coated.
device-cmyk(0.091777 0.043303 0.312816 0.000000) is
lab(91.44% 4.142 20.52)
Suppose the implementation uses an ICC profile
to obtain
lab()
colors,
and in this example a FOGRA39 Coated profile is used:
device-cmyk(0.091777 0.043303 0.312816 0.000000) is
lab(91.840596 -3.559090 20.449159)
The deltaE 2000 between this and the original printed color
is
8.17
which is clearly visible.
Now suppose another implementation uses
the naive color conversion algorithm,
giving an sRGB result.
device-cmyk(0.091777 0.043303 0.312816 0.000000) is
rgb(90.8223% 95.6697% 68.7184%)
which is
lab(94.02% -12.31 31.79)
The deltaE 2000 between this and the original printed color
is
14.3
which is very visible.
3.5.
Effect of Non-Unity Alpha on color-mix
So far, all the
color-mix()
examples
have used fully opaque colors.
To simplify the examples,
the
premultilication
and unpremultiplication steps
were omitted
because these would simply multiply by 1, and divide by 1,
so the result would be unchanged.
In the general case,
colors may have non-unity alpha components
and thus the
premultiply
, interpolate, unpremultiply steps
must not be omitted.
This example is 25% semi-opaque red
and 75% semi-opaque green.
mixed in sRGB.
Both the correct (
premultiplied
and incorrect (non-premultiplied)
workings are shown.
color-mix
in srgb
rgb
100
0.7
25
rgb
100
0.2
));
The calcuation is as follows:
rgb(100% 0% 0% / 0.7)
when premultiplied, is [0.7, 0, 0]
rgb(0% 100% 0% / 0.2)
when premultiplied, is [0, 0.2, 0]
the premultiplied, interpolated result is
[0.7 * 0.25 + 0 * (1 - 0.25), 0 * 0.25 + 0.2 * (1 - 0.25), 0 * 0.25 + 0 * (1 - 0.25)]
which is [0.175, 0.150, 0]
the interpolated alpha is 0.7 * 0.25 + 0.2 * (1 - 0.25) = 0.325
the un-premultiplied result is
[0.175 / 0.325, 0.150 / 0.325, 0 / 0.325]
which is [0.53846, 0.46154, 0]
so the mixed color is
color(srgb 0.53846 0.46154 0 / 0.325)
The
incorrect
calculation would be:
the interpolated result is
[1 * 0.25 + 0 * (1 - 0.25), 0 * 0.25 + 1 * (1 - 0.25), 0 * 0.25 + 0 * (1 - 0.25)]
which is [0.25, 0.75, 0]
so the
incorrect
mixed color is
color(srgb 0.25 0.75 0 / 0.325)
This is a
huge
difference; the ΔE2000 between the correct and incorrect results is 30.7!
When the percentage normalization generates an alpha multiplier,
the calculation is the same except for an additional last step.
This example is similar to the previous one,
25% semi-opaque red
and 75% semi-opaque green.
mixed in sRGB.
However in this case the percentages are specified as
20% of the first color and 60% of the second.
This adds to 80% so the alpha multiplier is 0.8.
The mix percentages are then scaled
by a factor of 100/80:
20% * 100/80 = 25%
60% * 100/80 = 75%
giving the same final mix percentages as the previous example.
color-mix
in srgb
rgb
100
0.7
20
rgb
100
0.2
60
);
The calcuation is as follows:
rgb(100% 0% 0% / 0.7)
when premultiplied, is [0.7, 0, 0]
rgb(0% 100% 0% / 0.2)
when premultiplied, is [0, 0.2, 0]
the premultiplied, interpolated result is
[0.7 * 0.25 + 0 * (1 - 0.25), 0 * 0.25 + 0.2 * (1 - 0.25), 0 * 0.25 + 0 * (1 - 0.25)]
which is [0.175, 0.150, 0]
the interpolated alpha is 0.7 * 0.25 + 0.2 * (1 - 0.25) = 0.325
the un-premultiplied result is
[0.175 / 0.325, 0.150 / 0.325, 0 / 0.325]
which is [0.53846, 0.46154, 0]
so the mixed color would be
color(srgb 0.53846 0.46154 0 / 0.325)
there is a 0.8 alpha multiplier,
so the alpha of the mixed result is actually 0.325 * 0.8 = 0.260
so the mixed color is actually
color(srgb 0.53846 0.46154 0 / 0.260)
Note:
do not multiply the interpolated alpha by the alpha multiplier
and then use that to undo premultiplication.
That would be correct if the mix percentages were not scaled to sum to 100%,
but they are, so doing it this way would adjust the mixed color
twice
4.
Relative Colors
4.1.
Processing Model for Relative Colors
In previous levels of this specification,
the color functions could only specify colors in an absolute manner,
by directly specifying all of the color components.
The new
relative color
syntax
extends
modern color syntax
to allow existing colors to be modified
using the color functions:
if an
origin color
is specified,
then each color component
(and the alpha component, if specified)
can
either
be directly specified,
or taken from the origin color
(and possibly modified with
math functions
).
The origin color and the relative color need not use the same color function.
Required conversion
All operations take part in the
color space
of the
relative color
function;
if the
originally specified color space
for the
origin color
used a different color function,
it’s first converted into the chosen color function,
so it has meaningful values for the components,
and
component keywords
refer to the color space of the relative color,
not
the
origin color
If the alpha value of the relative color is omitted,
it defaults to that of the
origin color
(rather than defaulting to
100%
, as it does in the absolute syntax).
When relative color syntax is used,
color component
values,
whether directly specified
or arising from color space conversion,
are
not clamped
to the reference ranges but are retained as-is.
This preserves out of gamut values,
if the destination color space is capable of representing them.
However, when relative color syntax is used,
alpha component
values
whether directly specified
or arising from color space conversion,
are
clamped to the reference range.
Missing components are handled the same way as with
CSS Color 4
§ 12.2 Interpolating with Missing Components
the origin colorspace and the relative function colorspace
are checked for
analogous components
which are then
carried forward
as missing.
While most uses of
relative color
syntax
will use the
component keywords
in their corresponding argument,
you can use them in any position.
Beware when using components outside their normal position;
when percentages are resolved to numbers,
there is no "magic scaling"
to account for the changed position
if those numbers are used in a different place.
There is no relative
device-cmyk()
syntax.
4.2.
Relative Color Syntax
The precise details of each function’s syntactic changes
to accommodate
relative colors
are listed below,
but they all follow a common structure:
An
origin color
can be specified with a
from
value
at the start of the function.
This includes the optional alpha component, if specified.
If no origin color is specified, the function is not a relative color.
If an
origin color
is specified,
the remaining arguments can either be specified directly, as normal,
or be specified as a
component keyword
referring to one of the components of the
origin color
converted to the
color space
of the relative color.
Math functions
can also use these keywords
to do dynamic modifications of the
origin color’s
components.
Relative color
syntax doesn’t change whether an argument is required or optional.
Relative color syntax only applies to the
modern color syntax
It
cannot
be used with
legacy color syntax
and attempting to do so is an error.
However, the
origin color
can use either modern or legacy syntax.
The
component keywords
return a
, or
none
if they were originally specified as a
or an
that
is resolved to a
and the
is resolved to a
of degrees
(which is the
canonical unit
in the range [0, 360].
Tests
relative-color-with-zoom.html
(live test)
(source)
relative-currentcolor-a98rgb-01.html
(live test)
(source)
relative-currentcolor-lch-01.html
(live test)
(source)
relative-currentcolor-rgb-01.html
(live test)
(source)
relative-currentcolor-displayp3-01.html
(live test)
(source)
relative-currentcolor-oklab-01.html
(live test)
(source)
relative-currentcolor-rgb-02.html
(live test)
(source)
relative-currentcolor-hsl-01.html
(live test)
(source)
relative-currentcolor-oklch-01.html
(live test)
(source)
relative-currentcolor-xyzd50-01.html
(live test)
(source)
relative-currentcolor-hsl-02.html
(live test)
(source)
relative-currentcolor-prophoto-01.html
(live test)
(source)
relative-currentcolor-xyzd65-01.html
(live test)
(source)
relative-currentcolor-hwb-01.html
(live test)
(source)
relative-currentcolor-rec2020-01.html
(live test)
(source)
relative-currentcolor-lab-01.html
(live test)
(source)
relative-currentcolor-rec2020-02.html
(live test)
(source)
relative-currentcolor-visited-getcomputedstyle.html
(live test)
(source)
alpha-color-computed.html
(live test)
(source)
alpha-color-parsing-invalid.html
(live test)
(source)
alpha-color-parsing-valid.html
(live test)
(source)
color-computed-relative-color.html
(live test)
(source)
color-invalid-relative-color.html
(live test)
(source)
color-valid-relative-color.html
(live test)
(source)
relative-color-out-of-gamut.html
(live test)
(source)
For example, if a color is specified using
then RCS in the same colorspace will use the resolved
form:
html
--bluegreen
oklab
54.3
-22.5
-5
);
.overlay
background
oklab
from
var
--bluegreen
calc
1.0
- l
calc
a *
0.8
);
In this example, the specified percentages are resolved to numbers,
giving oklab(0.543 -0.09 -0.02).
The resulting RCS color has l = 1 - 0.543 = 0.457,
a = -0.09 * 0.8 = -0.072,
and b is unchanged at -0.02:
oklab(0.457 -0.072 -0.02).
For example, if the origin color has a hue
specified in degrees,
then RCS in the same colorspace will use the resolved
form:
html
--base
oklch
52.6
0.115
44.6
deg
.summary
background
oklch
from
var
--base
l c
calc
h +
90
));
In this example the resulting RCS color is oklch(0.526 0.115 134.6).
Had the origin color hue
been specified in another unit,
such as radians or turns,
still the resolved
would be the number of degrees.
By using the
component keywords
in a
math function
an
origin color
can be manipulated in more advanced ways.
html
--color
green
.foo
--darker-accent
lch
from
var
--color
calc
l /
c h
);
In this example, the
origin color
is darkened
by cutting its lightness in half,
without changing any other aspect of the color.
Note as well that the
origin color
is a color keyword
(and thus, sRGB),
but it’s automatically interpreted as an LCH color
due to being used in the
lch()
function.
For example, if a theme color is specified as opaque,
but in a particular instance you need it to be partially transparent:
html
--bg-color
blue
.overlay
background
rgb
from
var
--bg-color
r g b /
80
);
In this example, the r, g, and b components of the
origin color
are unchanged,
indicated by specifying them with the keywords
drawing their values from the
origin color
but the opacity is set to
80%
to make it slightly transparent,
regardless of what the
origin color’s
opacity was.
For example, a Display P3 color
which is outside the gamut of sRGB can still be represented,
as it is not clipped.
--vivid-yellow
color
display-p3
);
--paler-yellow
color
from
var
--vivid-yellow
srgb r g
calc
b +
0.5
));
Here --vivid-yellow, once converted to sRGB,
is
rgb(100% 100% -34.63%)
and the negative blue component is not clamped.
The result of the RCS calculation is
rgb(100% 100% 15.37%)
For example, attempting to double an alpha of 0.7 in the origin color
results in an alpha in the result of 1, not 1.4.
--tan
oklch
78
0.06
75
0.7
);
--deeper-tan
oklch
from
var
--tan
l c h /
calc
alpha *
));
For example, to do a rough approximation of grayscaling a color:
--blue-into-gray
rgb
from
var
--color
calc
r *
.3
+ g *
.59
+ b *
.11
calc
r *
.3
+ g *
.59
+ b *
.11
calc
r *
.3
+ g *
.59
+ b *
.11
));
Using this,
red
would become
rgb(76.5 76.5 76.5)
lime
would become
rgb(150.45 150.45 150.45)
and
blue
would become
rgb(150.45 150.45 150.45)
A more moderate color, like
darkolivegreen
which has RGB values
rgb(85 107 47)
would become
rgb(93.8 93.8 93.8)
(Rough because firstly,
although this looks like a luminance calculation,
the red green and blue values
are manipulated in gamma-encoded space
rather than linear-light;
secondly,
the weighting factors are those
for the obsolete NTSC color space,
not sRGB.)
(Note, too, that this is just to illustrate the syntax;
an easier and more accurate way to grayscale a color
is to use the
oklch()
function,
as that color space is more accurate to human perception:
oklch(from var(--color) l 0 h)
preserves the lightness,
but zeroes out the chroma,
which determines how "colorful" the color is.)
For example,
color
color
from
color
srgb
60
srgb alpha
0.6
0.6
0.9
);
The alpha component is resolved as a
giving 0.6; thus the resulting color is
color(srgb 0.6 0.6 0.6 / 0.9)
However, in this second example, again the alpha resolves to 0.6,
giving a very different color due to the color component range
of 0 to 255 in
rgb() syntax:
color
rgb
from
rgb
60
alpha
153
153
0.9
);
which results in
rgb(0.6 153 153 / 0.9)
and
not
rgb(153 153 153 / 0.9)
In this example the achromatic origin color has a missing hue;
the
relative color
also has a missing hue,
which affects a gradient using that color.
html
--bg
hsl
none
50
);
.foo
--darker-bg
oklch
from
var
--bg
calc
l *
0.8
c h
);
.bar
background
linear-gradient
in Oklab to right
var
--darker-bg
),
#4C3
);
The value of --bg when converted to OkLCh is
oklch(0.592 0.009 17.42)
but the analogous hue component is carried forward
giving oklch(0.592 0.009 none).
These values are then used in the relative function, giving
the darker color oklch(0.474 0.009 none).
The light green in the gradient is
oklch(0.743 0.222 141.6),
and so, when interpolated, the other color take that hue,
becoming
oklch(0.474 0.009 141.6).
Thus, the gradient will have a constant greenish hue.
If an implementation failed to do this carrying forward,
the grayish --darker-bg would have a hue of 0,
giving an undesirable reddish tint at the start of the gradient.
Correct (above) and incorrect (below, reddish) gradients.
However, if calculations are done on missing values,
none
is treated as 0.
4.3.
Relative sRGB Colors
The meaning of sRGB colors is defined in
CSS Color 4
§ 5 sRGB Colors
The grammar of the
modern color syntax
rgb()
and
rgba()
functions are extended as follows:
= rgb( [ from
none]
{3}
[ / [
none] ]
= rgba( [ from
none]
{3}
[ / [
none] ]
Within a
relative color
syntax
rgb()
or
rgba()
function,
the allowed
component keywords
are:
, and
are all
that correspond to the
origin color’s
red, green, and blue components
after
conversion, if required
to sRGB.
255.0 is equivalent to 100%.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-rgb-01.html
(live test)
(source)
relative-currentcolor-rgb-02.html
(live test)
(source)
To manipulate color components in the sRGB color space:
rgb
from
indianred
255
g b
This takes the sRGB value of indianred (205 92 92) and replaces
the red component with 255 to give
rgb(255 92 92).
Relative sRGB color syntax is
only
applicable to the non-legacy RGB syntactic forms.
For example, this attempt to use the rgba
legacy color syntax
with commas would be incorrect
rgba
from
darkblue
16
32
0.5
Instead, use
rgb
from
darkblue
16
32
b /
0.5
This takes the sRGB value of darkblue (0 0 139) and replaces
the red, green and alpha components to give
rgb(16 32 139 / 0.5)
4.4.
Relative HSL Colors
The meaning of HSL colors is defined in
CSS Color 4
§ 7 HSL Colors: hsl() and hsla() functions
The grammar of the
modern color syntax
hsl()
and
hsla()
functions is extended as follows:
= hsl([from
none]
none]
none]
[ / [
none] ]
= hsla([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
hsl()
or
hsla()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
HSL hue, in degrees,
after
conversion, if required
to sRGB,
normalized to a [0, 360] range. 90 is equivalent to 90deg.
and
are
s that correspond to the
origin color’s
HSL saturation and lightness,
after
conversion, if required
to sRGB.
100 is equivalent to 100%.
alpha
is a
that corresponds to the
origin color’s
alpha transparency 1.0 is equivalent to 100%.
Tests
relative-currentcolor-hsl-01.html
(live test)
(source)
relative-currentcolor-hsl-02.html
(live test)
(source)
This adds 180 degrees to the hue angle, giving a complementary color.
--accent
lightseagreen
--complement
hsl
from
var
--accent
calc
h +
180
s l
);
lightseagreen is hsl(177deg 70% 41%), so --complement is
hsl(357deg 70% 41%)
Relative HSL color syntax is only applicable to the non-legacy HSL syntactic forms.
4.5.
Relative HWB Colors
The meaning of HWB colors is defined in
CSS Color 4
§ 8 HWB Colors: hwb() function
The grammar of the
hwb()
function is extended as follows:
hwb()
= hwb([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
hwb()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
HWB hue, in degrees,
after
conversion, if required
to sRGB,
normalized to a [0, 360] range. 90 is equivalent to 90deg.
and
are
s that correspond to the
origin color’s
HWB whiteness and blackness
after
conversion, if required
to sRGB.
100 is equivalent to 100%.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-hwb-01.html
(live test)
(source)
4.6.
Relative Lab Colors
The meaning of Lab colors is defined in
CSS Color 4
§ 9.1 CIE Lab and LCH
The grammar of the
lab()
function is extended as follows:
lab()
= lab([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
lab()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
CIE Lightness
after
conversion, if required
, to CIE Lab.
100 is equivalent to 100%.
and
are
that correspond to the
origin color’s
CIE Lab a and b axes
after
conversion, if required
, to CIE Lab.
125 is equivalent to 100%, while -125 is equivalent to -100%.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-lab-01.html
(live test)
(source)
Multiple ways to adjust the transparency of a base color:
lab(from var(--mycolor) l a b / 100%)
sets the alpha of
var(--mycolor)
to 1.0, regardless of what it originally was.
lab(from var(--mycolor) l a b / calc(alpha * 0.8))
reduces the alpha of
var(--mycolor)
by 20% of its original value.
Note that all the adjustments are lossless in the sense that no gamut clipping occurs, since lab() encompasses all visible color.
This is not true for the alpha adjustments in the sRGB based functions
(such as 'rgb()', 'hsl()', or 'hwb()'),
which would also convert to sRGB
as a necessary step for calculation of HSL or HWB,
in addition to adjusting the alpha transparency.
Fully desaturating a color to gray, keeping the exact same lightness:
--mycolor
orchid
// orchid is
lab
62.753
52.460
-34.103
--mygray:
lab
from
var
--mycolor
// mygray is
lab
62.753
which is
rgb
59.515
59.515
59.515
4.7.
Relative Oklab Colors
The meaning of Oklab colors is defined in
CSS Color 4
§ 9.2 Oklab and OkLCh
The grammar of the
oklab()
function is extended as follows:
oklab()
= oklab([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
oklab()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
Oklab Lightness
after
conversion, if required
, to Oklab.
1.0 is equivalent to 100%.
and
are
that correspond to the
origin color’s
Oklab a and b axes
after
conversion, if required
, to Oklab.
0.4 is equivalent to 100%, while -0.4 is equivalent to -100%.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-oklab-01.html
(live test)
(source)
4.8.
Relative LCH Colors
The meaning of LCH colors is defined in
CSS Color 4
§ 9.1 CIE Lab and LCH
The grammar of the
lch()
function is extended as follows:
lch()
= lch([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
lch()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
CIE Lightness
after
conversion, if required
, to CIE LCH.
100 is equivalent to 100%.
is a
that corresponds to the
origin color’s
LCH chroma
after
conversion, if required
, to CIE LCH.
150 is equivalent to 100%.
is a
that corresponds to the
origin color’s
LCH hue, in degrees,
after
conversion, if required
, to CIE LCH,
normalized to a [0, 360] range. 90 is equivalent to 90deg.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-lch-01.html
(live test)
(source)
lch(from peru calc(l * 0.8) c h)
produces a color that is 20% darker than
peru or lch(62.2532% 54.0114 63.6769), with its chroma and hue left unchanged.
The result is
lch(49.80256 54.0114 63.6769)
This adds 180 degrees to the hue angle, giving the complementary color.
--accent
lightseagreen
--complement
lch
from
var
--accent
l c
calc
h +
180
));
lightseagreen is lch(65.4937 39.4484 190.1013), so --complement is
lch(65.4937 39.4484 370.1013)
Fully desaturating a color to gray, keeping the exact same lightness:
--mycolor
orchid
// orchid is
lch
62.753
62.571
326.973
--mygray:
lch
from
var
--mycolor
// mygray is
lch
62.753
326.973
which is
rgb
59.515
59.515
59.515
But now (since the hue was preserved)
re-saturating
again
--mymuted
lch
from
var
--mygray
30
);
// mymuted is
lch
62.753
30
326.973
which is
rgb
72.710
53.293
71.224
However, unlike HSL, manipulations are not guaranteed to be in-gamut.
In this example, the aim is to produce a new color
with the same Lightness and Chroma,
but the triad (hue differs by 120 degrees).
The origin color is inside the RGB gamut,
but rotating the hue in LCH
produces an out of gamut color.
--mycolor
lch
60
90
320
);
lch
from
var
--mycolor
l c
calc
h -
120
));
This gives a very high-chroma blue-green,
lch(60% 90 200)
which is color(srgb -0.6 0.698 0.772)
and thus out of gamut (negative red value) for sRGB.
Indeed, it is out of gamut for display-p3:
color(display-p3 -0.46 0.68 0.758)
and even rec2020:
color(rec2020 -0.14 0.623 0.729).
The closest color inside the sRGB gamut would be
lch(60.71% 37.56 201.1)
which is
rgb(0% 64.2% 66.3%). The difference in chroma (37.5, instead of 90) is huge.
Diagram of CIE CH plane showing relative color manipulation.
The
and
axes are labelled,
and cross in the middle.
We are looking down the central Lightness axis.
The maximal gamut of the sRGB color space
is shown as an irregular, convex polygon.
This diagram shows the sRGB gamut, in the CIE ab plane.
Small circles indicate the primary and secondary color.
The
origin color, shown as a large circle, is in gamut for sRGB;
but becomes
out of gamut (shown as a grey fill and red border)
when the LCH hue is rotated -120°.
The gamut-mapped
result has much lower chroma.
Performing the same operation in HSL will return an in-gamut result.
But it is unsatisfactory in other ways:
--mycolor
lch
60
90
320
);
hsl
from
var
--mycolor
calc
h -
120
s l
);
In HSL, --mycolor is
hsl(289.18 93.136% 65.531%)
so subtracting 120 degrees gives
hsl(169.18 93.136% 65.531%).
Converting that result back to LCH
lch(89.0345% 49.3503 178.714)
we see that, due to the hue rotate in HSL,
Lightness shot up from 60% to 89%,
the Chroma has dropped from 90 to 49,
and the Hue actually changed by 141 degrees, not 120.
4.9.
Relative OkLCh Colors
The meaning of OkLCh colors is defined in
CSS Color 4
§ 9.2 Oklab and OkLCh
The grammar of the
oklch()
function is extended as follows:
oklch()
= oklch([from
none]
none]
none]
[ / [
none] ]
Within a
relative color
syntax
oklch()
function,
the allowed
component keywords
are:
is a
that corresponds to the
origin color’s
Oklab Lightness
after
conversion, if required
, to OkLCh.
1.0 is equivalent to 100%.
is a
that corresponds to the
origin color’s
OkLCh chroma
after
conversion, if required
, to OkLCh.
0.4 is equivalent to 100%.
is a
that corresponds to the
origin color’s
OkLCh hue, in degrees,
after
conversion, if required
, to OkLCh,
normalized to a [0, 360] range. 90 is equivalent to 90deg.
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
Tests
relative-currentcolor-oklch-01.html
(live test)
(source)
Because OkLCh is both perceptually uniform and chroma-preserving,
and because the axes correspond to easily understood attributes of a color,
OkLCh is a good choice for color manipulation.
In this example, the aim is again to produce a new color
with the same Lightness and Chroma,
but the triad (hue differs by 120 degrees).
In this example, we will do the manipulation in OkLCh.
The origin color is inside the RGB gamut,
but rotating the hue in OkLCh
again produces an out of gamut color.
--mycolor
lch
60
90
320
);
oklch
from
var
--mycolor
l c
calc
h -
120
));
--mycolor is
oklch(0.69012 0.25077 319.893).
Subtracting 120 from the Hue gives a very high-chroma blue-green,
oklch(0.69012 0.25077 199.893)
which is out of sRGB gamut,
color(srgb -0.6018 0.7621 0.8448)
as the negative red component indicates.
Bring this into gamut
by reducing OkLCh Chroma, yields
oklch(0.69012 0.1173 199.893).
The OkLCh chroma has dropped from 0.251 to 0.117.
4.10.
Relative Alpha Colors
Relative alpha colors refer to an origin color, and only change the alpha channel. The meaning of alpha channels is defined in
CSS Color 4
§ 4.2 Representing Transparency in Colors: the
The grammar of the
alpha()
function, new in this level, is as follows:
alpha()
= alpha([from
[ / [
none] ]
Within a
relative color
syntax
alpha()
function,
the allowed
component keywords
are:
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
The color components of the
origin color
are unchanged,
the alpha component is modified or replaced.
The result of this function is in the color space of the origin color.
Tests
alpha-color-computed.html
(live test)
(source)
alpha-color-parsing-invalid.html
(live test)
(source)
alpha-color-parsing-valid.html
(live test)
(source)
For example, here the result is the same as the origin color,
but the alpha is changed to 80%
--mycolor
oklch
60
0.25
315
0.3
);
alpha
from
var
--mycolor
80
);
For example, here the result is the same as the origin color,
but the alpha is modified to be half of that in the origin color
--mycolor
oklch
60
0.25
315
0.8
);
alpha
from
var
--mycolor
calc
alpha *
0.5
));
5.
Specifying Predefined and Custom Color Spaces: the
color()
Function
The
color()
function allows a color to be specified
in a particular, given
color space
(rather than the implicit sRGB color space that most of the other color functions operate in).
In this level the
color()
function is extended
to allow custom color spaces,
in addition to the predefined spaces from
CSS Color 4
§ 10. Predefined Color Spaces
It is also extended to allow relative, rather than just absolute, colors.
Its syntax is now as follows:
color()
= color( [from
[ / [
none ] ]
= [
none ]
none ]
{3}
= srgb
srgb-linear
display-p3
a98-rgb
prophoto-rgb
rec2020
none ]
{3}
The color function takes parameters specifying a color, in an explicitly listed color space.
It represents either an
invalid color
, as described below,
or a
valid color
Any color which is not an
invalid color
is a
valid color
A color may be a
valid color
but still be outside the range of colors
that can be produced by an output device
(a screen, projector, or printer).
It is said to be
out of gamut
for that color space.
An out of gamut color has component values
less than 0 or 0%, or greater than 1 or 100%.
These are not invalid; instead, for display, they are
gamut-mapped
using a relative colorimetric intent
which brings the values within the range 0/0% to 1/100%
at computed-value time.
Each
valid color
is either in-gamut for the output device (screen, or printer),
or it is
out of gamut
5.1.
Relative Color-Function Colors
Within a
relative color
syntax
color()
function using
the number and name of the allowed
component keywords
are:
defined by the
components
descriptor on the corresponding
@color-profile
if present;
otherwise, no relative color manipulation is valid. They are
s that correspond to the
origin color’s
components
after
conversion, if required
to the color space of the color profile. The value 1.0 corresponds to 100%.
Within a
relative color
syntax
color()
function using
the allowed
component keywords
are:
, and
are all
that correspond to the
origin color’s
red, green, and blue components
after
conversion, if required
to the predefined RGB color space.
The value 1.0 corresponds to 100%.
Within a
relative color
syntax
color()
function using
the allowed
component keywords
are:
are all
that correspond to the
origin color’s
X, Y and Z components
after
conversion, if required
to relative CIE XYZ color space
adapted to the relevant white point.
The value 1.0 corresponds to 100%.
Within a
relative color
syntax
color()
function
using either
or
, an additional allowed
component keyword
is:
alpha
is a
that corresponds to the
origin color’s
alpha transparency. 1.0 is equivalent to 100%.
The parameters have the following form:
An
or
denoting the color space.
If this is an
it denotes one of the
predefined color spaces
CSS Color 4
§ 10. Predefined Color Spaces
(such as
display-p3
);
if it is a
it denotes a custom
color space, defined by a
@color-profile
rule.
Individual predefined color spaces
may further restrict whether
s or
or both, may be used.
If the
names a non-existent color space
(a name that does not match one of the
predefined color spaces),
or a predefined but unsupported color space,
this argument represents an
invalid color
If the
names a non-existent color space
( a name that does not match an
color profile’s
name,
or which matches but the corresponding profile has not loaded,
or does not represent a valid profile),
this argument represents an
invalid color
One or more
s or
s providing the parameter values that the color space takes.
For custom color spaces,
specified component values
less than 0 or 0%, or greater than 1 or 100%
are not invalid;
they are clamped to the valid range at computed value time.
This is because ICC profiles typically do not accept
out of range input values.
For custom color spaces, if more
s or
s are provided than parameters that the color space takes,
the excess
s at the end are ignored.
The color is still a
valid color
For custom color spaces, if more
s or
s are provided than components listed in the optional components descriptor,
the additional values at the end are still valid,
but cannot be used in Relative Color Syntax.
The color is still a
valid color
For custom color spaces, if fewer
s or
s are provided than parameters that the color space takes,
the missing parameters default to
(This is particularly convenient for multicomponent printers
where the additional inks are spot colors or varnishes
that most colors on the page won’t use.)
The color is still a
valid color
For predefined color spaces,
specified component values
less than 0 or 0%, or greater than 1 or 100%
are not invalid;
these out of gamut colors
are gamut mapped to the valid range at computed value time,
with a relative colorimetric intent.
An optional slash-separated
.If omitted, it defaults to
100%
Tests
color-computed-relative-color.html
(live test)
(source)
For example, Relative Color Syntax in the CIE XYZ D65 colorspace
is used to generate a color which has
the same chromaticity but exactly half the luminance
of the base color:
--base
color
display-p3
0.7
0.5
0.1
);
--dark
color
from
var
--base
xyz-d65
calc
x/
calc
y/
calc
z/
));
The origin color is color(xyz-d65 0.281 0.253 0.044)
and so the relative color is color(xyz-d65 0.14 0.126 0.022).
5.2.
Custom Color Spaces
CSS allows
colors
to be specified by reference to a color profile.
This could be for example a calibrated CMYK printer,
or an RGB color space,
or any other color or monochrome output device which has been characterized.
This example specifies four calibrated colors:
two are custom spaces
(for a SWOP-coated CMYK press,
and for a wide-gamut seven-ink printer),
the other two are predefined spaces
(the ProPhoto RGB,
and display-p3 RGB spaces).
In each case, the numerical parameters
are in the range 0.0 to 1.0
(rather than, for example, 0 to 255).
color
color
--swopc
0.0134
0.8078
0.7451
0.3019
);
color
color
--indigo
0.0941
0.6274
0.3372
0.1647
0.0706
0.1216
);
color
color
prophoto-rgb
0.9137
0.5882
0.4784
);
color
color
display-p3
0.3804
0.9921
0.1412
);
The colors not using a predefined color space
CSS Color 4
§ 10. Predefined Color Spaces
are distinguished by their use of
and
also need a matching
@color-profile
at-rule
somewhere in the stylesheet,
to connect the name with the profile data.
Tests
at-color-profile-001.html
(live test)
(source)
@color-profile
--swopc
src
url
'http://example.org/swop-coated.icc'
);}
@color-profile
--indigo
src
url
'http://example.org/indigo-seven.icc'
);}
5.3.
Specifying a Color Profile: the
@color-profile
at-rule
The
@color-profile
rule
defines and names a
color profile
which can later be used in the
color()
function to specify a color.
It’s defined as:
@color-profile = @color-profile [
device-cmyk
] {
Tests
at-color-profile-001.html
(live test)
(source)
The
gives the
color profile’s
name,
by which it will be used in a CSS stylesheet.
Alternatively, the
device-cmyk
keyword
means that this color profile will,
if valid,
be used to resolve colors specified in
device-cmyk
The
@color-profile
rule accepts the descriptors defined in this specification.
Name:
src
For:
@color-profile
Value:
Initial:
n/a
The
src
descriptor specifies the URL to retrieve the color-profile information from.
If multiple
@color-profile
rules are defined with the same name,
the last one in document order wins, and all preceding ones are ignored.
The retrieved ICC profile is valid if
it can be parsed as an ICC Profile
it is an Input, Display, Output, or color space ICC profile. (Abstract, DeviceLink, and NamedColor ICC Profiles must not be used).
If the profile is not valid, all CSS colors
which reference this profile
are
invalid color
s.
To
fetch an external color profile
, given a
@color-profile
rule
rule
fetch a style resource
given
rule
’s URL,
with ruleOrDeclaration being
rule
destination "color-profile",
CORS mode "cors",
and processResponse being the following steps given
response
|/res| and null, failure or
a byte stream
byteStream
If
byteStream
is a byte stream,
apply the color profile as parsed from |byteStream.
Note:
The Internet Media Type ("MIME type")
for ICC profiles is
application/vnd.iccprofile
Name:
rendering-intent
For:
@color-profile
Value:
relative-colorimetric
absolute-colorimetric
perceptual
saturation
Initial:
relative-colorimetric
Color profiles
contain “rendering intents”,
which define how to
gamut-map
their color to smaller gamuts than they’re defined over.
Often a profile will contain only a single intent,
but when there are multiple,
the
rendering-intent
descriptor chooses one of them to use.
The four possible rendering intents are
[ICC]
relative-colorimetric
Media-relative colorimetric is required to leave source colors that fall
inside the destination medium gamut unchanged relative to the respective
media white points. Source colors that are out of the destination medium
gamut are mapped to colors on the gamut boundary using a variety of
different methods.
The media-relative colorimetric rendering intent is
often used with black point compensation, where the source medium black point
is mapped to the destination medium black point as well.
This method must map the source white point to the destination white point. If
black point compensation is in use, the source black point must also be
mapped to the destination black point. Adaptation algorithms should be used
to adjust for the change in white point. Relative relationships of colors
inside both source and destination gamuts should be preserved. Relative
relationships of colors outside the destination gamut may be changed.
absolute-colorimetric
ICC-absolute colorimetric is required to leave source colors that fall
inside the destination medium gamut unchanged relative to the adopted
white (a perfect reflecting diffuser). Source colors that are out of the
destination medium gamut are mapped to colors on the gamut boundary using a
variety of different methods. This method produces the most accurate
color matching of in-gamut colors, but will result in highlight clipping
if the destination medium white point is lower than the source medium
white point. For this reason it is recommended for use only in applications
that need exact color matching and where highlight clipping is not a concern.
This method MUST disable white point matching and black point matching when
converting colors. In general, this option is not recommended except
for testing purposes.
perceptual
This method is often the preferred choice for images, especially when there are
substantial differences between the source and destination (such as a screen display
image reproduced on a reflection print). It takes the colors of the source image
and re-optimizes the appearance for the destination medium using proprietary
methods. This re-optimization may result in colors within both the source
and destination gamuts being changed, although perceptual transforms are
supposed to maintain the basic artistic intent of the original in the
reproduction. They will not attempt to correct errors in the source image.
Note:
With v2 ICC profiles there is no specified perceptual reference medium,
which can cause interoperability problems. When v2 ICC profiles are used it can
be safer to use the media-relative colorimetric rendering intent with black
point compensation, instead of the perceptual rendering intent, unless the
specific source and destination profiles to be used have been checked to ensure
the combination produces the desired result.
This method should maintain relative color values among the pixels as they
are mapped to the target device gamut. This method may change pixel values
that were originally within the target device gamut, in order to avoid
hue shifts and discontinuities and to preserve as much as possible the
overall appearance of the scene.
saturation
This option was created to preserve the relative saturation (chroma) of the original,
and to keep solid colors pure. However, it experienced interoperability problems like
the perceptual intent, and as solid color preservation is not amenable to a reference
medium solution using v4 profiles does not solve the problem. Use of this rendering intent
is not recommended unless the specific source and destination profiles to be used have
been checked to ensure the combination produces the desired result.
This option should preserve the relative saturation (chroma) values of the original
pixels. Out of gamut colors should be converted to colors that have the same saturation
but fall just inside the gamut.
Name:
components
For:
@color-profile
Value:
Initial:
n/a
Color profiles can define color spaces which contain a varying number of components.
For example, a Cyan, Magenta, Yellow and Black (CMYK) profile
has four components
named c, m, y and k
While a four-component additive screen profile
might use four components
named r, g, y and b.
The value of this descriptor is a comma-separated list of
tokens.
Each
> names a component,
in the order in which they are used in the color profile,
while the total number of tokens defines the number of components.
This descriptor declares that there are four components named cyan, magenta, yellow and black:
components: cyan, magenta, yellow, black
while this descriptor opts for terser names:
components: c,m,y,k
This descriptor declares that there are seven components
named cyan, magenta, yellow, black, orange, green and violet:
components: cyan, magenta, yellow, black, orange, green, violet
If a component is an
ASCII case-insensitive
match for
none
the descriptor is invalid,
because that would clash with the token for missing values.
If the name chosen for a component
clashes with a CSS numeric constant
as defined in
CSS Values 4
§ 10.7.1 Numeric Constants: e, pi
the component is still valid,
but inside
calc()
the component
will be shadowed by the numeric constant
leading to unexpected results.
This descriptor unwisely calls a component
pi
leading to unexpected results in Relative Color Syntax.
@color-profile --unwise {
src: url(https://example.com/unwise);
components: mi, pi, ni;
--base: color(--unwise 35% 20% 8%);
--accent: color(from var(--base) mi calc(pi * 2) calc(ni / 2));
Here, the component values of --accent are 35%,
3.14159265358979 * 2 = 6.28318530717959,
4%.
5.4.
CSS and Print: Using Calibrated CMYK and Other Printed Color Spaces
The
@color-profile
at-rule is not restricted to RGB color spaces.
While screens typically display colors directly in RGB,
printers often represent colors with CMYK.
Calibrated four color print with Cyan, Magenta, Yellow and Black (CMYK),
or high-fidelity wide gamut printing with additional inks
such as Cyan Magenta Yellow Black Orange Green Violet (CMYKOGV)
can also be done in CSS,
provided you have an ICC profile
corresponding to the combination of
inks, paper, total ink coverage and equipment
you will use.
For example, using offset printing to ISO 12647-2:2004 / Amd 1:2007
using the FOGRA39 characterization data
on 115gsm coated paper
with an ink limit of 300% Total Area Coverage
[FOGRA39]
@color-profile
--fogra39
src
url
'https://example.org/Coated_Fogra39L_VIGC_300.icc'
);
.header
background-color
color
--fogra39
70
20
);
Here the color() function first states the name we have given the profile,
then gives the percentage
of cyan, magenta, yellow, and black.
In this profile, this resolves to the color
lab(63.673303% 51.576902 5.811058)
which is
rgb(93.124, 44.098% 57.491%).
Because the actual color resulting from a given CMYK combination is known,
an on-screen visualization of the printed output (soft-proof) can be made.
Also, procedures that rely on knowing the color
(anti-aliasing, compositing, using the color in a gradient, etc)
can proceed as normal.
A grid of colored squares. There are six columns,
labelled A to F,
and four rows,
labelled 1 to 4.
A color checker, used for ensuring color fidelity
in the print and photographic industries.
Averaged measured Lab values are available for each patch.
The rectangles show the Lab values, converted to sRGB.
The circles, which are barely visible, show the Lab values,
passed through a FOGRA51
[FOGRA51]
ICC profile to convert them to CMYK.
The CMYK values are then passed through the same ICC profile in reverse,
to yield new Lab values. These are then converted to sRGB for display.
The one patch with a more visible circle (third row, first patch)
is because the color is slightly outside the gamut of the
FOGRA51 CMYK space used.
The table below shows, for each patch, the DeltaE 2000 between the original Lab and the Lab value after round-tripping through CMYK. A DeltaE 2000 of 1 or more is just visible.
0.06
0.07
0.03
0.04
0.06
0.17
0.03
0.75
0.05
0.06
0.03
0.02
1.9
0.04
0.06
0.05
0.02
0.05
0.03
0.08
0.03
0.03
0.04
0.80
This example is using offset printing to ISO 12647-2:2004
using the CGATS/SWOP TR005 2007 characterization data
on grade 5 paper
with an ink limit of 300% Total Area Coverage,
and medium gray component replacement (GCR).
@color-profile
--swop5c
src
url
'https://example.org/SWOP2006_Coated5v2.icc'
);
.header
background-color
color
--swop5c
70
20
);
In this profile, this amount of CMYK
(the same percentages as the previous example)
resolves to the color
lab(64.965217% 52.119710 5.406966)
which is
rgb(94.903% 45.248% 59.104%).
Fallback colors can be specified,
for example using media queries,
to be used
if the specified CMYK color is known
to be outside the sRGB gamut.
This example uses the same FOGRA39 setup as before,
but specifies a bright green which is outside the sRGB gamut.
It is, however, inside the display-p3 gamut.
Therefore it is displayed as-is on wide gamut screens
and in print,
and a less intense fallback color is used on sRGB screens.
@media
color-gamut: srgb
.header
background-color
rgb
8.154
60.9704
37.184
);
@media
color-gamut: p3
){
.header
background-color
color
--fogra39
90
90
);
This CMYK color corresponds to lab(56.596645% -58.995875 28.072154)
or lch(56.596645% 65.33421077211648 154.5533771086801).
In sRGB this would be rgb(-60.568% 62.558% 32.390%) which,
as the large negative red component shows,
is out of gamut.
Reducing the chroma until the result is in gamut
gives
lch(56.596645% 51 154.5533771086801)
which is
rgb(8.154% 60.9704% 37.184%)
and this has been manually specified as a fallback color.
For wide gamut screens, the color is inside the display-p3 gamut
(it is display-p3(0.1658 0.6147 0.3533) ).
Colors are not restricted to four inks (CMYK). For example, wide-gamut 7 Color ink sets can be used.
This example uses the beta FOGRA55 dataset
[FOGRA55]
for CMYKOGV seven-color printing.
Four of the inks - black, cyan, magenta, and yellow - are the same as,
and give the same results as, the FOGRA51 set.
The other three inks are:
Orange: CIELAB 65 58 88
Green: CIELAB 60 -75 0
CIELAB 22 47 -56
The measurement condition is M1,
which means that optical brighteners in the paper are accounted for
and the spectrophotometer has no UV-cut filter.
@color-profile
--fogra55beta
src
url
'https://example.org/2020_13.003_FOGRA55beta_CL_Profile.icc'
);
.dark_skin
background-color
color
--fogra55beta
0.183596
0.464444
0.461729
0.612490
0.156903
0.000000
0.000000
);
.light_skin
background-color
color
--fogra55beta
0.070804
0.334971
0.321802
0.215606
0.103107
0.000000
0.000000
);
.blue_sky
background-color
color
--fogra55beta
0.572088
0.229346
0.081708
0.282044
0.000000
0.000000
0.168260
);
.foliage
background-color
color
--fogra55beta
0.314566
0.145687
0.661941
0.582879
0.000000
0.234362
0.000000
);
.blue_flower
background-color
color
--fogra55beta
0.375515
0.259934
0.034849
0.107161
0.000000
0.000000
0.308200
);
.bluish_green
background-color
color
--fogra55beta
0.397575
0.010047
0.223682
0.031140
0.000000
0.317066
0.000000
);
5.5.
Converting CMYK colors to Lab
Conversion from a calibrated CMYK color space to Lab
is typically done by looking up the Lab values in an ICC profile.
5.6.
Converting Lab colors to CMYK
For print,
Lab colors will need to be converted
to the color space of the printer.
This is typically done by looking up the CMYK values in an ICC profile.
6.
Uncalibrated CMYK Colors: the
device-cmyk()
Function
Sometimes, when a given printer has not been calibrated,
but the output for particular ink combinations is known through experimentation,
or via a printed sample swatchbook,
it is useful to express CMYK colors
in a device-dependent way.
Note:
Because the actual resulting color can be unknown,
CSS processors might attempt to approximate it.
This approximation is likely to be visually very far from the actual printed result.
The
device-cmyk()
function allows authors to specify a color in this way:
device-cmyk()
= device-cmyk(
#{4}
= device-cmyk(
{4}
[ / [
none
] ]
none
The arguments of the
device-cmyk()
function specify the cyan, magenta, yellow, and black components, in order,
as a number between 0 and 1
or, in the modern syntax, as a percentage between 0% and 100%.
These two usages are equivalent, and map to each other linearly.
Values less than 0 or 0%, or greater than 1 or 100%,
are not invalid;
instead, they are clamped to 0/0% or 1/100% at computed-value time.
In the modern syntax, the fifth argument specifies the alpha component of the color.
It’s interpreted identically to the fourth argument of the
rgb()
function.
If omitted, it defaults to
100%
For
historical reasons
device-cmyk()
also support a
legacy color syntax
Typically, print-based applications will actually store the used colors as CMYK,
and send them to the printer in that form.
However, such colors do not have a colorimetric interpretation,
and thus cannot be used in gradients, compositing, blending and so on.
As such, Device CMYK colors must be converted to an equivalent color.
This is not trivial, like the conversion from HSL or HWB to RGB;
the precise conversion depends on the precise characteristics of the output device.
If the user, author, or user-agent stylesheet
has an
@color-profile
definition for device-cmyk,
and the resource specified by the src descriptor can be retrieved,
and the resource is a valid CMYK ICC profile,
and the user agent can process ICC profiles,
the computed value of the
device-cmyk()
function
must be the Lab value of the CMYK color.
Otherwise,
the computed value of the
device-cmyk()
function must be
the sRGB value of the CMYK color,
as converted with the following naive conversion algorithm.
For example, with no
@color-profile
the following colors are equivalent, using the naive conversion.
color
device-cmyk
81
81
30
);
color
rgb
178
34
34
);
color
firebrick
With the
@color-profile
specified as in the example stylesheet,
the following colors are equivalent, using colorimetric conversion.
color
device-cmyk
81
81
30
);
color
lab
45.060
45.477
35.459
color:
rgb
70.690
26.851
19.724
);
The naive conversion is necessarily approximate,
since it has no knowledge of the colorimetry of the inks,
the dot gain,
the colorimetry of the RGB space, and so on.
A grid of colored squares. There are six columns,
labelled A to F,
and four rows,
labelled 1 to 4.
A color checker, used for ensuring color fidelity
in the print and photographic industries.
Averaged measured Lab values are available for each patch.
The rectangles show the Lab values, converted to sRGB.
The circles show the Lab values, passed through an ICC profile to convert them to CMYK.
The CMYK value are then naively converted to sRGB.
The table below shows, for each patch, the DeltaE 2000 between the original Lab and the Lab value after round-tripping through CMYK. A DeltaE 2000 of 1 or more is just visible, while 5 or more is just a different color altogether.
11.33
9.36
5.66
7.52
12.39
21.58
6.40
8.79
11.77
17.16
11.91
3.97
12.1
17.00
3.38
1.94
18.08
14.97
1.89
6.56
7.85
8.76
9.82
10.29
6.1.
Naively Converting Between Uncalibrated CMYK and sRGB-Based Color
To
naively convert from CMYK to RGBA
red
min
cyan
black
black
green
min
magenta
black
black
blue
min
yellow
black
black
Alpha is same as for input color.
To
naively convert from RGBA to CMYK
black
max
red
green
blue
cyan
red
black
black
),
or
if
black is
magenta
green
black
black
),
or
if
black is
yellow
blue
black
black
),
or
if
black is
alpha is the same as the input color
7.
Reacting to the used color-scheme: the
light-dark()
Function
System colors have the ability to react to the current used
color-scheme
value.
The
light-dark()
function exposes the same capability to authors.
There are two forms of this function: one takes a pair of colors
while the other takes a pair of images.
Attempting to use one image and one color
will result in a parse-time error.
light-dark()
= light-dark(
= light-dark( [
none ]
none ] )
For the color form, this function computes to the computed value of the first color,
if the
used color scheme
is
light
or unknown,
or to the computed value of the second color,
if the
used color scheme
is
dark
For the image form, this function returns the first image,
if the
used color scheme
is
light
or unknown,
or the second image,
if the
used color scheme
is
dark
The none keyword
produces a fully transparent image
with no natural size.
It is equivalent to a single-stop gradient whose stop color is
transparent
':
linear-gradient
transparent
For example, to maintain a legible contrast on links,
for light mode and dark mode:
link
color
light-dark
blue
#81D9FE
);
background-color
light-dark
white
black
);
The traditional
blue link text
is legible on a
white background
(WCAG contrast 8.59:1, AAA pass)
but would not be legible on a
black background
(WCAG contrast 2.44:1, AA fail).
Instead, a lighter blue
#81D9FE
is used in dark mode.
(WCAG contrast 13.28:1, AAA pass).
Legible link text
Illegible link text
Legible link text
For example, to provide easily visible list bullets
for light mode and dark mode:
ul.fancy
list-style-image
light-dark
url
"icons/deep-maroon-ball.png"
),
url
"icons/pale-yellow-star.png"
);
For example, a raster image is used in light mode,
while in dark mode we want a fully-transparent image.
background-image
light-dark
url
my-light-image.png
),
none
);
Tests
light-dark-basic.html
(live test)
(source)
light-dark-currentcolor.html
(live test)
(source)
light-dark-image.html
(live test)
(source)
light-dark-inheritance.html
(live test)
(source)
light-dark-currentcolor-in-color.html
(live test)
(source)
highlight-styling-004.html
(live test)
(source)
8.
Dynamically specifying a text color with adequate contrast: the
contrast-color()
Function
When colors are created dynamically, it can often be a challenge to specify a text color that
provides adequate contrast with them when used as a background color.
The
contrast-color()
function automatically provides a color with guaranteed color contrast
when used as a text color on a solid background of the specified color.
Note:
Legibility is a complex topic, and sufficient color contrast is only one piece of the puzzle.
Having a color pair with sufficient contrast does not guarantee that the text will be legible,
as that also depends on a variety of factors, such as the font, the size of the text,
the surrounding colors, etc.
contrast-color()
= contrast-color(
contrast-color()
resolves to either
white
or
black
whichever produces
maximum
color contrast for text
when the input color is used as a solid background.
If both
white
and
black
produce the same contrast,
it resolves to
white
The precise color contrast algorithm for determining whether to output a light or dark color
is UA-defined at this level.
Note:
Future versions of this specification are expected to introduce more control over
both the contrast algorithm(s) used, the use cases, as well as the returned color.
UAs are advised to not simply use the WCAG 2.1
section 1.4.3 Contrast (Minimum)
contrast ratio algorithm to decide between light and dark colors, as it has
several known issues
However, colors returned by this function should still meet the WCAG 2.1
section 1.4.3 Contrast (Minimum)
for AA large text, as many authors need to meet legal requirements that mandate this.
Tests
contrast-color-001.html
(live test)
(source)
contrast-color-currentcolor-inherited.html
(live test)
(source)
contrast-color-interpolation.html
(live test)
(source)
color-computed-contrast-color-function.html
(live test)
(source)
color-invalid-contrast-color-function.html
(live test)
(source)
color-valid-contrast-color-function.html
(live test)
(source)
contrast-color-function-calc-container.html
(live test)
(source)
9.
Color Interpolation
9.1.
Color Space for Interpolation
The
as defined in
CSS Color 4
§ 12.1 Color Space for Interpolation
is extended to allow use of the
custom color spaces
srgb
srgb-linear
display-p3
display-p3-linear
a98-rgb
prophoto-rgb
rec2020
lab
oklab
hsl
hwb
lch
oklch
= [ shorter
longer
increasing
decreasing ] hue
= in [
The
must have been declared
in a valid
@color-profile
rule,
otherwise the
is invalid.
10.
Resolving
Values
10.1.
Resolving
color-mix()
Values
If all
parameters resolve
to the corresponding colors in their respective color spaces,
the computed value is the mixed color,
in the specified mixing color space,
resolved according to
CSS Color 4
§ 14. Resolving
Otherwise (if
currentColor
was used in the function),
the computed value is the
color-mix()
function
with each
parameter resolved according to
CSS Color 4
§ 14. Resolving
thus preserving inheritance into child elements.
Tests
color-mix-currentcolor-001.html
(live test)
(source)
color-mix-currentcolor-002.html
(live test)
(source)
color-mix-currentcolor-003.html
(live test)
(source)
color-mix-currentcolor-nested-for-color-property.html
(live test)
(source)
nested-color-mix-with-currentcolor.html
(live test)
(source)
10.2.
Resolving Relative Color Syntax Values
If all
parameters resolve
to the corresponding colors in their respective color spaces,
the computed value is the absolute
value,
in the specified RCS color space,
resolved according to
CSS Color 4
§ 14. Resolving
US