{
"TIFF": {
"href": "https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml",
"title": "TIFF Revision 6.0",
"date": "3 June 1992"
},
"Sharma": {
"href": "https://www.hajim.rochester.edu/ece/sites/gsharma/ciede2000/",
"title": "The CIEDE2000 Color-Difference Formula: Implementation Notes, Supplementary Test Data, and Mathematical Observations",
"authors": ["G. Sharma", "W. Wu", "E. N. Dalal"],
"journal": "Color Research and Application, vol. 30. No. 1, pp. 21-30",
"date": "February 2005"
},
"Oklab": {
"href": "https://bottosson.github.io/posts/oklab/",
"title": "A perceptual color space for image processing",
"authors": ["Björn Ottosson"],
"date": "December 2020"
},
"HSL": {
"title": "Color spaces for computer graphics",
"authors": ["George H. Joblove, Donald Greenberg"],
"journal": "ACM SIGGRAPH Computer Graphics Volume 12 Issue 3 pp 20–25",
"date": "August 1978",
"href": "https://doi.org/10.1145/965139.807362"
},
"HWB": {
"title": "HWB — A More Intuitive Hue-Based Color Model",
"authors": ["Alvy Ray Smith"],
"journal": "Journal of Graphics, GPU and Game tools. 1 (1): 3–17 ",
"date": "1996",
"href": "http://alvyray.com/Papers/CG/HWB_JGTv208.pdf"
},
"Display-P3": {
"title": "Display P3",
"authors": ["Apple, Inc"],
"date": "2022-02",
"publisher": "ICC",
"href": "https://www.color.org/chardata/rgb/DisplayP3.xalter"
},
"Wolfe": {
"title": "Design and Optimization of the ProPhoto RGB Color Encodings",
"authors": ["Geoff Wolfe"],
"date": "2011-12-21",
"href": "http://www.realtimerendering.com/blog/2011-color-and-imaging-conference-part-vi-special-session/"
},
"ROMM": {
"title": "ISO 22028-2:2013 Photography and graphic technology — Extended colour encodings for digital image storage, manipulation and interchange — Part 2: Reference output medium metric RGB colour image encoding (ROMM RGB)",
"date": "2013-04",
"publisher": "ISO",
"href": "https://www.iso.org/standard/56591.html"
},
"ROMM-RGB": {
"title": "ROMM RGB",
"publisher": "ICC",
"href": "https://www.color.org/chardata/rgb/rommrgb.xalter"
}
}
Introduction
This section is not normative.
This module describes CSS properties which
allow authors to specify
the foreground color and opacity of the text content of an element.
This module also describes in detail the CSS <> value type.
It not only defines the color-related properties and values that
already exist in CSS1, CSS2,
and CSS Color 3,
but also defines new properties and values.
In particular, it allows specifying colors
in other [=color spaces=] than sRGB;
previously, the more saturated colors outside the sRGB gamut
could not be used in CSS
even if the display device supported them.
A draft implementation report is available.
Value Definitions
This specification follows the CSS property definition conventions from [[!CSS2]]
using the value definition syntax from [[!CSS-VALUES-3]].
Value types not defined in this specification are defined in CSS Values & Units [[!CSS-VALUES-3]].
Combination with other CSS modules may expand the definitions of these value types.
In addition to the property-specific values listed in their definitions,
all properties defined in this specification
also accept the CSS-wide keywords as their property value.
For readability they have not been repeated explicitly.
Color Terminology
A color is a definition (numeric or textual)
of the human visual perception of a light
or a physical object illuminated with light.
The objective study of human color perception is termed
colorimetry.
The color of a physical object
depends on how much light it reflects
at each visible wavelength,
plus the actual color of the light illuminating it
(again, the amount of light at each wavelength).
It is measured by a spectrophotometer .
The color of something that emits light
(including colors on a computer screen)
depends on how much light it emits
at each visible wavelength.
It is measured by a spectroradiometer.
If two objects have different
spectra,
but still produce the same physical sensation,
we say they have the same color.
We can calculate whether two colors are the same
by converting the spectra to CIE XYZ
(three numbers).
For example a green leaf, a photograph of that leaf
displayed on a computer screen, and a print of that photograph,
are all producing a green sensation by different means.
If the screen and the printer are [=calibrated=],
the green in the leaf, and the photo, and the print will look the same.
A color space is an organization of colors
with respect to an underlying
colorimetric
model,
such that there is a clear, objectively-measurable meaning
for any color in that color space.
This also means that the same color can be expressed in multiple color spaces,
or transformed from one color space to another,
while still looking the same.
A leaf is measured
with a spectrophotometer
and found to have the color
lch(51.2345% 21.2 130)
which is
lab(51.2345% -13.6271 16.2401).
This same color could be expressed in various color spaces:
An additive color space
means that the coordinate system is linear in light intensity.
The CIE
XYZ color space is an additive color space.
The Y component of XYZ is the
luminance, the light intensity per unit area,
or 'how bright it is'. Luminance is measured in candelas per square meter.
cd/m², also called nits.
In an additive color space, calculations can be done
to accurately predict color mixing. Most RGB spaces
are not additive, because the components are
gamma encoded. Undoing this gamma encoding
produces linear-light values.
For example, if a light fixture contains two identical colored lights,
and only one is switched on,
and the color is measured to be
color(xyz 0.13 0.12 0.04),
then the color when both are switched on will be exactly twice that,
color(xyz 0.26 0.24 0.08).
If we have two differently colored spotlights shining on a stage,
and one has the measured value
color(xyz 0.15 0.24 0.17)
while the other is
color(xyz 0.11 0.06 0.06)
then we can accurately predict that if the colored beams are made to overlap,
the color of the mixture will be the sum
of the XYZ component values, or
color(xyz 0.26 0.30 0.23).
A chromaticity is a color measurement
where the lightness component has been factored out.
From the identical lights example above,
the u',v' chromaticity with one light is
(0.2537, 0.5268)
and the chromaticity is the same with both lights
(they are the same color, it is just brighter).
Chromaticities are additive,
so they accurately predict
the chromaticity (but not the resulting lightness)
of a mixture.
Being two-dimensional, chromaticity is easily represented
on a chromaticity diagram
to predict the chromaticity of a color mixture.
Any two colors can be mixed, and the resulting colors
will lie on the line joining them on the diagram.
Three colors form a plane, and the resulting colors
will lie in the triangle they form on the diagram.
Thus, once linearized, RGB color spaces are additive,
and their gamut is defined
by the chromaticities of the red, green and blue primaries,
plus the chromaticity of the white point
(the color formed by all three primaries at full intensity).
Most color spaces use one of a few
daylight-simulating [=white points=],
which are named by the color temperature
of the corresponding black-body radiator.
For example, [=D65=] is a daylight whitepoint
corresponding to a correlated color temperature
of 6500 Kelvin
(actually 6504,
because the value of Plank's constant has changed
since the color was originally defined).
To avoid cumulative round-trip errors,
it is important that the identical chromaticity values
are used consistently,
at all places in a calculation. Thus,
for maximum compatibility,
for this specification,
the following two standard
daylight-simulating [=white points=] are defined:
Name
x
y
CCT
D50
0.345700
0.358500
5003K
D65
0.312700
0.329000
6504K
When the measured physical characteristics
(such as the chromaticities of the primary colors it uses,
or the colors produced in response to a given set of inputs)
of a [=color space=] or a color-producing device are known,
it is said to be characterized.
If in addition adjustments have been made so that a device meets calibration targets
such as white point, neutrality of greys, predictability and consistency of tone response,
then it is said to be calibrated.
Real physical devices cannot yet produce every possible color that the human eye can see.
The range of colors that a given device can produce is termed the gamut
(not to be confused with gamma).
Devices with a limited gamut cannot produce very saturated colors,
like those found in a rainbow.
The gamuts of different [=color space=]s may be compared
by looking at the volume (in cubic Lab units) of colors that can be expressed.
The following table examines the predefined color spaces available in CSS.
color space
Volume (million Lab units)
sRGB
0.820
display-p3
1.233
a98-rgb
1.310
prophoto-rgb
2.896
rec2020
2.042
A color in CSS is either an invalid color,
as described below for each syntactic form,
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.
Each [=valid color=] is either in-gamut
for a particular output device (screen, or printer)
or it is [=out of gamut=].
For example, given a screen which covers 100% of the display-p3 color space,
but no more, the following color is out of gamut:
color(prophoto-rgb 0.88 0.45 0.10)
because, expressed in display-p3,
one or more coordinates are either greater that 1.0 or less than 0.0:
color(display-p3 1.0844 0.43 0.1)
This color is valid,
and could, for example, be used as a gradient stop,
but would need to be CSS gamut mapped for display,
producing a similar-looking but lower chroma (less saturated) color.
Name: color
Value: <>
Initial: CanvasText
Applies to: all elements and text
Inherited: yes
Percentages: N/A
Computed value: computed color, see resolving color values
Animation type: by computed value type
color-001.html
color-002.html
color-003.html
inheritance.html
animation/color-interpolation.html
color-initial-canvastext.html
parsing/color-valid.html
parsing/color-invalid.html
This property specifies the primary foreground color of the element.
This is used as the fill color of its text content,
and in addition specifies the [=used value=]
that ''color/currentcolor'' resolves to,
which allows indirect references to this foreground color
and affects the initial values of various other color properties
such as 'border-color' and 'text-emphasis-color'.
<>
Sets the primary foreground color to the specified <>.
The <> type provides multiple ways to syntactically specify a given color.
For example, the following declarations all specify the sRGB color “lime”:
em { color: lime; } /* color keyword */
em { color: rgb(0 255 0); } /* RGB range 0-255 */
em { color: rgb(0% 100% 0%); } /* RGB range 0%-100% */
em { color: color(sRGB 0 1 0); } /* sRGB range 0.0-1.0 */
When applied to text, this property, including its alpha component,
has no effect on “color glyphs” (such as the emoji in some fonts),
which are colored by a built-in palette.
However, some colored fonts are able to refer to a contextual “foreground color”,
such as by palette entry ''0xFFFF'' in the COLR table of OpenType,
or by the ''context-fill'' value in SVG-in-OpenType.
In such cases, the foreground color is set by this property,
identical to how it sets the ''/currentcolor'' value.
Transparency: the 'opacity' property
Opacity can be thought of as a postprocessing operation.
Conceptually, after the element (including its descendants) is rendered into an RGBA offscreen image,
the opacity setting specifies how to blend the offscreen rendering into
the current composite rendering.
See simple alpha compositing for details.
Name: opacity
Value: <>
Initial: 1
Applies to: all elements
Inherited: no
Percentages: map to the range [0,1]
Computed value: specified number, clamped to the range [0,1]
Animation type: by computed value type
The opacity to be applied to the element.
The resulting opacity is applied to the entire element,
rather than a particular color.
Opacity values outside the range [0,1] are not invalid,
and are preserved in specified values,
but are clamped to the range [0, 1]
in computed values.
inline-opacity-float-child.html
Opacity in CSS is represented using the <> syntax,
for example in the 'opacity' property.
<> = <> | <>
Represented as a <>, the useful range of the value is ''0''
(representing full transparency)
to ''1''
(representing full opacity).
It can also be written as a <>,
which [=computed value|computes to=]
the equivalent <>
(''0%'' to ''0'', ''100%'' to ''1'').
The 'opacity' property applies the specified opacity to the element as a whole,
including its contents,
rather than applying it to each descendant individually.
This means that, for example,
an opaque child occluding part of the element's background will continue to do so even when 'opacity' is less than 1,
but the element and child as a whole will show the underlying page through themselves.
It also means that the glyphs
corresponding to all characters in the element
are treated as a whole;
any overlapping portions do not increase the opacity.
opacity-overlapping-letters.html
If separate opacity for each glyph is desired,
it can be achieved by using a color value
which includes alpha,
rather than setting the 'opacity' property.
If a box has 'opacity' less than 1,
it forms a stacking context for its children.
(This prevents its contents from interleaving in the z-axis
with content outside it.)
body-opacity-0-to-1-stacking-context.html
Furthermore, if the 'z-index' property applies to the box,
the ''z-index/auto'' value is treated as ''0'' for the element;
it is otherwise painted on the same layer within its parent stacking context
as positioned elements with stack level 0
(as if it were a positioned element with ''z-index:0'').
See section 9.9
and Appendix E of [[!CSS2]]
for more information on stacking contexts.
These rules about z-order do not apply to SVG elements,
since SVG has its own rendering model ([[!SVG11]], Chapter 3).
Color Space of Tagged Images
An tagged image is an image
that is explicitly assigned a color profile,
as defined by the image format.
This is usually done by including an
International Color Consortium (ICC) profile [[!ICC]].
For example JPEG [[JPEG]], PNG [[PNG]] and TIFF [[TIFF]]
all specify a means to embed an ICC profile.
Image formats may also use other, equivalent methods, often for brevity.
For example, PNG specifies a means (the
sRGB chunk)
to explicitly tag an image as being in the sRGB color space,
without including the sRGB ICC profile.
Similarly, PNG specifies a compact means
(the cICP chunk)
to explicitly tag an image as being one of various SDR or HDR color spaces,
such as Display P3 or BT.2100 HLG,
without including an ICC profile.
Tagged RGB images,
and tagged images using a transformation of RGB such as YCbCr,
if the color profile or other identifying information is valid,
must be treated as being in the specified color space.
tagged-images-001.html
tagged-images-002.html
tagged-images-003.html
tagged-images-004.html
cicp-chunk.html
apng/fDAT-inherits-cICP.html
For example, when a browser running on a system with a Display P3 monitor
displays an JPEG image
tagged as being in the ITU Rec BT.2020 [[!Rec.2020]]
color space, it must convert the colors
from ITU Rec BT.2020 to Display P3
so that they display correctly.
It must not treat the ITU Rec BT.2020 values
as if they were Display P3 values, which would produce incorrect colors.
If the color profile or other identifying information is invalid, the image is treated as described for [=untagged images=].
Color Spaces of Untagged Colors
For compatibility, colors specified in HTML,
and [=untagged images=] must be treated
as being in the sRGB color space ([[!SRGB]])
unless otherwise specified.
untagged-images-001.html
An untagged image is an image that is not explicitly assigned a color profile, as defined by the image format.
This rule does not apply to untagged videos, since
untagged video should be presumed to be in an ITU-defined color space.
* At below 720p, it is Recommendation ITU-R BT.601 [[!ITU-R-BT.601]]
* At 720p, it is SMPTE ST 296 (same colorimetry as 709) [[!SMPTE296]]
* At 1080p, it is Recommendation ITU-R BT.709 [[!ITU-R-BT.709]]
* At 4k (UHDTV) and above, it is ITU-R BT.2020 [[!Rec.2020]] for SDR video
Representing Colors: the <> type
Colors in CSS are represented as a list of color components,
also sometimes called “channels”,
representing axises in the color space.
Each channel has a minimum and maximum value,
and can take any value between those two.
Additionally, every color is accompanied by
an alpha component,
indicating how transparent it is,
and thus how much of the backdrop one can see through the color.
CSS has several syntaxes for specifying color values:
* the sRGB [=hex color notation=]
which represents the RGB and alpha channels in hexadecimal notation
* the various [=color functions=]
which can represent colors using a variety of color spaces and coordinate systems
* the constant [=named color=] keywords
* the variable <> keywords and ''currentColor'' keyword.
The color functions
use CSS [=functional notation=]
to represent colors in a variety of [=color spaces=]
by specifying their channel coordinates.
Some of these use a cylindrical polar color model,
specifying color by a <> angle,
a central axis representing lightness
(black-to-white),
and a radius representing saturation or chroma
(how far the color is from a neutral grey).
The others use a rectangular orthogonal color model,
specifying color using three
orthogonal component axes.
The [=color functions=] available in Level 4 are
* ''rgb()'' and its ''rgba()'' alias,
which (like the [=hex color notation=]) specify sRGB colors directly
by their red/green/blue/alpha channels.
* ''hsl()'' and its ''hsla()'' alias,
which specify sRGB colors
by hue, saturation, and lightness
using the [[#the-hsl-notation|HSL]] cylindrical coordinate model.
* ''hwb()'',
which specifies an sRGB color
by hue, whiteness, and blackness
using the [[#the-hwb-notation|HWB]] cylindrical coordinate model.
* ''lab()'',
which specifies a CIELAB color
by CIE Lightness and its a- and b-axis hue coordinates
(red/green-ness, and yellow/blue-ness)
using the [[#cie-lab|CIE LAB rectangular coordinate model]].
* ''lch()'' ,
which specifies a CIELAB color
by CIE Lightness, Chroma, and hue
using the [[#cie-lab|CIE LCH cylindrical coordinate model]]
* ''oklab()'',
which specifies an Oklab color
by Oklab Lightness and its a- and b-axis hue coordinates
(red/green-ness, and yellow/blue-ness)
using the [[#ok-lab|Oklab]] rectangular coordinate model.
* ''oklch()'' ,
which specifies an Oklab color
by Oklab Lightness, Chroma, and hue
using the [[#ok-lab|Oklch]] cylindrical coordinate model.
* ''color()'',
which allows specifying colors in a variety of color spaces
including
[[#predefined-sRGB|sRGB]],
[[#predefined-sRGB-linear|Linear-light sRGB]],
[[#predefined-display-p3|Display P3]],
[[#predefined-a98-rgb|A98 RGB]],
[[#predefined-prophoto-rgb|ProPhoto RGB]],
[[#predefined-rec2020|ITU-R BT.2020-2]],
and
[[#predefined-xyz|CIE XYZ]].
For easy reference in other specifications,
opaque black is defined as the color ''rgb(0 0 0 / 100%)'';
transparent black is the same color,
but fully transparent--
i.e. ''rgb(0 0 0 / 0%)''.
parsing/color-computed-named-color.html
parsing/color-computed.html
parsing/color-valid.html
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)
* a <> (which depends on the color mode)
The <>, <>, <>, <>, and <> [=color functions=]
are [=cylindrical polar color=] representations using a <> angle;
the other [=color functions=] use [=rectangular orthogonal color=] representations.
Modern (Space-separated) Color Function Syntax
All of the [=absolute color=] functional forms
first defined in this specification
use the modern color syntax,
meaning:
* color components are separated by whitespace
* the optional alpha term is separated by a solidus ("/")
* minimum required precision when serializing is defined,
and may be greater than 8 bits per component
* the ''none'' value is allowed, to represent [=missing components=]
* components using <> and <> may be freely mixed
The following represents a saturated sRGB red that is 50% opaque:
rgb(100% 0% 0% / 50%)
Legacy (Comma-separated) Color Function Syntax
For Web compatibility,
the syntactic forms
of ''rgb()'', ''rgba()'', ''hsl()'', and ''hsla()'',
(those defined in earlier specifications)
also support a
legacy color syntax
which has the following differences:
* color components are separated by commas
(optionally preceded and/or followed by whitespace)
* non-opaque forms use a separate notation
(for example ''hsla()'' rather than ''hsl()'')
and the alpha term is separated by commas
(optionally preceded and/or followed by whitespace)
* minimum required precision is lower, 8 bits per component
* the ''none'' value is not allowed
* color components must be specified using either all-<>
or all-<>, they can not be mixed.
The following represents a saturated sRGB red that is 50% opaque:
rgba(100%, 0%, 0%, 0.5)
For the [=color functions=] introduced
in this or subsequent levels,
where there is no Web compatibility issue,
the legacy color syntax is invalid.
Representing Transparency in Colors: the <> syntax
<alpha-value> = <> | <>
Unless otherwise specified,
an <> component of a color defaults to ''100%'' when omitted.
Values outside the range [0,1] are not invalid,
but are clamped to that range at parsed-value time.
Representing Cylindrical-coordinate Hues: the <> syntax
Hue is represented as an angle of the color circle
(the rainbow, twisted around into a circle, and with purple added between violet and red).
<hue> = <> | <>
Because this value is so often given in degrees,
the argument can also be given as a number,
which is interpreted as a number of degrees
and is the [=canonical unit=].
This number is normalized
to the range [0,360).
Note: The angles and spacing
corresponding to particular hues
depend on the color space.
For example, in HSL and HWB, which use the sRGB color space,
sRGB green is 120 degrees.
In LCH, sRGB green is 134.39 degrees,
display-p3 green is 136.01 degrees,
a98-rgb green is 145.97 degrees
and prophoto-rgb green is 141.04 degrees
(because these are all different shades of green).
<> components are the most common components to become [=powerless=];
any achromatic color will have a [=powerless=] hue component.
“Missing” Color Components and the ''none'' Keyword
In certain cases,
a color can have one or more
missing color components.
In this specification,
this happens automatically due to [[#hue-interpolation|hue-based interpolation]]
for some colors (such as ''white'');
other specifications can define additional situations
in which components are automatically missing.
It can also be specified explicitly,
by providing the keyword none
for a component in a color function.
All color functions
(with the exception of those using the legacy color syntax)
allow any of their components to be specified as ''none''.
This should be done with care,
and only when the particular effect of doing so is desired.
parsing/color-computed-color-function.html
parsing/color-computed-hsl.html
parsing/color-computed-hwb.html
parsing/color-computed-lab.html
parsing/color-computed-relative-color.html
parsing/color-computed-rgb.html
parsing/color-invalid-hsl.html
parsing/color-invalid-rgb.html
parsing/color-valid-color-function.html
parsing/color-valid-color-mix-function.html
parsing/color-valid-hsl.html
parsing/color-valid-hwb.html
parsing/color-valid-lab.html
parsing/color-valid-relative-color.html
parsing/color-valid-rgb.html
For handling of [=missing components=] in
situations which combine two colors,
such as color interpolation,
see [[#interpolation-missing]].
For all other purposes, a [=missing component=] behaves as a zero value,
in the appropriate unit for that component: ''0'', ''0%'', or ''0deg''.
This includes rendering the color directly,
converting it to another color space,
performing computations on the color component values,
etc.
If a color with a [=missing component=] is serialized
or otherwise presented directly to an author,
then for legacy color syntax
it represents that component as a zero value;
otherwise,
it represents that component as being the ''none'' keyword.
A missing hue is common when interpolating in cylindrical color spaces.
For example, using the ''color-mix()'' function specified in [[CSS-COLOR-5]]
one could write ''color-mix(in hsl, white 30%, green 70%)''.
Since ''white'' is an achromatic color,
it has a [=missing=] hue when expressed in ''hsl()''
(effectively ''hsl(none 0% 100%))'',
since any hue will produce the same color)
which means that the color-mix function
will treat it as having the same hue as ''green''
(effectively ''hsl(120deg 0% 100%)''),
and then interpolate based on those components.
The result will be a color that truly looks like a blend of green and white,
rather than perhaps looking reddish
(if ''white''s hue was defaulted to ''0deg'').
Explicitly specifying missing components can be useful
to achieve an effect where you only want
to interpolate certain components of a color.
For example, to animate a color to "grayscale", no matter what the color is,
one can interpolate it with ''oklch(none 0 none)''.
This will take the hue and lightness from the starting color,
but animate its chroma down to 0,
rendering it into an equal-lightness gray
with a steady hue across the whole animation.
Doing this manually would require
matching the hue and lightness of the starting color explicitly.
“Powerless” Color Components
Individual color syntaxes can specify that,
in some cases,
a given component of their syntax becomes a
powerless color component.
This indicates that the value of the component doesn't affect the rendered color;
any value you give it will result in the same color displayed in the screen.
For example, in ''hsl()'', the hue component is [=powerless=]
when the saturation component is ''0%'';
a ''0%'' saturation indicates a grayscale color,
which has no hue at all,
so ''0deg'' and ''180deg'', or any other angle,
will give the exact same result.
If a [=powerless component=] is manually specified,
it acts as normal;
the fact that it's [=powerless=] has no effect.
However, if a color is automatically produced by color space conversion,
then any [=powerless components=] in the result must instead be set to [=missing=],
instead of whatever value was produced by the conversion process.
User agents may treat a component as [=powerless=]
if the color is "sufficiently close" to the precise conditions specified.
For example, a gray color converted into ''lch()'' may,
due to numerical errors,
have an extremely small chroma rather than precisely ''0%'';
this can, at the user agent's discretion,
still treat the hue component as [=powerless=].
It is intentionally unspecified exactly what "sufficiently close" means for this purpose.
Parsing a <> Value
To parse a CSS <> value,
given a [=string=] |input|,
and an optional context [=/element=] |element|:
1. [=CSS/Parse=] |input| as a <>.
If the result is failure,
return failure;
otherwise, let |color| be the result.
3. Let |used color| be the result of [[#resolving-color-values|resolving]] |color|
to a [=used color=].
If the value of other properties
on the element a <> is on
is required to do the resolution
(such as resolving a ''currentcolor'' or [=system color=]),
use |element| if it was passed,
or the [=initial values=] of the properties if not.
4. Return |used color|.
sRGB Colors
CSS colors in the sRGB color space
are represented by a triplet of values--
red, green, and blue--
identifying a point in the sRGB color space [[!SRGB]].
This is an internationally-recognized, device-independent color space,
and so is useful for specifying colors that will be displayed on a computer screen,
but is also useful for specifying colors on other types of devices, like printers.
CSS also allows the use of non-sRGB [=color space=]s,
as described in [[#predefined]].
CSS provides several methods of directly specifying an sRGB color:
[=hex colors=],
''rgb()''/''rgba()'' [=color functions=],
''hsl()''/''hsla()'' [=color functions=],
''hwb()'' [=color function=],
[=named colors=],
and the ''transparent'' keyword.
The RGB functions: ''rgb()'' and ''rgba()''
The ''rgb()'' and ''rgba()'' functions define an sRGB color
by specifying the r, g and b (red, green, and blue) channels directly.
Their syntax is:
For r, g and b: 0% = 0.0, 100% = 255.0
For alpha: 0% = 0.0, 100% = 1.0
rgb-001.html
rgb-002.html
rgb-003.html
rgb-004.html
rgb-005.html
rgb-006.html
rgb-007.html
rgb-008.html
out-of-gamut-legacy-rgb.html
parsing/color-valid.html
parsing/color-computed-rgb.html
parsing/color-invalid-rgb.html
parsing/color-valid-rgb.html
The first three arguments specify the r, g and b (red, green, and blue)
channels of the color, respectively.
''0%'' represents the minimum value for that color channel in the sRGB gamut,
and ''100%'' represents the maximum value.
The percentage reference range of the color channels comes from the historical fact that
many graphics engines stored the color channels internally as a single byte,
which can hold integers between 0 and 255.
Implementations should honor the precision of the channel as authored or calculated wherever possible.
If this is not possible, the channel should be rounded towards +∞.
The final argument, the <>, specifies the alpha of the color.
If omitted, it defaults to ''100%''.
background-color-rgb-001.html
background-color-rgb-002.html
background-color-rgb-003.html
parsing/color-valid.html
Values outside these ranges are not invalid,
but are clamped to the ranges defined here at parsed-value time.
For historical reasons,
''rgb()'' and ''rgba()'' also support a legacy color syntax.
rgba-001.html
rgba-002.html
rgba-003.html
rgba-004.html
rgba-005.html
rgba-006.html
rgba-007.html
rgba-008.html
parsing/color-valid.html
The RGB Hexadecimal Notations: ''#RRGGBB''
The CSS hex color notation
allows an sRGB color to be specified by giving the channels as hexadecimal numbers,
which is similar to how colors are often written directly in computer code.
It's also shorter than writing the same color out in ''rgb()'' notation.
The syntax of a <hex-color> is a <> token whose value consists of 3, 4, 6, or 8 hexadecimal digits.
In other words, a hex color is written as a hash character, "#",
followed by some number of digits 0-9 or letters a-f
(the case of the letters doesn't matter - ''#00ff00'' is identical to ''#00FF00'').
The number of hex digits given determines how to decode the hex notation into an RGB color:
6 digits
The first pair of digits, interpreted as a hexadecimal number,
specifies the red channel of the color,
where ''00'' represents the minimum value
and ''ff'' (255 in decimal) represents the maximum.
The next pair of digits, interpreted in the same way,
specifies the green channel,
and the last pair specifies the blue.
The alpha channel of the color is fully opaque.
In other words, ''#00ff00'' represents the same color as ''rgb(0 255 0)'' (a lime green).
8 digits
The first 6 digits are interpreted identically to the 6-digit notation.
The last pair of digits, interpreted as a hexadecimal number,
specifies the alpha channel of the color,
where ''00'' represents a fully transparent color
and ''ff'' represent a fully opaque color.
In other words, ''#0000ffcc'' represents the same color as ''rgb(0 0 100% / 80%)'' (a slightly-transparent blue).
3 digits
This is a shorter variant of the 6-digit notation.
The first digit, interpreted as a hexadecimal number,
specifies the red channel of the color,
where ''0'' represents the minimum value
and ''f'' represents the maximum.
The next two digits represent the green and blue channels, respectively,
in the same way.
The alpha channel of the color is fully opaque.
This syntax is often explained by saying that it's identical to a 6-digit notation obtained by "duplicating" all of the digits.
For example, the notation ''#123'' specifies the same color as the notation ''#112233''.
This method of specifying a color has lower "resolution" than the 6-digit notation;
there are only 4096 possible colors expressible in the 3-digit hex syntax,
as opposed to approximately 17 million in 6-digit hex syntax.
4 digits
This is a shorter variant of the 8-digit notation,
"expanded" in the same way as the 3-digit notation is.
The first digit, interpreted as a hexadecimal number,
specifies the red channel of the color,
where ''0'' represents the minimum value
and ''f'' represents the maximum.
The next three digits represent the green, blue, and alpha channels, respectively.
In addition to the various numeric syntaxes for <>s,
CSS defines several sets of color keywords that can be used instead—
each with their own advantages or use cases.
Named Colors
CSS defines a large set of named colors,
so that common colors can be written and read more easily.
A <named-color> is written as an <>,
accepted anywhere a <> is.
As usual for CSS-defined <>s,
all of these keywords are ASCII case-insensitive.
The names resolve to colors in sRGB.
16 of CSS's named colors come from the VGA palette originally, and were then adopted into HTML:
aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow.
Most of the rest
come from one version of the X11 color system,
used in Unix-derived systems to specify colors for the console,
and were then adopted into SVG.
Note: these color names are standardized here,
not because they are good,
but because their use and implementation has been widespread for decades
and the standard needs to reflect reality.
Indeed, it is often hard to imagine what each name will look like (hence the list below);
the names are not evenly distributed throughout the sRGB color volume,
the names are not even internally consistent
( ''darkgray'' is lighter than
''gray'', while
''lightpink'' is darker than
''pink''),
and some names
(such as ''indianred'',
which was originally named after a red pigment from India),
have been found to be offensive.
Thus, their use is not encouraged.
(Two special color values, ''transparent'' and ''/currentcolor'',
are specially defined in their own sections.)
The following table defines all of the opaque named colors,
by giving equivalent numeric specifications in the other color syntaxes.
Named
Numeric
Color name
Hex rgb
Decimal
aliceblue
#f0f8ff
240 248 255
antiquewhite
#faebd7
250 235 215
aqua
#00ffff
0 255 255
aquamarine
#7fffd4
127 255 212
azure
#f0ffff
240 255 255
beige
#f5f5dc
245 245 220
bisque
#ffe4c4
255 228 196
black
#000000
0 0 0
blanchedalmond
#ffebcd
255 235 205
blue
#0000ff
0 0 255
blueviolet
#8a2be2
138 43 226
brown
#a52a2a
165 42 42
burlywood
#deb887
222 184 135
cadetblue
#5f9ea0
95 158 160
chartreuse
#7fff00
127 255 0
chocolate
#d2691e
210 105 30
coral
#ff7f50
255 127 80
cornflowerblue
#6495ed
100 149 237
cornsilk
#fff8dc
255 248 220
crimson
#dc143c
220 20 60
cyan
#00ffff
0 255 255
darkblue
#00008b
0 0 139
darkcyan
#008b8b
0 139 139
darkgoldenrod
#b8860b
184 134 11
darkgray
#a9a9a9
169 169 169
darkgreen
#006400
0 100 0
darkgrey
#a9a9a9
169 169 169
darkkhaki
#bdb76b
189 183 107
darkmagenta
#8b008b
139 0 139
darkolivegreen
#556b2f
85 107 47
darkorange
#ff8c00
255 140 0
darkorchid
#9932cc
153 50 204
darkred
#8b0000
139 0 0
darksalmon
#e9967a
233 150 122
darkseagreen
#8fbc8f
143 188 143
darkslateblue
#483d8b
72 61 139
darkslategray
#2f4f4f
47 79 79
darkslategrey
#2f4f4f
47 79 79
darkturquoise
#00ced1
0 206 209
darkviolet
#9400d3
148 0 211
deeppink
#ff1493
255 20 147
deepskyblue
#00bfff
0 191 255
dimgray
#696969
105 105 105
dimgrey
#696969
105 105 105
dodgerblue
#1e90ff
30 144 255
firebrick
#b22222
178 34 34
floralwhite
#fffaf0
255 250 240
forestgreen
#228b22
34 139 34
fuchsia
#ff00ff
255 0 255
gainsboro
#dcdcdc
220 220 220
ghostwhite
#f8f8ff
248 248 255
gold
#ffd700
255 215 0
goldenrod
#daa520
218 165 32
gray
#808080
128 128 128
green
#008000
0 128 0
greenyellow
#adff2f
173 255 47
grey
#808080
128 128 128
honeydew
#f0fff0
240 255 240
hotpink
#ff69b4
255 105 180
indianred
#cd5c5c
205 92 92
indigo
#4b0082
75 0 130
ivory
#fffff0
255 255 240
khaki
#f0e68c
240 230 140
lavender
#e6e6fa
230 230 250
lavenderblush
#fff0f5
255 240 245
lawngreen
#7cfc00
124 252 0
lemonchiffon
#fffacd
255 250 205
lightblue
#add8e6
173 216 230
lightcoral
#f08080
240 128 128
lightcyan
#e0ffff
224 255 255
lightgoldenrodyellow
#fafad2
250 250 210
lightgray
#d3d3d3
211 211 211
lightgreen
#90ee90
144 238 144
lightgrey
#d3d3d3
211 211 211
lightpink
#ffb6c1
255 182 193
lightsalmon
#ffa07a
255 160 122
lightseagreen
#20b2aa
32 178 170
lightskyblue
#87cefa
135 206 250
lightslategray
#778899
119 136 153
lightslategrey
#778899
119 136 153
lightsteelblue
#b0c4de
176 196 222
lightyellow
#ffffe0
255 255 224
lime
#00ff00
0 255 0
limegreen
#32cd32
50 205 50
linen
#faf0e6
250 240 230
magenta
#ff00ff
255 0 255
maroon
#800000
128 0 0
mediumaquamarine
#66cdaa
102 205 170
mediumblue
#0000cd
0 0 205
mediumorchid
#ba55d3
186 85 211
mediumpurple
#9370db
147 112 219
mediumseagreen
#3cb371
60 179 113
mediumslateblue
#7b68ee
123 104 238
mediumspringgreen
#00fa9a
0 250 154
mediumturquoise
#48d1cc
72 209 204
mediumvioletred
#c71585
199 21 133
midnightblue
#191970
25 25 112
mintcream
#f5fffa
245 255 250
mistyrose
#ffe4e1
255 228 225
moccasin
#ffe4b5
255 228 181
navajowhite
#ffdead
255 222 173
navy
#000080
0 0 128
oldlace
#fdf5e6
253 245 230
olive
#808000
128 128 0
olivedrab
#6b8e23
107 142 35
orange
#ffa500
255 165 0
orangered
#ff4500
255 69 0
orchid
#da70d6
218 112 214
palegoldenrod
#eee8aa
238 232 170
palegreen
#98fb98
152 251 152
paleturquoise
#afeeee
175 238 238
palevioletred
#db7093
219 112 147
papayawhip
#ffefd5
255 239 213
peachpuff
#ffdab9
255 218 185
peru
#cd853f
205 133 63
pink
#ffc0cb
255 192 203
plum
#dda0dd
221 160 221
powderblue
#b0e0e6
176 224 230
purple
#800080
128 0 128
rebeccapurple
#663399
102 51 153
red
#ff0000
255 0 0
rosybrown
#bc8f8f
188 143 143
royalblue
#4169e1
65 105 225
saddlebrown
#8b4513
139 69 19
salmon
#fa8072
250 128 114
sandybrown
#f4a460
244 164 96
seagreen
#2e8b57
46 139 87
seashell
#fff5ee
255 245 238
sienna
#a0522d
160 82 45
silver
#c0c0c0
192 192 192
skyblue
#87ceeb
135 206 235
slateblue
#6a5acd
106 90 205
slategray
#708090
112 128 144
slategrey
#708090
112 128 144
snow
#fffafa
255 250 250
springgreen
#00ff7f
0 255 127
steelblue
#4682b4
70 130 180
tan
#d2b48c
210 180 140
teal
#008080
0 128 128
thistle
#d8bfd8
216 191 216
tomato
#ff6347
255 99 71
turquoise
#40e0d0
64 224 208
violet
#ee82ee
238 130 238
wheat
#f5deb3
245 222 179
white
#ffffff
255 255 255
whitesmoke
#f5f5f5
245 245 245
yellow
#ffff00
255 255 0
yellowgreen
#9acd32
154 205 50
Note: this list of colors and their definitions is a superset of the list of named colors defined by SVG 1.1.
For historical reasons, this is also referred to as the X11 color set.
Note: The history of the X11 color system is interesting,
and was excellently summarized by
Alex Sexton in their talk “Peachpuffs and Lemonchiffons”.
named-001.html
parsing/color-valid.html
parsing/color-computed-named-color.html
parsing/color-invalid-named-color.html
System Colors
In general, the <> keywords
reflect default color choices made by the user, the browser, or the OS.
They are typically used in the browser default stylesheet, for this reason.
To maintain legibility,
the <> keywords also respond to light mode or dark mode changes.
For example, 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 such as #81D9FE would be used in dark mode
(WCAG contrast 13.28:1, AAA pass).
Legible link text
Illegible link text
Legible link text
However, in [=forced colors mode=],
most colors on the page are forced into a restricted, user-chosen palette.
The <system-color> keywords
expose these user-chosen colors
so that the rest of the page can integrate with this restricted palette.
When the forced-colors [=media feature=] is ''forced-colors/active'',
authors should use the <> keywords as color values
in properties other than those listed in [[css-color-adjust-1#forced-colors-properties]],
to ensure legibility and consistency across the page
and avoid an uncoordinated mishmash of user-forced and page-chosen colors.
system-color-consistency.html
system-color-support.html
parsing/color-valid-system-color.html
When the values of <> keywords come from the browser,
(as opposed to being OS defaults or user choices) the browser should
ensure that matching
foreground/background pairs have a minimum
of WCAG AA contrast.
However, user preferences (for higher or lower contrast),
whether set as a browser preference, a user stylesheet,
or by altering the OS defaults,
must take precedence over this requirement.
Authors may also use these keywords at any time,
but should be careful to use the colors
in matching background-foreground pairs
to ensure appropriate contrast,
as any particular contrast relationship across non-matching pairs
(e.g. ''Canvas'' and ''ButtonText'')
is not guaranteed.
The <> keywords are defined as follows:
AccentColor
Background of accented user interface controls.
AccentColorText
Text of accented user interface controls.
ActiveText
Text in active links. For light backgrounds, traditionally red.
ButtonBorder
The base border color for push buttons.
ButtonFace
The face background color for push buttons.
ButtonText
Text on push buttons.
Canvas
Background of application content or documents.
CanvasText
Text in application content or documents.
Field
Background of input fields.
FieldText
Text in input fields.
GrayText
Disabled text.
(Often, but not necessarily, gray.)
Highlight
Background of selected text, for example from ::selection.
HighlightText
Text of selected text.
LinkText
Text in non-active, non-visited links. For light backgrounds, traditionally blue.
Mark
Background of text that has been specially marked
(such as by the HTML <{mark}> element).
MarkText
Text that has been specially marked
(such as by the HTML <{mark}> element).
SelectedItem
Background of selected items, for example a selected checkbox.
SelectedItemText
Text of selected items.
VisitedText
Text in visited links. For light backgrounds, traditionally purple.
parsing/color-valid.html
system-color-compute.html
system-color-hightlights-vs-getSelection-001.html
system-color-hightlights-vs-getSelection-002.html
Note: As with all other [=CSS/keywords=],
these names are ASCII case-insensitive.
They are shown here with mixed capitalization for legibility.
For systems that do not have a particular system UI concept,
the specified value should be mapped to
the most closely related system color value that exists.
The following system color pairings are expected to form legible background-foreground colors:
* ''Canvas'' background with ''CanvasText'', ''LinkText'', ''VisitedText'', ''ActiveText'' foreground.
* ''Canvas'' background with a ''ButtonBorder'' border and adjacent color ''Canvas''
* ''ButtonFace'' background with ''ButtonText'' foreground.
* ''Field'' background with ''FieldText'' foreground.
* ''Mark'' background with ''MarkText'' foreground
* ''ButtonFace'' or ''Field'' background with a ''ButtonBorder'' border and adjacent color ''Canvas'''
* ''Highlight'' background with ''HighlightText'' foreground.
* ''SelectedItem'' background with ''SelectedItemText'' foreground.
* ''AccentColor'' background with ''AccentColorText'' foreground.
Additionally, ''GrayText'' is expected to be readable,
though possibly at a lower contrast rating,
over any of the backgrounds.
For example, the system color combinations in the browser you are currently using:
Canvas with CanvasText: CanvasText
Canvas with LinkText: LinkText
Canvas with VisitedText: VisitedText
Canvas with ActiveText: ActiveText
Canvas with GrayText: GrayText
Canvas with ButtonBorder and adjacent Canvas: CanvasTextAdjacent
ButtonFace with ButtonText: ButtonText
ButtonFace with ButtonText and ButtonBorder: ButtonText
ButtonFace with GrayText: GrayText
Field with FieldText: FieldText
Field with GrayText: GrayText
Mark with MarkText: MarkText
Mark with GrayText: GrayText
Highlight with HighlightText: HighlightText
Highlight with GrayText: GrayText
SelectedItem with SelectedItemText: SelectedItemText
AccentColor with AccentColorText: AccentColorText
AccentColor with GrayText: GrayText
Earlier versions of CSS defined additional <>s,
which have since been deprecated.
These are documented in [[#deprecated-system-colors]].
Note: The <>s incur some privacy and security risk, as detailed in [[#privacy]] and [[#security]].
User Agents may,
to mitigate privacy and security risks such as fingerprinting,
elect to return fixed values for the used value of system colors
which do not reflect customisation or theming choices
made by the user.
The ''transparent'' keyword
The keyword transparent specifies a transparent black.
It is a type of <>.
parsing/color-computed.html
parsing/color-valid.html
t423-transparent-1-a.xht
t423-transparent-2-a.xht
The ''/currentcolor'' keyword
The keyword currentcolor represents value of the 'color' property on the same element.
Unlike <>s, it is not restricted to sRGB;
the value can be any <>.
Its used values is determined by resolving color values.
border-color-currentcolor.html
color-mix-currentcolor-nested-for-color-property.html
currentcolor-001.html
currentcolor-002.html
currentcolor-003.html
currentcolor-004.html
currentcolor-visited-fallback.html
parsing/color-valid.html
Here's a simple example showing how to use the ''/currentcolor'' keyword:
For example, the 'text-emphasis-color' property [[CSS3-TEXT-DECOR]],
whose initial value is ''/currentcolor'',
by default matches the text color
even as the 'color' property changes across elements.
In the above example, the emphasis marks are black over the text "Some" and "emphasized text",
but red over the text "really".
Note: Multi-word keywords in CSS usually separate their component words with hyphens.
''/currentcolor'' doesn't, because (deep breath)
it was originally introduced in SVG
as a property value, "current-color" with the usual CSS spelling.
It (along with all other properties and their values)
then became presentation attributes and attribute values,
as well as properties,
to make generation with XSLT easier.
Then all of the presentation attributes were changed
from hyphenated to camelCase, because the DOM
had an issue with hyphen meaning "minus".
But then, they didn't follow CSS conventions
anymore so all the properties and property values
that were already part of CSS were changed back to hyphenated!
''/currentcolor'' was not a part of CSS at that time,
so remained camelCased.
Only later did CSS pick it up,
at which point the capitalization stopped mattering,
as CSS keywords are ASCII case-insensitive.
HSL Colors: ''hsl()'' and ''hsla()'' functions
The RGB system for specifying colors,
while convenient for machines and graphic libraries,
is often regarded as very difficult for humans to gain an intuitive grasp on.
It's not easy to tell, for example,
how to alter an RGB color to produce a lighter variant of the same hue.
There are several other color schemes possible.
One such is the HSL [[!HSL]] color scheme,
which is more intuitive to use,
but still maps easily back to RGB colors.
HSL colors are specified
as a triplet of hue, saturation, and lightness.
The syntax of the ''hsl()'' and ''hsla()'' functions is:
hsl-001.html
hsl-002.html
hsl-003.html
hsl-004.html
hsl-005.html
hsl-006.html
hsl-007.html
hsl-008.html
background-color-hsl-001.html
background-color-hsl-002.html
background-color-hsl-003.html
background-color-hsl-004.html
parsing/color-computed-hsl.html
parsing/color-invalid-hsl.html
parsing/color-valid-hsl.html
The first argument specifies the hue angle.
In HSL (and HWB) the angle ''0deg'' represents sRGB primary red
(as does ''360deg'', ''720deg'', etc.),
and the rest of the hues are spread around the circle,
so ''120deg'' represents sRGB primary green,
''240deg'' represents sRGB primary blue, etc.
The next two arguments are the saturation and lightness, respectively.
For saturation, ''100%'' or ''100'' is a fully-saturated, bright color,
and ''0%'' or ''0'' is a fully-unsaturated gray.
For lightness, ''50%'' or ''50'' represents the "normal" color,
while ''100%'' or ''100'' is white and ''0%'' or ''0'' is black.
For historical reasons,
if the saturation is less than ''0%''
it is clamped to ''0%''
at parsed-value time,
before being converted to an sRGB color.
t424-hsl-clip-outside-gamut-b.xht
parsing/color-valid-hsl.html
The final argument specifies the alpha channel of the color.
It's interpreted identically to the fourth argument of the ''rgb()'' function.
If omitted, it defaults to ''100%''.
HSL colors resolve to sRGB.
If the saturation of an HSL color is ''0%'' or ''0'',
then the hue component is [=powerless=].
For example, an ordinary red,
the same color you would see from the keyword ''red''
or the hex notation ''#f00'',
is represented in HSL as ''hsl(0deg 100% 50%)''.
An advantage of HSL over RGB is that it is more intuitive:
people can guess at the colors they want,
and then tweak.
For example, the following colors can all be generated off of the basic "green" hue,
just by varying the other two arguments:
hsl(120deg 100% 50%) lime green
hsl(120deg 100% 25%) dark green
hsl(120deg 100% 75%) light green
hsl(120deg 75% 85%) pastel green
A disadvantage of HSL over Oklch
is that hue manipulation changes the visual lightness,
and that hues are not evenly spaced apart.
It is thus easier in HSL to create sets of matching colors
(by keeping the hue the same and varying the saturation and lightness),
compared to manipulating the sRGB component values;
however, because the lightness is simply the mean of the gamma-corrected
red, green and blue components
it does not correspond to the visual perception of lightness
across hues.
For example, ''blue''
is represented in HSL as
''hsl(240deg 100% 50%)''
while ''yellow''
is ''hsl(60deg 100% 50%)''.
Both have an HSL Lightness of 50%,
but clearly the yellow looks much lighter than the blue.
In Oklch, sRGB blue is
''oklch(0.452 0.313 264.1)''
while
sRGB yellow is
''oklch(0.968 0.211 109.8)''.
The Oklch Lightnesses of 0.452 and 0.968 clearly reflect
the visual lightnesses of the two colors.
The hue angle in HSL is not perceptually uniform;
colors appear bunched up in some areas
and widely spaced in others.
For example, the pair of hues
''hsl(220deg 100% 50%)''
and
''hsl(250deg 100% 50%)''
have an HSL hue difference of 250-220 = 30deg and look fairly similar,
while another pair of colors
''hsl(50deg 100% 50%)''
and
''hsl(80deg 100% 50%)'',
which also have a hue difference of 80-50 = 30deg, look very different.
In Oklch, the same pair of colors
''oklch(0.533 0.26 262.6)''
and
''oklch(0.462 0.306 268.9)''
have a hue difference of 268.9 - 262.6 = 6.3deg
while the second pair
''oklch(0.882 0.181 94.24)''
and
''oklch(0.91 0.245 129.9)''
have a hue difference of 129.9 - 94.24 = 35.66deg,
correctly reflecting the visual separation of hues.
For historical reasons,
''hsl()'' and ''hsla()'' also support a legacy color syntax.
hsla-001.html
hsla-002.html
hsla-003.html
hsla-004.html
hsla-005.html
hsla-006.html
hsla-007.html
hsla-008.html
parsing/color-valid.html
Converting HSL Colors to sRGB
Converting an HSL color to sRGB is straightforward mathematically.
Here's a sample implementation of the conversion algorithm in JavaScript.
It returns an array of three numbers
representing the red, green, and blue channels of the colors,
which for colors in the sRGB gamut will be in the range [0, 1].
This code assumes that parse-time clamping
of negative saturation has already been applied.
path: hslToRgb.js
highlight: js
Converting sRGB Colors to HSL
Conversion in the reverse direction proceeds similarly.
Special care is taken to deal with
intermediate negative values of saturation,
which can be produced by colors far outside the sRGB gamut.
path: better-rgbToHsl.js
highlight: js
Examples of HSL Colors
This section is not normative.
The tables below illustrate a wide range of possible HSL colors.
Each table represents one hue,
selected at 30° intervals,
to illustrate the common "core" hues:
red,
yellow,
green,
cyan,
blue,
magenta,
and the six intermediary colors between these.
In each table, the X axis represents the saturation
while the Y axis represents the lightness.
0° Reds
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
30° Reds-Yellows (=Oranges)
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
60° Yellows
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
90° Yellow-Greens
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
120° Greens
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
150° Green-Cyans
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
180° Cyans
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
210° Cyan-Blues
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
240° blues
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
270° Blue-Magentas
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
300° Magentas
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
330° Magenta-Reds
100%
80%
60%
40%
20%
0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
HWB Colors: ''hwb()'' function
HWB (short for Hue-Whiteness-Blackness)
[[!HWB]]
is another method of specifying sRGB colors,
similar to ''HSL''', but often even easier for humans to work with.
It describes colors with a starting hue,
then a degree of whiteness and blackness to mix into that base hue.
Many color-pickers are based on the HWB color system,
due to its intuitiveness.
HWB colors resolve to sRGB.
The syntax of the ''hwb()'' function is:
The first argument specifies the hue,
and is interpreted identically to ''hsl()'';
this means it suffers the same disadvantages
such as hue uniformity.
The second argument specifies the amount of white to mix in,
as a percentage from ''0%'' (no whiteness) to ''100%'' (full whiteness).
Similarly, the third argument specifies the amount of black to mix in,
also from ''0%'' (no blackness) to ''100%'' (full blackness).
For example, hwb(150 20% 10%)
is the same color as
hsl(150 77.78% 55%)
and
rgb(20% 90% 55%).
Values outside of these ranges
are not invalid;
hue angles outside the range [0,360) will be normalized to that range
and values of white and black which sum to 100% or greater will
produce achromatic colors as described below.
The resulting color can be thought of conceptually as a mixture of paint in the chosen hue,
white paint, and black paint,
with the relative amounts of each determined by the percentages.
If the sum white+black is greater than or equal to ''100%'',
it defines an achromatic color,
i.e. a shade of gray;
when converted to sRGB the R, G and B values are identical
and have the value white / (white + black).
For example, in the color
hwb(45 40% 80%)
white and black adds to 120, so this is an achromatic color
whose R, G and B components are 40 / 40 + 80 = 0.33
rgb(33.33% 33.33% 33.33%).
Achromatic HWB colors no longer contain any hint of the chosen hue.
In this case, the hue component is [=powerless=].
The fourth argument specifies the alpha channel of the color.
It's interpreted identically to the fourth argument of the ''rgb()'' function.
If omitted, it defaults to ''100%''.
There is no Web compatibility issue
with ''hwb'', which is new in this level of the specification, and so
''hwb()'' does not support a legacy color syntax
that separates all of its arguments with commas.
Using commas inside ''hwb()'' is an error.
hwb-001.html
hwb-002.html
hwb-003.html
hwb-004.html
hwb-005.html
parsing/color-valid.html
parsing/color-computed-hwb.html
parsing/color-invalid-hwb.html
parsing/color-valid-hwb.html
Converting HWB Colors to sRGB
Converting an HWB color to sRGB is straightforward,
and related to how one converts HSL to RGB.
The following Javascript implementation of the algorithm
first normalizes the white and black components,
so their sum is no larger than 100%.
path: hwbToRgb.js
highlight: js
Converting sRGB Colors to HWB
Conversion in the reverse direction proceeds similarly.
path: rgbToHwb.js
highlight: js
Examples of HWB Colors
This section is not normative.
0° Reds
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
30° Red-Yellows (Oranges)
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
60° Yellows
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
90° Yellow-Greens
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
120° Greens
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
150° Green-Cyans
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
180° Cyans
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
210° Cyan-Blues
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
240° Blues
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
270° Blue-Magentas
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
300° Magentas
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
330° Magenta-Reds
W\B
0%
20%
40%
60%
80%
100%
0%
20%
40%
60%
80%
100%
Device-independent Colors: CIE Lab and LCH, Oklab and Oklch
CIE Lab and LCH
This section is not normative.
Physical measurements of a color are typically expressed in the CIE L*a*b* [[!CIELAB]] color space,
created in 1976 by the CIE
and commonly referred to simply as Lab.
Color conversions from one device to another may also use Lab as an intermediate step.
Derived from human vision experiments,
Lab represents the entire range of color that humans can see.
Lab is a rectangular coordinate system
with a central Lightness (L) axis.
This value is usually written as a unitless number;
for compatibility with the rest of CSS, it may also be written as a percentage.
100% means an L value of 100, not 1.0.
L=0% or 0 is deep black (no light at all)
while L=100% or 100 is a diffuse white.
Usefully, L=50% or 50 is mid gray, by design,
and equal increments in L are evenly spaced visually:
the Lab color space is intended to be perceptually uniform.
The a and b axes convey hue;
positive values along the a axis are a purplish red
while negative values are the complementary color, a green.
Similarly, positive values along the b axis are yellow
and negative are the complementary blue/violet.
Desaturated colors have small values of a and b
and are close to the L axis;
saturated colors lie far from the L axis.
The illuminant is [=D50=] white, a standardized daylight spectrum with a color temperature of 5000K,
as reflected by a perfect diffuse reflector; it approximates the color of sunlight on a sunny day.
D50 is also the whitepoint used for the profile connection space in ICC color interconversion,
the whitepoint used in image editors which offer Lab editing,
and the value used by physical measurement devices
such as spectrophotometers and spectroradiometers,
when they report measured colors in Lab.
Conversion from colors specified using other white points is called a chromatic adaptation transform,
which models the changes in the human visual system as we adapt to a new lighting condition.
The linear Bradford algorithm [[!ICC]]
(a simplification of the original Bradford algorithm [[!Bradford-CAT]])
is the industry standard chromatic adaptation transform,
and is easy to calculate as it is a simple matrix multiplication.
CIE LCH has the same L axis as Lab,
but uses polar coordinates C (chroma) and H (hue),
making it a polar, cylindrical coordinate system.
C is the geometric distance from the L axis
and H is the angle from the positive a axis,
towards the positive b axis.
Note: The L axis in Lab and LCH
is not to be confused with the L axis in HSL.
For example, in HSL, the sRGB colors blue (#00F) and yellow (#FF0)
have the same value of L (50%) even though visually, blue is much darker.
This is much clearer in Lab:
sRGB blue is lab(29.567% 68.298 -112.0294)
while
sRGB yellow is lab(97.607% -15.753 93.388).
In Lab and LCH, if two colors have the same measured L value,
they have identical visual lightness.
HSL and related polar RGB models were developed
in an attempt
to give similar usability benefits for RGB that LCH gave to Lab,
but are significantly less accurate.
Although the use of CIE Lab and LCH is widespread,
it is known to have some problems. In particular:
Hue linearity
In the blue region (LCH Hue between 270° and 330°),
visual hue departs from what LCH predicts.
Plotting a set of blues of the same hue and differing Chroma,
which should lie on a straight line from the neutral axis,
instead form a curve.
Put another way,
as a saturated blue has it's Chroma progressively reduced,
it becomes noticeably purple.
Hue uniformity
While hues in LCH are in general evenly spaced,
(and far better than HSL or HWB),
uniformity is not perfect.
Over-prediction of high Chroma differences
For high Chroma colors,
changes in Chroma are less noticeable
than for more neutral colors.
These deficiencies affect, for example,
creation of evenly spaced gradients,
gamut mapping from one color space to a smaller one,
and computation of the visual difference between two colors.
To compensate for this,
formulae to predict the visual difference between two colors
(delta E)
have been made more accurate over time
(but also, much more complex to compute).
The current industry standard formula,
delta E 2000,
works well to mitigate some of the Lab and LCH problems.
A sample implementation is given in
[[#color-difference-2000]].
This does not help with hue curvature, however.
Oklab and Oklch
This section is not normative.
Recently, Oklab,
an improved Lab-like space has been developed [[!Oklab]].
The corresponding polar form is called Oklch.
It was produced by numerical optimization
of a large dataset of visually similar colors,
and has improved hue linearity,
hue uniformity,
and chroma uniformity
compared to CIE LCH.
Like CIE Lab, there is a central lightness L axis
which is usually written as a unitless number in the range [0,1];
for compatibility with the rest of CSS,
it may be written as a percentage. 100% means an L value of 1.0.
L=0% or 0.0 is deep black (no light at all) while L=100% or 1.0 is a diffuse white.
Note: Unlike CIE Lab, which assumes adaptation to the diffuse white,
Oklab assumes adaptation to the color being defined,
which is intended to make it scale invariant.
As with CIE Lab, the a and b axes convey hue;
positive values along the a axis are a purplish red
while negative values are the complementary color, a green.
Similarly, positive values along the b axis are yellow
and negative are the complementary blue/violet.
The illuminant is [=D65=], the same white point
as most RGB color spaces.
Oklch has the same L axis as Oklab,
but uses polar coordinates C (chroma) and H (hue).
Note: Unlike CIE LCH, where Chroma can reach values of 200 or more,
Oklch Chroma ranges to 0.5 or so.
The hue angles between CIE LCH and Oklch are broadly similar,
but not identical.
Because Oklab is more perceptually uniform than CIE Lab,
the color difference is a straightforward distance in 3D space
(root sum of squares).
Although trivial,
a sample implementation is give in
[[#color-difference-OK]].
Specifying Lab and LCH: the ''lab()'' and ''lch()'' functional notations
CSS allows colors to be directly expressed in Lab and LCH.
for L: 0% = 0.0, 100% = 100.0
for a and b: -100% = -125, 100% = 125
lab-001.html
lab-002.html
lab-003.html
lab-004.html
lab-005.html
lab-006.html
lab-007.html
lab-008.html
lab-l-over-100-1.html
lab-l-over-100-2.html
parsing/color-valid.html
parsing/color-computed-lab.html
parsing/color-invalid-lab.html
parsing/color-valid-lab.html
In Lab,
the first argument specifies the CIE Lightness, L.
This is a number between ''0%'' or 0
and ''100%'' or 100
Values less than ''0%'' or 0 must be clamped to ''0%'' at parsed-value time;
values greater than
''100%'' or 100 are clamped to ''100%'' at parsed-value time.
The second and third arguments are the distances along the "a" and "b" axes
in the Lab color space,
as described in the previous section.
These values are signed
(allow both positive and negative values)
and theoretically unbounded
(but in practice do not exceed ±160).
There is an optional fourth <> component,
separated by a slash,
representing the [=alpha component=].
If the lightness of a Lab color (after clamping) is ''0%'',
or ''100%''
the color will be displayed as black, or white, respectively
due to gamut mapping to the display.
for L: 0% = 0.0, 100% = 100.0
for C: 0% = 0, 100% = 150
lch-001.html
lch-002.html
lch-003.html
lch-004.html
lch-005.html
lch-006.html
lch-007.html
lch-008.html
lch-009.html
lch-010.html
lch-l-over-100-1.html
lch-l-over-100-2.html
parsing/color-valid.html
In CIE LCH
the first argument specifies the CIE Lightness L,
interpreted identically to the Lightness argument of ''lab()''.
The second argument is the chroma C,
(roughly representing the "amount of color").
Its minimum useful value is ''0'',
while its maximum is theoretically unbounded
(but in practice does not exceed ''230'').
If the provided value is negative,
it is clamped to ''0'' at parsed-value time.
The third argument is the hue angle H.
It's interpreted similarly to the <> argument of ''hsl()'',
but doesn't map hues to angles in the same way
because they are evenly spaced perceptually.
Instead, ''0deg'' points along the positive "a" axis (toward purplish red),
(as does ''360deg'', ''720deg'', etc.);
''90deg'' points along the positive "b" axis (toward mustard yellow),
''180deg'' points along the negative "a" axis (toward greenish cyan),
and ''270deg'' points along the negative "b" axis (toward sky blue).
There is an optional fourth <> component,
separated by a slash,
representing the [=alpha component=].
If the chroma of an LCH color is ''0%'',
the hue component is [=powerless=].
If the lightness of an LCH color (after clamping) is ''0%'',
or ''100%'',
the color will be displayed as black, or white, respectively
due to gamut mapping to the display.
There is no Web compatibility issue
with ''lab'' or ''lch''', which are new in this level of the specification, and so
''lab()'' and ''lch()'' do not support a legacy color syntax
that separates all of their arguments with commas.
Using commas inside these functions is an error.
Specifying Oklab and Oklch: the ''oklab()'' and ''oklch()'' functional notations
CSS allows colors to be directly expressed in Oklab and Oklch.
for L: 0% = 0.0, 100% = 1.0
for a and b: -100% = -0.4, 100% = 0.4
oklab-001.html
oklab-002.html
oklab-003.html
oklab-004.html
oklab-005.html
oklab-006.html
oklab-007.html
oklab-008.html
oklab-009.html
oklab-l-almost-0.html
oklab-l-almost-1.html
oklab-l-over-1-1.html
oklab-l-over-1-2.html
parsing/color-valid.html
In Oklab
the first argument specifies the Oklab Lightness.
This is a number between ''0%'' or 0
and ''100%'' or 1.0.
Values less than ''0%'' or 0.0 must be clamped to ''0%''
at parsed-value time;
values greater than ''100%'' or 1.0 are clamped to ''100%''
at parsed-value time.
The second and third arguments are the distances along
the "a" and "b" axes
in the Oklab color space,
as described in the previous section.
These values are signed
(allow both positive and negative values)
and theoretically unbounded
(but in practice do not exceed ±0.5).
There is an optional fourth <> component,
separated by a slash,
representing the [=alpha component=].
If the lightness of an Oklab color is ''0%'' or 0,
or ''100%'' or 1.0,
the color will be displayed as black, or white, respectively
due to gamut mapping to the display.
for L: 0% = 0.0, 100% = 1.0
for C: 0% = 0.0 100% = 0.4
oklch-001.html
oklch-002.html
oklch-003.html
oklch-004.html
oklch-005.html
oklch-006.html
oklch-007.html
oklch-008.html
oklch-009.html
oklch-010.html
oklch-011.html
oklch-l-almost-0.html
oklch-l-almost-1.html
oklch-l-over-1-1.html
oklch-l-over-1-2.html
parsing/color-valid.html
In Oklch
the first argument specifies the Oklch Lightness L,
interpreted identically to the Lightness argument of ''oklab()''.
The second argument is the chroma C.
Its minimum useful value is ''0'',
while its maximum is theoretically unbounded
(but in practice does not exceed ''0.5'').
If the provided value is negative,
it is clamped to ''0'' at parsed-value time.
The third argument is the hue angle H.
It's interpreted similarly to the <> arguments
of ''hsl()'' and ''lch()'',
but doesn't map hues to angles in the same way.
''0deg'' points along the positive "a" axis (toward purplish red),
(as does ''360deg'', ''720deg'', etc.);
''90deg'' points along the positive "b" axis (toward mustard yellow),
''180deg'' points along the negative "a" axis (toward greenish cyan),
and ''270deg'' points along the negative "b" axis (toward sky blue).
There is an optional fourth <> component,
separated by a slash,
representing the [=alpha component=].
If the chroma of an Oklch color is ''0%'' or 0,
the hue component is [=powerless=].
If the lightness of an Oklch color is ''0%'' or 0,
or ''100%'' or 1.0,
the color will be displayed as black, or white, respectively
due to gamut mapping to the display.
There is no Web compatibility issue
with ''oklab'' or ''oklch''', which are new in this level of the specification, and so
''oklab()'' and ''oklch()'' do not support a legacy color syntax
that separates all of their arguments with commas.
Using commas inside these functions is an error.
Converting Lab or Oklab colors to LCH or Oklch colors
Conversion to the polar form is trivial:
H = atan2(b, a)
C = sqrt(a^2 + b^2)
L is the same
For extremely small values of a and b (near-zero Chroma),
although the visual color does not change from being on the neutral axis,
small changes to the values can result in the reported hue angle swinging about wildly
and being essentially random.
In CSS, this means the hue is [=powerless=],
and treated as [=missing=] when converted into LCH or Oklch;
in non-CSS contexts this might be reflected as a missing value, such as NaN.
Converting LCH or Oklch colors to Lab or Oklab colors
Conversion to the rectangular form is trivial:
If H is missing, a = b = 0
Otherwise,
a = C cos(H)
b = C sin(H)
L is the same
Predefined Color Spaces
CSS provides several predefined color spaces
including ''display-p3'' [[!Display-P3]],
which is a wide gamut space typical of current wide-gamut monitors,
''prophoto-rgb'', widely used by photographers
and ''rec2020'' [[!Rec.2020]],
which is a broadcast industry standard,
ultra-wide gamut space capable of representing almost all visible real-world colors.
Specifying Predefined Colors: the ''color()'' function
The ''color()'' function allows a color to be specified
in a particular, specified [=color space=]
(rather than the implicit sRGB color space that most of the other color functions operate in).
Its syntax is:
parsing/color-computed-color-function.html
parsing/color-invalid-color-function.html
parsing/color-valid-color-function.html
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=].
The parameters have the following form:
* An <> denoting
one of the predefined color spaces
(such as ''display-p3'')
Individual predefined color spaces
may further restrict whether <>s or <>s
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),
this argument represents an invalid color.
* The three parameter values that the color space takes (RGB or XYZ values).
An out of gamut color has component values
less than 0 or 0%, or greater than 1 or 100%.
These are not invalid, and are retained for intermediate computations;
instead, for display, they are
css gamut mapped using a relative colorimetric intent
which brings the values
(in the display color space)
within the range 0/0% to 1/100%
at actual-value time.
* An optional slash-separated <>.
parsing/color-valid.html
There is no Web compatibility issue
with ''color()'', which is new in this level of the specification, and so
''color()'' does not support a legacy color syntax
that separates all of its arguments with commas.
Using commas inside this function is an error.
A color which is either an [=invalid color=] or
an [=out of gamut=] color
can't be displayed.
If the specified color can be displayed,
(that is, it isn't an [=invalid color=]
and isn't [=out of gamut=])
then this is the actual value of the ''color()'' function.
If the specified color
is a [=valid color=]
but [=can't be displayed=],
the actual value is derived from the specified color,
css gamut mapped for display.
If the color is an [=invalid color=],
the used value is opaque black.
This very intense lime color is in-gamut for rec.2020:
color(rec2020 0.42053 0.979780 0.00579);
in LCH, that color is
lch(86.6146% 160.0000 136.0088);
in display-p3, that color is
color(display-p3 -0.6112 1.0079 -0.2192);
and is out of gamut for display-p3
(red and blue are negative, green is greater than 1).
If you have a display-p3 screen, that color is:
valid
in gamut (for rec.2020)
out of gamut (for your display)
and so can't be displayed
The color used for display will be a less intense color
produced automatically by gamut mapping.
This example has a typo!
An intense green is provided in profoto-rgb space (which doesn't exist).
This makes it invalid, so the used value is opaque black
color(profoto-rgb 0.4835 0.9167 0.2188)
The Predefined sRGB Color Space: the ''sRGB'' keyword
The sRGB predefined color space
defined below
is the same as is used for legacy sRGB colors,
such as ''rgb()''.
srgb
The ''srgb'' [[!SRGB]] color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1].
The whitepoint is [=D65=].
[[!SRGB]] specifies two viewing conditions, encoding
and typical. The [[ICC]] recommends using the encoding
conditions for color conversion and for optimal viewing, which are
the values in the table below.
sRGB is the default color space for CSS,
used for all the legacy color functions.
It has the following characteristics:
x
y
Red chromaticity
0.640
0.330
Green chromaticity
0.300
0.600
Blue chromaticity
0.150
0.060
White chromaticity
[=D65=]
Transfer function
see below
White luminance
80.0 cd/m2
Black luminance
0.20 cd/m2
Image state
display-referred
Percentages
Allowed for R, G and B
Percent reference range
for R,G,B: 0% = 0.0, 100% = 1.0
let sign = c < 0? -1 : 1;
let abs = Math.abs(c);
if (abs <= 0.04045) {
cl = c / 12.92;
}
else {
cl = sign * (Math.pow((abs + 0.055) / 1.055, 2.4));
}
c is the gamma-encoded red, green or blue component.
cl is the corresponding linear-light component.
The Predefined Linear-light sRGB Color Space: the ''srgb-linear'' keyword
The sRGB-linear predefined color space
is the same as ''srgb''
except that the transfer function
is linear-light (there is no gamma-encoding).
srgb-linear
The ''srgb-linear'' [[!SRGB]] color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1].
The whitepoint is [=D65=].
It has the following characteristics:
x
y
Red chromaticity
0.640
0.330
Green chromaticity
0.300
0.600
Blue chromaticity
0.150
0.060
White chromaticity
[=D65=]
Transfer function
unity, see below
White luminance
80.0 cd/m2
Black luminance
0.20 cd/m2
Image state
display-referred
Percentages
Allowed for R, G and B
Percent reference range
for R,G,B: 0% = 0.0, 100% = 1.0
cl = c;
c is the red, green or blue component.
cl is the corresponding linear-light component, which is identical.
To avoid banding artifacts, a
higher precision is required
for ''srgb-linear'' than for ''srgb''.
The Predefined Display P3 Color Space: the ''display-p3'' keyword
display-p3
The ''display-p3'' [[!Display-P3]] color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1].
It uses the same primary chromaticities as [[DCI-P3]],
but with a [=D65=] whitepoint, and the same transfer curve as sRGB.
Modern displays, TVs, laptop screens and phone screens
are able to display all, or nearly all,
of the display-p3 gamut.
It has the following characteristics:
The Predefined A98 RGB Color Space: the ''a98-rgb'' keyword
a98-rgb
The ''a98-rgb'' color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1].
The transfer curve is
a gamma function, close to but not exactly 1/2.2.
It has the following characteristics:
The Predefined ProPhoto RGB Color Space: the ''prophoto-rgb'' keyword
prophoto-rgb
The ''prophoto-rgb'' color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1].
The transfer curve is
a gamma function with a value of 1/1.8,
and a small linear portion near black.
The white point is [=D50=], the same as is used by CIE Lab. Thus,
conversion to CIE Lab does not require the chromatic adaptation step.
The ProPhoto RGB space uses hyper-saturated,
non physically realizable primaries.
These were chosen to allow
a wide color gamut and in particular,
to minimize hue shifts under tonal manipulation.
It is often used in digital photography as a wide gamut
color space for the archival version of
photographic images. The ''prophoto-rgb'' color space allows CSS to
specify colors that will match colors in such images
having the same RGB values.
The ProPhoto RGB space was originally developed by Kodak
and is described in [[Wolfe]].
It was standardized by ISO as [[!ROMM]],[[ROMM-RGB]].
The white luminance is given as a range, and
the viewing flare (and thus, the black luminance)
is 0.5% to 1.0% of this.
It has the following characteristics:
x
y
Red chromaticity
0.734699
0.265301
Green chromaticity
0.159597
0.840403
Blue chromaticity
0.036598
0.000105
White chromaticity
[=D50=]
Transfer function
see below
White luminance
160.0 to 640.0 cd/m2
Black luminance
See text
Image state
display-referred
Percentages
Allowed for R, G and B
Percent reference range
for R,G,B: 0% = 0.0, 100% = 1.0
const E = 16/512;
let sign = c < 0? -1 : 1;
let abs = Math.abs(c);
if (abs <= E) {
cl = c / 16;
}
else {
cl = sign * Math.pow(c, 1.8);
}
c is the gamma-encoded red, green or blue component.
cl is the corresponding linear-light component.
The Predefined ITU-R BT.2020-2 Color Space: the ''rec2020'' keyword
rec2020
The ''rec2020'' [[!Rec.2020]] color space accepts three numeric parameters,
representing the red, green, and blue channels of the color.
In-gamut colors have all three components in the range [0, 1],
("full-range", in video terminology).
ITU Reference 2020 is used for
Ultra High Definition, 4k and 8k television.
The primaries are physically realizable,
but with difficulty
as they lie very close to the spectral locus.
Current displays are unable to reproduce the full gamut of rec2020.
Coverage is expected to increase over time as displays improve.
It has the following characteristics:
The Predefined CIE XYZ Color Spaces: the ''xyz-d50'', ''xyz-d65'', and ''xyz'' keywords
xyz-d50, xyz-d65, xyz
The ''xyz'' color space accepts three numeric parameters,
representing the X,Y and Z values.
It represents the CIE XYZ [[!COLORIMETRY]] color space,
scaled such that diffuse white has a [=luminance=] (Y) of 1.0.
and, if necessary, chromatically adapted to the reference white.
The reference white for ''xyz-d50'' is [=D50=], while
the reference white for ''xyz-d65'' and ''xyz'' is [=D65=].
Values greater than 1.0/100% are allowed and must not be clamped;
they represent colors brighter than diffuse white.
Values less than 0/0% are uncommon,
but can occur as a result of chromatic adaptation,
and likewise must not be clamped.
It has the following characteristics:
Converting Predefined Color Spaces to Lab or Oklab
For all predefined RGB color spaces,
conversion to Lab requires several steps,
although in practice all but the first step are linear calculations and can be combined.
Convert from gamma-encoded RGB to linear-light RGB (undo gamma encoding)
Convert from linear RGB to CIE XYZ
If needed, convert from a [=D65=] whitepoint
(used by ''sRGB'', ''display-p3'', ''a98-rgb'' and ''rec2020'')
to the [=D50=] whitepoint used in Lab,
with the linear Bradford transform. ''prophoto-rgb'' already has a [=D50=] whitepoint.
Convert D50-adapted XYZ to Lab
Conversion to Oklab is similar,
but the chromatic adaptation step
is only needed for ''prophoto-rgb''.
Convert from gamma-encoded RGB to linear-light RGB (undo gamma encoding)
Convert from linear RGB to CIE XYZ
If needed, convert from a [=D50=] whitepoint (used by ''prophoto-rgb'')
to the [=D65=] whitepoint used in Oklab,
with the linear Bradford transform.
Convert D65-adapted XYZ to Oklab
There is sample JavaScript code for these conversions
in [[#color-conversion-code]].
Converting Lab or Oklab to Predefined RGB Color Spaces
Conversion from Lab to predefined spaces like ''display-p3'' or ''rec2020''
also requires multiple steps,
and again in practice all but the last step are linear calculations and can be combined.
Convert Lab to (D50-adapted) XYZ
If needed, convert from a [=D50=] whitepoint (used by Lab)
to the [=D65=] whitepoint used in sRGB and most other RGB spaces,
with the linear Bradford transform. ''prophoto-rgb''' does not require this step.
Convert from (D65-adapted) CIE XYZ to linear RGB
Convert from linear-light RGB to RGB (do gamma encoding)
Conversion from Oklab is similar,
but the chromatic adaptation step
is only needed for ''prophoto-rgb''.
Convert Oklab to (D65-adapted) XYZ
If needed, convert from a [=D65=] whitepoint (used by Oklab)
to the [=D50=] whitepoint used in ''prophoto-rgb'',
with the linear Bradford transform.
Convert from (D65-adapted) CIE XYZ to linear RGB
Convert from linear-light RGB to RGB (do gamma encoding)
There is sample JavaScript code for these conversions
in [[#color-conversion-code]].
Implementations may choose to implement these steps in some other way
(for example, using an ICC profile with relative colorimetric rendering intent)
provided the results are the same for colors inside both the source and destination gamuts.
Converting Between Predefined RGB Color Spaces
Conversion from one predefined RGB color space to another
requires multiple steps,
one of which is only needed when the whitepoints differ.
To convert from src to dest:
Convert from gamma-encoded srcRGB to linear-light srcRGB (undo gamma encoding)
Convert from linear srcRGB to CIE XYZ
If src and dest have different whitepoints,
convert the XYZ value from srcWhite to destWhite
with the linear Bradford transform.
Convert from CIE XYZ to linear destRGB
Convert from linear-light destRGB to destRGB (do gamma encoding)
There is sample JavaScript code for this conversion
for the predefined RGB color spaces, in [[#color-conversion-code]].
Colors may be converted
from one color space to another and,
provided that there is no gamut mapping
and that each color space can represent out of gamut colors,
(for RGB spaces, this means that the transfer function is defined over the extended range)
then (subject to numerical precision and round-off error)
the two colors will look the same and represent the same color sensation.
To convert a color |col1| in a source color space |src|
with white point |src-white|
to a color |col2| in destination color space |dest|
with white point |dest-white|:
If |src| is in a cylindrical polar color representation,
first convert |col1|
to the corresponding rectangular orthogonal color representation
and let this be the new |col1|. Replace any [=missing component=] with zero.
If |src| is not a linear-light representation,
convert it to linear light (undo gamma-encoding)
and let this be the new |col1|.
Convert |col1| to CIE XYZ with a given whitepoint |src-white|
and let this be |xyz|.
If |dest-white| is not the same as |src-white|,
chromatically adapt |xyz| to |dest-white|
using a linear Bradford [=chromatic adaptation transform=],
and let this be the new |xyz|.
Convert |xyz| to |dest|,
followed by applying any transfer function (gamma encoding),
producing |col2|.
If |dest| is a physical output color space, such as a display,
then |col2| must be [=css gamut mapped=]
so that it [=can be displayed=].
If |dest-rect| is not the same as |dest|,
in other words |dest| is a cylindrical polar color representation,
convert from |dest-rect| to |dest|, and let this be |col2|.
This may produce [=missing component=]s.
Color Interpolation
Color interpolation happens with
gradients,
compositing,
filters,
transitions,
animations, and
color mixing and color modification functions.
Interpolation between two <> values
takes place by executing the following steps:
checking the two colors for [=analogous components=]
which will be carried forward
converting them to a given color space
which will be referred to as the interpolation color space below.
If one or both colors are already in the interpolation colorspace,
this conversion changes any [=powerless=] components to [=missing=] values
(if required) re-inserting [=carried-forward=] values in the converted colors
(if required) fixing up the hues, depending on the selected <>
changing the color components to premultiplied form
linearly interpolating each component of the computed value of the color separately
undoing premultiplication
Interpolating to or from ''/currentcolor'' is possible.
The numerical value used for this purpose is the used value.
Color Space for Interpolation
Various features in CSS depend on interpolating colors.
Mixing or otherwise combining colors
has different results depending on the [=interpolation color space=] used.
Thus, different color spaces may be more appropriate for each interpolation use case.
* In some cases, the result of physically mixing two colored lights is desired.
In that case, the CIE XYZ or srgb-linear color space is appropriate, because they are linear in light intensity.
* If colors need to be evenly spaced perceptually (such as in a gradient),
the Oklab color space (and the older Lab), are designed to be perceptually uniform.
* If avoiding graying out in color mixing is desired, i.e. maximizing chroma throughout the transition,
Oklch (and the older LCH) work well for that.
* Lastly, compatibility with legacy Web content may be the most important consideration.
The sRGB color space, which is neither linear-light nor perceptually uniform, is the choice here,
even though it produces poorer results (overly dark or greyish mixes).
These features are collectively termed the host syntax.
They are not used by this specification itself,
only exposed so that other specifications can use them;
see e.g. use in [[css-images-4#linear-gradients]].
The host syntax should define
what the default [=interpolation color space=] should be for each case,
and optionally provide syntax for authors to override this default.
If such syntax is part of a property value, it should use a <> token,
defined below for easy reference from other specifications.
This ensures consistency across CSS,
and that further customizations on how color interpolation is performed
can automatically percolate across all of CSS.
The keywords in the definitions of <> and <>
each refer to their corresponding color space,
represented in CSS either by the functional syntax with the same name,
or (if no such function is present), by the corresponding <> in the ''color()'' function.
color-mix-percents-01.html
color-mix-percents-02.html
gradients-with-transparent.html
gradient/gradient-eval-001.html
gradient/gradient-eval-002.html
gradient/gradient-eval-003.html
gradient/gradient-eval-004.html
gradient/gradient-eval-005.html
gradient/gradient-eval-006.html
gradient/gradient-eval-007.html
gradient/gradient-eval-008.html
gradient/gradient-eval-009.html
gradient/gradient-none-interpolation.html
gradient/legacy-color-gradient.html
gradient/oklab-gradient.html
gradient/srgb-gradient.html
gradient/srgb-linear-gradient.html
gradient/xyz-gradient.html
parsing/gradient-interpolation-method-valid.html
parsing/gradient-interpolation-method-invalid.html
parsing/gradient-interpolation-method-computed.html
If the host syntax does not define what color space
interpolation should take place in,
it defaults to Oklab.
Authors that prefer interpolation in sRGB
in a particular instance
can opt-in to the the old behavior
by explicitly specifying sRGB as the [=interpolation color space=],
for example on a particular gradient where that result is desired.
If the colors to be interpolated are outside the gamut
of the [=interpolation color space=] ,
then once converted to that space,
they will contain out of range values.
These are not clipped, but the values are interpolated as-is.
Interpolating with Missing Components
In the course of converting the two colors
to the [=interpolation color space=],
any [=missing components=]
would be replaced with the value 0.
Thus, the first stage in interpolating two colors
is to classify any [=missing components=]
in the input colors,
and compare them to the components of the
[=interpolation color space=].
If any [=analogous components=] which are [=missing components=] are found,
they will be carried forward
and re-inserted in the converted color
before premultiplication, and
before linear interpolation takes place.
The analogous components are as follows:
Category
Components
Reds
r,x
Greens
g,y
Blues
b,z
Lightness
L
Colorfulness
C, S
Hue
H
Opponent a
a
Opponent b
b
Note: for the purposes of this classification,
the XYZ spaces are considered super-saturated RGB spaces.
Also, despite Saturation being Lightness-dependent,
it falls in the same category as Chroma here.
The Whiteness and Blackness components of HWB
have no analogs in other color spaces.
For example, if these two colors
are to be interpolated in Oklch,
the missing hue in the CIE LCH color
is analogous to the hue component of Oklch
and will be carried forward
while the missing blue component
in the second color
is not analogous to any Oklch component
and will not be carried forward:
If a color with a carried forward [=missing component=]
is interpolated
with another color
which is not missing that component,
the [=missing component=]
is treated as having
the other color's component value.
Therefore,
the carrying-forward step
must be performed before any
[=powerless component=] handling.
For example, if these two colors are interpolated,
the second of which has a missing hue:
oklch(78.3% 0.108 326.5)
oklch(39.2% 0.4 none)
Then the actual colors to be interpolated are
oklch(78.3% 0.108 326.5)
oklch(39.2% 0.4 326.5)
and not
oklch(78.3% 0.108 326.5)
oklch(39.2% 0.4 0)
If the carried forward [=missing component=]
is alpha, the color must be premultiplied with this carried forward value,
not with the zero value that would have resulted from color conversion.
For example, if these two colors are interpolated,
the second of which has a missing alpha:
giving the premultiplied Oklch values [0.3915, 0.054, 326] and [0.196, 0.2, 0].
If both colors are [=missing=]
a given component,
the interpolated color
will also be [=missing=] that component.
Interpolating with Alpha
When the colors to be interpolated are not fully opaque,
they are transformed into premultiplied color values
as follows:
* If the alpha value is ''none'', the premultiplied value is the un-premultiplied value. Otherwise,
* If any component value is ''none'', the premultiplied value is also ''none''.
* For [=rectangular orthogonal color=] coordinate systems, all component values are multiplied by the alpha value.
* For [=cylindrical polar color=] coordinate systems, the hue angle is not premultiplied, but the other two axes are premultiplied.
To obtain a color value from a premultipled color value,
* If the interpolated alpha value is zero or ''none'',
the un-premultiplied value is the premultiplied value. Otherwise,
* If any component value is ''none'',
the un-premultiplied value is also ''none''.
* otherwise, each component which had been premultiplied
is divided by the interpolated alpha value.
animations/color-transition-premultiplied.html
Why is premultiplied alpha useful?
Interpolating colors using the premultiplied representations
tends to produce more attractive transitions than the non-premultiplied representations,
particularly when transitioning from a fully opaque color to fully transparent.
Note that transitions where either the transparency or the color are held constant
(for example, transitioning between rgba(255, 0, 0, 100%) (opaque red)
and rgba(0,0,255,100%) (opaque blue),
or rgba(255,0,0,100%) (opaque red)
and rgba(255,0,0,0%) (transparent red))
have identical results whether the color interpolation is done in premultiplied or non-premultiplied color-space.
Differences only arise when both the color and transparency differ between the two endpoints.
The following example illustrates the difference between
a gradient transitioning via pre-multiplied values
(in this case sRGB, since all colors involved are legacy colors)
and one transitioning (incorrectly) via non-premultiplied values.
In both of these examples,
the gradient is drawn over a white background.
Both gradients could be written with the following value:
linear-gradient(90deg, red, transparent, blue)
With premultiplied colors,
transitions to or from "transparent" always look nice:
On the other hand,
if a gradient were to incorrectly transition in non-premultiplied space,
the center of the gradient would be a noticeably grayish color,
because "transparent" is actually a shorthand for ''rgba(0,0,0,0)'', or transparent black,
meaning that the red transitions to a black
as it loses opacity,
and similarly with the blue's transition:
For example, to interpolate, in the sRGB color space, the two sRGB colors
rgb(24% 12% 98% / 0.4)
and
rgb(62% 26% 64% / 0.6)
they would first be converted to premultiplied form
[9.6% 4.8% 39.2% ]
and
[37.2% 15.6% 38.4%]
before interpolation.
The midpoint of linearly interpolating these colors
would be [23.4% 10.2% 38.8%]
which, with an alpha value of 0.5,
is rgb(46.8% 20.4% 77.6% / 0.5)
when premultiplication is undone.
To interpolate, in the Lab color space, the two colors
rgb(76% 62% 03% / 0.4)
and
color(display-p3 0.84 0.19 0.72 / 0.6)
they are first converted to lab
lab(66.927% 4.873 68.622 / 0.4)
lab(53.503% 82.672 -33.901 / 0.6)
then the L, a and b coordinates are premultiplied before interpolation
[26.771% 1.949 27.449]
and
[32.102% 49.603 -20.341].
The midpoint of linearly interpolating these would be
[29.4365% 25.776 3.554]
which, with an alpha value of 0.5,
is lab(58.873% 51.552 7.108) / 0.5)
when premultiplication is undone.
To interpolate, in the chroma-preserving LCH color space, the same two colors
rgb(76% 62% 03% / 0.4)
and
color(display-p3 0.84 0.19 0.72 / 0.6)
they are first converted to LCH
lch(66.93% 68.79 85.94 / 0.4)
lch(53.5% 89.35 337.7 / 0.6)
then the L and C coordinates (but not H) are premultiplied before interpolation
[26.771% 27.516 85.94]
and
[32.102% 53.61 337.7].
The midpoint of linearly interpolating these,
along the ''shorter'' hue arc (the default) would be
[29.4365% 40.563 31.82]
which, with an alpha value of 0.5,
is lch(58.873% 81.126 31.82) / 0.5)
when premultiplication is undone.
There is sample JavaScript code
for alpha premultiplication and un-premultiplication,
for both polar and rectangular color spaces,
in [[#color-conversion-code]].
Hue Interpolation
For color functions with a hue angle (LCH, HSL, HWB etc), there are multiple ways to interpolate.
As arcs greater than 360° are rarely desirable,
hue angles are fixed up prior to interpolation
so that per-component interpolation is done over less than 360°, often less than 180°.
Host syntax can specify any of the following algorithms for hue interpolation
(angles in the following are in degrees, but the logic is the same regardless of how they are specified).
Specifying a hue interpolation strategy is already part of the <> syntax
via the <> token.
Unless otherwise specified, if no specific hue interpolation algorithm is selected by the host syntax, the default is ''shorter''.
color-mix-percents-01.html
color-mix-percents-02.html
Note: As a reminder,
if the interpolating colors were not already in the specified interpolation color space,
then converting them will turn any [=powerless components=] into [=missing components=].
shorter
Hue angles are interpolated to take the shorter of the two arcs
between the starting and ending hues.
For example, the midpoint when interpolating in Oklch from a red
oklch(0.6 0.24 30) to a yellow
oklch(0.8 0.15 90)
would be at a hue angle of 30 + (90 - 30) * 0.5 = 60 degrees,
along the shorter arc between the two colors,
giving a deep orange
oklch(0.7 0.195 60)
Angles are adjusted so that |θ₂ - θ₁| ∈ [-180, 180]. In pseudo-Javascript:
Hue angles are interpolated to take the longer of the two arcs
between the starting and ending hues.
For example, the midpoint when interpolating in Oklch from a red
oklch(0.6 0.24 30) to a yellow
oklch(0.8 0.15 90)
would be at a hue angle of (30 + 360 + 90) * 0.5 = 240 degrees,
along the longer arc between the two colors,
giving a sky blue
oklch(0.7 0.195 240)
Angles are adjusted so that |θ₂ - θ₁| ∈ {(-360, -180], [180, 360)}. In pseudo-Javascript:
Hue angles are interpolated so that,
as they progress from the first color to the second,
the angle is always increasing.
If the angle increases to 360 it is reset to zero,
and then continues increasing.
Depending on the difference between the two angles,
this will either look the same as shorter or as longer.
However, if one of the hue angles is being animated,
and the hue angle difference passes through 180 degrees,
the interpolation will not flip to the other arc.
For example, the midpoint when interpolating in Oklch from a deep brown
oklch(0.5 0.1 30) to a turquoise
oklch(0.7 0.1 190)
would be at a hue angle of (30 + 190) * 0.5 = 110 degrees,
giving a khaki
oklch(0.6 0.1 110).
However, if the hue of the second color is animated to
oklch(0.7 0.1 230),
the midpoint of the interpolation will be (30 + 230) * 0.5 = 130 degrees,
continuing in the same increasing direction,
giving another green
oklch(0.6 0.1 130)
rather than flipping to the opponent color part-way through the animation.
Angles are adjusted so that |θ₂ - θ₁| ∈ [0, 360). In pseudo-Javascript:
if (θ₂ < θ₁) {
θ₂ += 360;
}
decreasing
Hue angles are interpolated so that,
as they progress from the first color to the second,
the angle is always decreasing.
If the angle decreases to 0 it is reset to 360,
and then continues decreasing.
Depending on the difference between the two angles,
this will either look the same as shorter or as longer.
However, if one of the hue angles is being animated,
and the hue angle difference passes through 180 degrees,
the interpolation will not flip to the other arc.
For example, the midpoint when interpolating in Oklch from a deep brown
oklch(0.5 0.1 30) to a turquoise
oklch(0.7 0.1 190)
would be at a hue angle of (30 + 360 + 190) * 0.5 = 290 degrees,
giving a purple
oklch(0.6 0.1 290).
However, if the hue of the second color is animated to
oklch(0.7 0.1 230),
the midpoint of the interpolation will be (30 + 360 + 230) * 0.5 = 310 degrees,
continuing in the same decreasing direction,
giving another purple
oklch(0.6 0.1 310)
rather than flipping to the opponent color part-way through the animation.
Angles are adjusted so that |θ₂ - θ₁| ∈ (-360, 0]. In pseudo-Javascript:
if (θ₁ < θ₂) {
θ₁ += 360;
}
Gamut Mapping
An Introduction to Gamut Mapping
Note: This section provides important context for the specific requirements described elsewhere in the document.
This section is non-normative
When a color in an origin color space
is converted to another, destination color space
which has a smaller gamut,
some colors will be outside the destination gamut.
For intermediate color calculations,
these out of gamut values are preserved.
However, if the destination is the display device
(a screen, or a printer)
then out of gamut values must be converted to
an in-gamut color.
Gamut mapping is the process of finding an in-gamut color
with the least objectionable change in visual appearance.
Clipping
The simplest and least acceptable method
is simply to clip the component values
to the displayable range.
This changes the proportions of
the three primary colors (for an RGB display),
resulting in a hue shift.
For example,
consider the color color(srgb-linear 0.5 1 3).
Because this is a linear-light color space,
we can compare the intensities of the three components
and see that
the amount of blue light is three times the amount of green,
while the amount of red light is half that of green.
There is six times as much blue primary as red.
In Oklch, this color has a hue angle of 265.1°
If we now clip this color
to bring it into gamut for sRGB,
we get color(srgb-linear 0.5 1 1).
The amount of blue light is the same as green.
In Oklch, this color has a hue angle of 196.1°,
a substantial change of 69°.
Closest Color (MINDE)
A better method is to map colors,
in a perceptually uniform color space,
by finding the closest in-gamut color
(so-called minimum ΔE or MINDE).
Clearly, the success of this technique
depends on
the degree of uniformity of the gamut mapping color space
and the predictive accuracy of the deltaE function used.
However, when doing gamut mapping
changes in Hue are particularly objectionable;
changes in Chroma are more tolerable,
and
small changes in Lightness can also be acceptable
especially if the alternative is a larger Chroma reduction.
MINDE weights changes in each dimension equally,
and thus gives suboptimal results.
Chroma Reduction
To improve on MINDE algorithms,
colors are mapped in a perceptually uniform, polar color space
by holding the hue constant,
and reducing the chroma until the color falls in gamut.
In this example, Display P3 primary yellow
(color(display-p3 1 1 0))
is being mapped to an sRGB display.
The gamut mapping color space is Oklch.
color(display-p3 1 1 0)
is
color(srgb 1 1 -0.3463)
which is
color(oklch 0.96476 0.24503 110.23)
By progressively reducing the chroma component
until the resulting color falls inside the sRGB gamut
(has no components negative, or greater than one)
a gamut mapped color is obtained.
color(oklch 0.96476 0.21094 110.23)
which is
color(srgb 0.99116 0.99733 0.00001)
A practical implementation will converge more quickly than a linear reduction;
either by binary search,
or by computing the geometric intersection
of the line of constant hue and lightness with the gamut boundary.
Excessive Chroma Reduction
Also, this simple approach will give sub-optimal results
for certain colors, principally very light colors
like yellow and cyan,
if the upper edge of the gamut boundary is shallow,
or even slightly concave.
The line of constant lightness can skim just above the gamut boundary,
resulting in an excessively low chroma in those cases.
The choice of color space will affect the acceptability of the gamut mapped colors.
In this example, Display P3 primary yellow (color(display-p3 1 1 0)
has the chroma progressively reduced in CIE LCH color space.
It can be seen that reduction in CIE LCH chroma makes the red
intensity curve up, out of Display P3 gamut;
by the time it falls again the chroma is very low.
Simple gamut mapping in CIE LCH would give unsatisfactory results.
In this example, Display P3 primary yellow (color(display-p3 1 1 0)
has the chroma progressively reduced, but this time in Oklch color space.
It can be seen that reduction in Oklch chroma is better behaved.
Colors do not go outside the Display P3 gamut, and the resulting
gamut-mapped yellow has good chroma.
Simple gamut mapping in OK LCH would give acceptable results.
Chroma Reduction with Local Clipping
The simple chroma-reduction algorithm can be improved:
at each step,
the color difference is computed between the current mapped color
and a clipped version of that color.
If the current color is outside the gamut boundary,
but the color difference between it and the clipped version
is below the threshold for a just noticeable difference (JND),
the clipped version of the color is returned as the mapped result.
Effectively, this is doing a MINDE mapping at each stage,
but constrained so the hue and lightness changes
are very small,
and thus are not noticeable.
In this example, Display P3 primary yellow (color(display-p3 1 1 0)
has the chroma progressively reduced in CIE LCH color space,
with the local clip modification.
It can be seen that reduction in CIE LCH chroma still makes the red
intensity curve up, out of Display P3 gamut;
but less than before and the sRGB boundary is found much more quickly.
Gamut mapping in CIE LCH with local clip would give acceptable results.
In this example, Display P3 primary yellow (color(display-p3 1 1 0)
has the chroma progressively reduced, but this time in Oklch color space
and with the local clip modification.
It can be seen that reduction in Oklch chroma,
which was already good,
is further improved by the local clip modification.
Simple gamut mapping in CIE LCH with local clip would give excellent results.
Deviations from Perceptual Uniformity: Hue Curvature
Using the CIE LCH color space
and deltaE2000 distance metric,
is known to give suboptimal results
with significant hue shifts,
for colors in the hue range
270° to 330°.
Using Oklch color space
and deltaEOK distance metric
avoids this issue
at all hue angles.
CSS Gamut Mapping to an RGB Destination
The CSS gamut mapping algorithm
applies to individual,
Standard Dynamic Range (SDR) CSS colors
which are out of gamut
of an RGB display
and thus require to be css gamut mapped.
It implements a relative colorimetric intent,
and colors inside the destination gamut are unchanged.
Note: other situations,
in particular mapping to printer gamuts
where the maximum black level is significantly above zero,
will require different algorithms
which align the respective black and white points,
which will result in lightness changes
for very light and very dark colors
as chroma is reduced..
Note: this algorithm is for individual, distinct colors;
for color images,
where relationships between neighboring pixels are important
and the aim is to preserve detail and texture,
a perceptual rendering intent is more appropriate
and in that case,
colors inside the destination gamut
could be changed.
CSS gamut mapping occurs in the Oklch color space,
and the color difference formula used is deltaEOK.
The local-MINDE improvement is used.
For colors which are out of range on the Lightness axis,
white is returned in the destination color space
if the Lightness is greater than or equal to 1.0,
while black is returned in the destination color space
if the Lightness is less than or equal to 0.0.
For the binary search implementation,
at each step in the search,
the deltaEOK is computed between the current mapped color
and a clipped version of that color.
If the current color is outside the gamut boundary,
but the deltaEOK between it and the clipped version
is below a threshold for a just noticeable difference (JND),
the clipped version of the color is returned as the mapped result.
For the geometric implementation,
having found the exact intersection,
project outwards (towards higher chroma) along the line of constant lightness
until either:
- the deltaEOK between the projected point
and a clipped version of that point
exceeds one JND, or
- the chroma of the projected point is equal to
the chroma of the original color (i.e. do not project past the original color)
Then return the clipped version of the color as the mapped result.
For the Oklch color space,
one JND is is an Oklch difference of 0.02.
Note: In CIE Lab color space,
where the range of the Lightness component is 0 to 100,
using deltaE2000,
one JND is 2.
Because the range of Lightness in Oklab and Oklch
is 0 to 1,
using deltaEOK,
one JND is 100 times smaller.
Sample Pseudocode for the Binary Search Gamut Mapping Algorithm with Local MINDE
To CSS gamut map a color |origin|
in color space |origin color space|
to be in gamut of a destination color space |destination|:
if |destination| has no gamut limits (XYZ-D65, XYZ-D50, Lab, LCH, Oklab, Oklch) convert |origin| to |destination| and return it as the gamut mapped color
let |origin_Oklch| be |origin| converted
from |origin color space| to the Oklch color space
if the Lightness of |origin_Oklch| is greater than or equal to 100%,
convert `oklab(1 0 0 / origin.alpha)` to |destination| and return it as the gamut mapped color
if the Lightness of |origin_Oklch| is less than than or equal to 0%,
convert `oklab(0 0 0 / origin.alpha)` to |destination| and return it as the gamut mapped color
let inGamut(|color|) be a function which returns true if, when passed a color,
that color is inside the gamut of |destination|.
For HSL and HWB, it returns true if the color is inside the gamut of sRGB.
if inGamut(|origin_Oklch|) is true, convert |origin_Oklch| to |destination| and return it as the gamut mapped color
otherwise, let delta(|one|, |two|) be a function which returns the deltaEOK of color |one| compared to color |two|
let |JND| be 0.02
let |epsilon| be 0.0001
let clip(|color|) be a function which converts |color| to |destination|,
clamps each component to the bounds of the reference range for that component
and returns the result
set |current| to |origin_Oklch|
set |clipped| to clip(|current|)
set |E| to delta(|clipped|, |current|)
if |E| < |JND|
return |clipped| as the gamut mapped color
set |min| to zero
set |max| to the Oklch chroma of |origin_Oklch|
let |min_inGamut| be a boolean that represents when |min| is still in gamut, and set it to true
while (|max| - |min| is greater than |epsilon|) repeat the following steps
set |chroma| to (|min| + |max|) /2
set the chroma component of |current| to |chroma|
if |min_inGamut| is true and also if inGamut(|current|) is true, set |min| to |chroma| and continue to repeat these steps
otherwise, carry out these steps:
set |clipped| to clip(|current|)
set |E| to delta(|clipped|, |current|)
if |E| < |JND|
if (|JND| - |E| < |epsilon|) return |clipped| as the gamut mapped color
otherwise,
set |min_inGamut| to false
set |min| to |chroma|
otherwise, set |max| to |chroma| and continue to repeat these steps
return |clipped| as the gamut mapped color
Resolving <> Values
Unless otherwise specified for a particular property,
[=specified value|specified=] colors are resolved to
[=computed value|computed=] colors
and then further to [=used value|used=] colors as described below.
The [=resolved value=] of a <> is its [=used value=].
parsing/color-computed-hex-color.html
parsing/color-computed-named-color.html
parsing/color-invalid-hex-color.html
parsing/color-invalid-named-color.html
system-color-compute.html
Resolving sRGB values
This applies to:
* [=hex colors=]
* ''rgb()'' and ''rgba()'' values
* ''hsl()'' and ''hsla()'' values
* ''hwb()'' values
* [=named colors=]
* [=system colors=]
* deprecated-colors
It does not apply to:
* ''color()'' values using the ''srgb'' or ''srgb-linear'' [=color space=]s.
If the sRGB color was explicitly specified by the author as a [=named color=],
or as a [=system color=],
the [=declared value=] is that named or system color, converted to
ASCII lowercase.
The computed and used value
is the corresponding sRGB color,
paired with the specified alpha channel
(after clamping to [0, 1])
and defaulting to opaque if unspecified).
The author-provided mixed-case form below has a declared value in all lowercase.
pUrPlE
purple
Otherwise, the declared, computed and used value
is the corresponding sRGB color,
paired with the specified alpha channel
(after clamping to [0, 1])
and defaulting to opaque if unspecified).
For historical reasons, when ''calc()'' in sRGB colors
resolves to a single value,
the declared value serialises without the "calc(" ")" wrapper.
For example, if a color is given as
rgb(calc(64 * 2) 127 255)
the declared value will be
rgb(128 127 255)
and not
rgb(calc(128) 127 255).
For example, if a color is given as
hsl(38.82 calc(2 * 50%) 50%)
the declared value will be
rgb(255 165.2 0)
because the ''calc()'' is lost
during HSL to RGB conversion.
Also for historical reasons,
when calc() is simplified down to a single value,
the color values are clamped to [0.0, 255.0].
For example, if a color is given as
rgb(calc(100 * 4) 127 calc(20 - 35))
the declared value will be
rgb(255 127 0)
and not
rgb(calc(400) 127 calc(-15)).
This clamping also takes care of values such as ''Infinity'', ''-Infiinity'', and ''NaN'' which will clamp at 255, 0 and 0 respectively.
This applies to ''lab()'' and ''lch()'' values.
The declared, computed and used value
is the corresponding CIE Lab or LCH color
(after clamping of L, C and H)
paired with the specified alpha channel
(as a <>, not a <>;
and defaulting to opaque if unspecified).
For example, the computed value of
lch(52.2345% 72.2 56.2 / 1)
is
lch(52.2345% 72.2 56.2)
Resolving Oklab and Oklch values
This applies to ''oklab()'' and ''oklch()'' values.
The declared, computed and used value
is the corresponding Oklab or Oklch color
(after clamping of L, C and H)
paired with the specified alpha channel
(as a <>, not a <>;
and defaulting to opaque if unspecified).
For example, the computed value of
oklch(42.1% 0.192 328.6 / 1)
is
oklch(42.1% 0.192 328.6)
Resolving values of the ''color()'' function
The declared, computed and used value
is the color in the specified [=color space=],
paired with the specified alpha channel
(as a <>, not a <>;
and defaulting to opaque if unspecified).
For example, the computed value of
color(display-p3 0.823 0.6554 0.2537 /1)
is
color(display-p3 0.823 0.6554 0.2537)
For colors specified in the ''xyz'' [=color space=],
which is an alias of the ''xyz-d65'' [=color space=],
the computed and used value
is in the ''xyz-d65'' [=color space=].
For example, the computed value of
color(xyz 0.472 0.372 0.131)
is
color(xyz-d65 0.472 0.372 0.131)
Resolving other colors
This applies to [=system colors=]
(including the <>s),
''transparent'',
and ''color>/currentcolor''.
The declared value for each <> keyword
and <> keyword
is itself.
The computed value
is the corresponding color in its color space.
However, such colors must not be altered by
'forced colors mode'.
The declared value of the color property is "ButtonText"
while the computed value could be, for example,
rgb(0, 0, 0).
The declared value of ''transparent'' is "transparent"
while the computed and used value is [=transparent black=].
The ''currentcolor'' keyword computes to itself.
In the 'color' property,
the used value of ''color>/currentcolor'' is the [=inherited value=].
In any other property,
its used value is the used value of the 'color' property on the same element.
Note: This means that if the ''/currentcolor'' value is inherited,
it’s inherited as a keyword,
not as the value of the 'color' property,
so descendants will use their own 'color' property to resolve it.
For example, given this html:
<div>
<p>Assume this example text is long enough
to wrap on multiple lines.
</p>
</div>
and this css:
div {
color: forestgreen;
text-shadow: currentColor;
}
p {
color: mediumseagreen;
}
p::firstline {
color: yellowgreen;
}
The used value of the inherited property text-shadow
on the first line fragment would be yellowgreen.
This section updates and replaces that part of CSS Object Model, section
Serializing CSS Values, which relates to serializing <> values.
In this section, the strings used in the specification and the corresponding characters are as follows.
String
Character(s)
" "
U+0020 SPACE
","
U+002C COMMA
"-"
U+002D HYPHEN-MINUS
"."
U+002E FULL STOP
"/"
U+002F SOLIDUS
"none"
U+006E LATIN SMALL LETTER N
U+006F LATIN SMALL LETTER O
U+006E LATIN SMALL LETTER N
U+0065 LATIN SMALL LETTER E
The string "." shall be used as a decimal separator,
regardless of locale,
and there shall be no thousands separator.
For syntactic forms which support [=missing color components=],
the value ''none'' (equivalently NONE, nOnE, etc),
shall be serialized in all-lowercase
as the string "none".
Serializing alpha values
This applies to any <> value which can take an optional alpha value.
It does not apply to the ''opacity'' property.
If, after clamping to the range [0, 1] the alpha is 1,
it is omitted from the serialization;
an implicit value of 1 (fully opaque) is the default.
If the alpha is any other value than 1,
it is explicitly included in the serialization as described below.
If the value is internally represented as an integer
between 0 and 255 inclusive (i.e. 8-bit unsigned integer),
follow these steps:
Let alpha be the given integer.
If there exists an integer between 0 and 100 inclusive that,
when multiplied with 2.55 and rounded to the closest integer
(rounding up if two values are equally close), equals alpha,
let rounded be that integer divided by 100.
Otherwise, let rounded be alpha
divided by 0.255 and rounded to the closest integer
(rounding up if two values are equally close),
divided by 1000.
Return the result of serializing rounded
as a <>.
Otherwise, return the result of serializing the given value
(as a <>, not a <>).
For example,
if the alpha is stored as the 8-bit unsigned integer 237,
the integer 93 satisfies the criterion
because Math.round(93 * 2.55) is 237,
and so the alpha is serialized as "0.93".
However,
if the alpha is stored as the 8-bit unsigned integer 236,
there is no such integer
(92 maps to 235 while 94 maps to 240),
and so since 236 ÷ 0.255 = 925.490196078
the alpha is serialized as "0.92549"
(no more than 6 figures, trailing zeroes omitted).
The <> value is expressed in base ten,
with the "." character as decimal separator.
The leading zero must not be omitted.
Trailing zeroes must be omitted.
For example, an alpha value of 70%
will be serialized as the string
"0.7"
which has a leading zero before the decimal separator,
"." as decimal separator
(even if the current locale would use some other character,
such as ","),
and all digits after the "7" would be "0" and are omitted.
The precision with which alpha values are retained,
and thus the number of decimal places in the serialized value,
is not defined in this specification,
but must at least be sufficient
to round-trip integer percentage values.
Thus, the serialized value must contain
at least two decimal places
(unless trailing zeroes have been removed).
Values must be
rounded towards +∞, not truncated.
For example, an alpha value of 12.3456789%
could be serialized as the strings
"0.12" or "0.123" or "0.1234" or "0.12346"
(rounding the value of 5
towards +∞
because the following digit is 6)
or any longer, rounded serialization of the same form.
Because <>s which were specified outside the valid range
are clamped at parse time, the declared value will be clamped.
However, per [[css-values-4#calc-range]], <>s
specified using calc() are not clamped when the specified form is serialized;
but the computed values are clamped.
For example an alpha value which was specified directly as 120%
would be serialized as the string "1".
However, if it was specified as calc(2*60%)
the declared value would be serialized as the string "calc(1.2)".
Serializing sRGB values
The serialized form of the following sRGB values:
* [=hex colors=]
* ''rgb()'' and ''rgba()'' values
* ''hsl()'' and ''hsla()'' values
* ''hwb()'' values
* [=named colors=]
* [=system colors=]
* deprecated-colors
* ''transparent''
is derived from the [=declared value=].
When serializing the value of a property
which was set by the author to a CSS [=named color=],
a [=system color=],
a deprecated-color,
or ''transparent''
therefore, for the [=declared value=],
the ASCII lowercase
keyword value is retained.
For the computed and used value,
the corresponding sRGB value is used.
system-color-compute.html
Thus, the serialized declared value of ''transparent'' is the string "transparent",
while the serialized computed value of ''transparent'' is the string "rgba(0, 0, 0, 0)".
For all other sRGB values,
the declared, computed and used value
is the corresponding sRGB value.
Corresponding sRGB values use either the ''rgb()'' or ''rgba()'' form
(depending on whether the (clamped) alpha is exactly 1, or not),
with all ASCII lowercase
letters for the function name.
During serialization,
any [=missing=] values
are converted to 0.
For compatibility, the sRGB component values
are serialized in <> form, not <>.
Also for compatibility,
the component values are serialized in base 10,
with a range of [0-255], regardless of
the bit depth with which they are stored.
As noted earlier,
unitary alpha values are not explicitly serialized.
Also, for compatibility, if the alpha is exactly 1,
the ''rgb()'' form is used,
with an implicit alpha;
otherwise, the ''rgba()'' form is used,
with an explicit alpha value.
For compatibility,
the legacy form with comma separators is used;
exactly one ASCII space follows each comma.
This includes the comma (not slash) used
to separate the blue component of ''rgba()''
from the alpha value.
Note: contrary to CSS Color 3,
the parameters of the ''rgb()'' function
are of type <>, not <>.
Thus, any higher precision than eight bits
is indicated with a fractional part.
The precision with which sRGB component values are retained,
and thus the number of significant figures in the serialized value,
is not defined in this specification,
but must at least be sufficient
to round-trip eight bit values.
Values must be rounded towards +∞, not truncated.
Note: authors of scripts which expect
color values returned from
getComputedStyle
to have <> component values,
are advised to update them to also cope with
<>.
For example,
rgb(146.064 107.457 131.223)
is now valid, and equal to
rgb(57.28% 42.14% 51.46%)
A conformant serialized form for both,
is the string "rgb(146.06, 107.46, 131.2)".
Trailing fractional zeroes in any component values must be omitted;
if the fractional part consists of all zeroes,
the decimal point must also be omitted.
This means that sRGB colors specified with integer component values
will serialize with backwards-compatible integer values.
The serialized computed value of
''goldenrod''
is the string "rgb(218, 165, 32)"
and not the string "rgb(218.000, 165.000, 32.000)"
Serializing Lab and LCH values
The serialized form of ''lch()'' and ''lab()'' values
is derived from the [=computed value=]
and uses the ''lab()'' or ''lch()'' forms,
with ASCII lowercase
letters for the function name.
The component values are serialized in base 10;
the L, a, b and C component values
are serialized as <>,
using the Lab percentage reference ranges
or the LCH percentage reference ranges
as appropriate
to perform percentage to number conversion;
thus 0% L maps to 0
and 100% L maps to 100.
A single ASCII space character " "
must be used as the separator
between the component values.
parsing/color-computed.html
The serialized value of
lab(56.200% 0.000 83.600)
is the string "lab(56.2 0 83.6)"
The serialized value of
lab(56.200% 0.000 66.88%)
is the string "lab(56.2 0 83.6)"
Trailing fractional zeroes in any component values must be omitted;
if the fractional part consists of all zeroes,
the decimal point must also be omitted.
The serialized value of
lch(37% 105.0 305.00)
is the string "lch(37 105 305)",
not "lch(37 105.0 305.00)".
The precision with which ''lab()'' component values are retained,
and thus the number of significant figures in the serialized value,
is not defined in this specification,
but due to the wide gamut must be sufficient
to round-trip L values between 0 and 100,
and a and b values between ±127,
with at least sixteen bit precision;
this will result in at least three decimal places
unless trailing zeroes have been omitted.
(half float or float, is recommended for internal storage).
Values must be rounded towards +∞, not truncated.
Note: a and b values outside ±125 are possible
with ultrawide gamut spaces. For example, all
of the ''prophoto-rgb'' primaries and secondaries
exceed this range, but are within ±200.
As noted earlier,
unitary alpha values are not explicitly serialized.
Non-unitary alpha values must be explicitly serialized,
and the string " / "
(an ASCII space, then forward slash, then another space)
must be used to separate the b component value from the alpha value.
The serialized value of
lch(56.2% 83.6 357.4 /93%)
is the string "lch(56.2 83.6 357.4 / 0.93)"
not "lch(56.2% 83.6 357.4 / 0.93)"
Serializing Oklab and Oklch values
The serialized form of ''oklch()'' and ''oklab()'' values
is derived from the [=computed value=]
and uses the ''oklab()'' or ''oklch()'' forms,
with ASCII lowercase
letters for the function name.
The component values are serialized in base 10;
the L, a, b and C component values
are serialized as <>
using the Oklab percentage reference ranges
or the Oklch percentage reference ranges
as appropriate
to perform percentage to number conversion;
thus 0% L maps to 0
and 100% L maps to 1.0.
A single ASCII space character " "
must be used as the separator
between the component values.
parsing/color-computed.html
The serialized value of
oklab(54.0% -0.10 -0.02)
is the string "oklab(0.54 -0.1 -0.02)"
not "oklab(54 -0.1 -0.02)" or
"oklab(54% -0.1 -0.02)"
The serialized value of
oklab(54.0 -25% -5%)
is the string "oklab(0.54 -0.1 -0.02)"
not "oklab(54 -0.25 -0.05)"
Trailing fractional zeroes in any component values must be omitted;
if the fractional part consists of all zeroes,
the decimal point must also be omitted.
The serialized value of
oklch(56.43% 0.0900 123.40)
is the string "oklch(0.5643 0.09 123.4)",
not "oklch(0.5643 0.0900 123.40)".
The precision with which ''oklab()'' component values are retained,
and thus the number of significant figures in the serialized value,
is not defined in this specification,
but due to the wide gamut must be sufficient
to round-trip L values between 0 and 1 (0% and 100%),
and a, b and C values between ±0.5,
with at least sixteen bit precision;
this will result in at least five decimal places
unless trailing zeroes have been omitted.
(half float or float, is recommended for internal storage).
Values must be rounded towards +∞, not truncated.
Note: a, b and C values outside ±0.5 are possible
with ultrawide gamut spaces. For example,
the ''prophoto-rgb'' green and blue primaries
exceed this range,
with C of 0.526 and 1.413 respectively.
As noted earlier,
unitary alpha values are not explicitly serialized.
Non-unitary alpha values must be explicitly serialized,
and the string " / "
(an ASCII space, then forward slash, then another space)
must be used to separate the final color component (b, or C) value from the alpha value.
The serialized value of
oklch(53.85% 0.1725 320.67 / 70%)
is the string "oklch(0.5385 0.1725 320.67 / 0.7)"
Serializing values of the ''color()'' function
The serialized form of ''color()'' values
is derived from the [=computed value=]
and uses the ''color()'' form,
with ASCII lowercase
letters for the function name
and the color space name.
The component values are serialized in base 10,
as <>.
A single ASCII space character " "
must be used as the separator
between the component values,
and also between the color space name and the first color component.
parsing/color-computed.html
The serialized value of
color(dIsPlAy-P3 0.964 0.763 0.787)
is the string "color(display-p3 0.96 0.76 0.79)",
if two decimal places are retained.
Notice that 0.787 has rounded up to 0.79,
rather than being truncated to 0.78.
Trailing fractional zeroes in any component values must be omitted;
if the fractional part consists of all zeroes,
the decimal point must also be omitted.
The serialized value of
color(rec2020 0.400 0.660 0.340)
is the string "color(rec2020 0.4 0.66 0.34)",
not "color(rec2020 0.400 0.660 0.340)".
If the color space is sRGB, the color space is still explicitly required in the serialized result.
For the predefined color spaces,
the minimum precision for round-tripping is as follows:
color space
Minimum bits
''srgb''
10
''srgb-linear''
12
''display-p3''
10
''a98-rgb''
10
''prophoto-rgb''
12
''rec2020''
12
''xyz'', ''xyz-d50'', ''xyz-d65''
16
(16bit, half-float, or float per component
is recommended for internal storage).
Values must be rounded towards +∞, not truncated.
Note: compared to the legacy forms
such as ''rgb()'', ''hsl()'' and so on,
''color(srgb)'' has a higher minimum precision requirement.
Stylesheet authors who prefer higher precision
are thus encouraged to use the ''color(srgb)'' form.
As noted earlier,
unitary alpha values are not explicitly serialized.
Non-unitary alpha values must be explicitly serialized,
and the string " / "
(an ASCII space, then forward slash, then another space)
must be used to separate
the final color component value
from the alpha value.
The serialized value of
color(prophoto-rgb 0.2804 0.40283 0.42259/85%)
is the string "color(prophoto-rgb 0.28 0.403 0.423 / 0.85)",
if three decimal places are retained.
Serializing other colors
This applies to
''currentcolor''.
The serialized form of this value
is derived from the [=computed value=]
and uses ASCII lowercase
letters for the color name.
The serialized form of ''currentColor'' is the string "currentcolor".
Default Style Rules
The following stylesheet is informative, not normative. This style sheet could be used by an implementation as part of its default styling of HTML documents.
/* traditional desktop user agent colors for hyperlinks */
:link { color: LinkText; }
:visited { color: VisitedText; }
:active { color: ActiveText; }
Sample code for Color Conversions
This section is not normative.
For clarity, a library is used for matrix multiplication.
(This is more readable than inlining all the multiplies and adds).
The matrices are in column-major order.
path: conversions.js
highlight: js
Sample Code for ΔE2000 and ΔEOK Color Differences
This section is not normative.
ΔE2000
The simplest color difference metric, ΔE76,
is simply the Euclidean distance in Lab color space.
While this is a good first approximation,
color-critical industries such as printing and fabric dyeing
soon developed improved formulae.
Currently, the most widely used formula
is ΔE2000.
It corrects a number of known asymmetries and non-linearities
compared to ΔE76.
Because the formula is complex,
and critically dependent on the sign
of various intermediate calculations,
implementations are often incorrect [[Sharma]].
The sample code below has been
validated
to five significant figures
against the test suite of paired Lab values and expected ΔE2000
published by [[Sharma]] and is correct.
path: deltaE2000.js
highlight: js
ΔEOK
Because Oklab does not suffer from
the hue linearity, hue uniformity,
and chroma non-linearities of CIE Lab,
the color difference metric does not need to correct for them
and so
is simply the Euclidean distance in Oklab color space.
path: deltaEOK.js
highlight: js
Appendix A: Deprecated CSS System Colors
Earlier versions of CSS defined several additional [=system colors=].
These color keywords have been deprecated, however,
as they are insufficient for their original purpose
(making website elements look like their native OS counterparts),
represent a security risk
by making it easier for a webpage to “spoof” a native OS dialog,
and increase fingerprinting surface, compromising user privacy.
User agents must support these keywords,
and to mitigate fingerprinting
must map them to the (undeprecated) [=system colors=]
as listed below.
Authors must not use these keywords.
The deprecated system colors are represented
as the <> sub-type,
and are defined as:
ActiveBorder
Active window border. Same as ''ButtonBorder''.
ActiveCaption
Active window caption. Same as ''Canvas''.
AppWorkspace
Background color of multiple document interface. Same as ''Canvas''.
Background
Desktop background. Same as ''Canvas''.
ButtonHighlight
The color of the border facing the light source for 3-D elements
that appear 3-D due to one layer of surrounding border. Same as ''ButtonFace''.
ButtonShadow
The color of the border away from the light source for 3-D elements
that appear 3-D due to one layer of surrounding border. Same as ''ButtonFace''.
CaptionText
Text in caption, size box, and scrollbar arrow box. Same as ''CanvasText''.
InactiveBorder
Inactive window border. Same as ''ButtonBorder''.
InactiveCaption
Inactive window caption. Same as ''Canvas''.
InactiveCaptionText
Color of text in an inactive caption. Same as ''GrayText''.
InfoBackground
Background color for tooltip controls. Same as ''Canvas''.
InfoText
Text color for tooltip controls. Same as ''CanvasText''.
Menu
Menu background. Same as ''Canvas''.
MenuText
Text in menus. Same as ''CanvasText''.
Scrollbar
Scroll bar gray area. Same as ''Canvas''.
ThreeDDarkShadow
The color of the darker (generally outer) of the two borders away
from the light source for 3-D elements that appear 3-D due to two
concentric layers of surrounding border. Same as ''ButtonBorder''.
ThreeDFace
The face background color for 3-D elements that appear 3-D due to
two concentric layers of surrounding border. Same as ''ButtonFace''.
ThreeDHighlight
The color of the lighter (generally outer) of the two borders facing
the light source for 3-D elements that appear 3-D due to two
concentric layers of surrounding border. Same as ''ButtonBorder''.
ThreeDLightShadow
The color of the darker (generally inner) of the two borders facing
the light source for 3-D elements that appear 3-D due to two
concentric layers of surrounding border. Same as ''ButtonBorder''.
ThreeDShadow
The color of the lighter (generally inner) of the two borders away
from the light source for 3-D elements that appear 3-D due to two
concentric layers of surrounding border. Same as ''ButtonBorder''.
When CSS is being parsed in [=quirks mode=],
<> is a type of <>
that is only valid in certain properties:
* 'background-color'
* 'border-color'
* 'border-top-color'
* 'border-right-color'
* 'border-bottom-color'
* 'border-left-color'
* 'color'
It is not valid in properties that include or reference these properties,
such as the 'background' shorthand,
or inside [=functional notations=]
such as ''color-mix()''
Additionally, while <> must be valid as a <>
when parsing the affected properties in the ''@supports'' rule,
it is not valid for those properties
when used in the {{CSS/supports(conditionText)|CSS.supports()}} method.
A <> can be represented
as a <>, <>, or <>,
according to the following rules:
* If it's an <>,
the token's representation must contain exactly 3 or 6 characters,
all hexadecimal digits.
It represents a <> with the same value.
* If it's a <>,
it must have its integer flag set.
Serialize the integer's value.
If the serialization has less than 6 characters,
prepend "0" characters to it until it is 6 characters long.
It represents a <> with the same value.
* If it's a <>,
it must have its integer flag set.
Serialize the integer's value,
and append the representation of the token's unit.
If the result has less than 6 characters,
prepend "0" characters to it until it is 6 characters long.
It represents a <> with the same value.
(In other words, Quirks Mode allows hex colors to be written without the leading "#",
but with weird parsing rules.)
Acknowledgments
In addition to those
who contributed to CSS Color 3, the editors would like to thank
Emilio Cobos Álvarez, Alexey Ardov, Chris Bai, Amelia Bellamy-Royds, Lars Borg, Mike Bremford, Andreu Botella, Dan Burzo, Max Derhak, fantasai, Simon Fraser, Devon Govett, Phil Green, Dean Jackson, Andreas Kraushaar, Pierre-Anthony Lemieux, Tiaan Louw, Cameron McCormack, Romain Menke, Chris Murphy, Isaac Muse, Jonathan Neal, Chris Needham, Björn Ottosson, Christoph Päper, Brad Pettit, Xidorn Quan, Craig Revie, Melanie Richards, Florian Rivoal, Jacob Rus, Joseph Salowey, Simon Sapin, Igor Snitkin, Lea Verou, Mark Watson, James Stuckey Weber, Sam Weinig, and Natalie Weizenbaum.
at-color-profile-001.html
color-mix-currentcolor-001.html
color-mix-currentcolor-002.html
color-mix-currentcolor-003.html
color-contrast-001.html
color-mix-basic-001.html
color-mix-non-srgb-001.html
color-mix-percents-01.html
color-mix-percents-02.html
composited-filters-under-opacity.html
light-dark-basic.html
light-dark-currentcolor.html
light-dark-inheritance.html
light-dark-currentcolor-in-color.html
t31-color-currentColor-b.xht
t31-color-text-a.xht
t41-html4-keywords-a.xht
t421-rgb-clip-outside-gamut-b.xht
t421-rgb-func-int-a.xht
t421-rgb-func-no-mixed-f.xht
t421-rgb-func-pct-a.xht
t421-rgb-func-whitespace-b.xht
t421-rgb-hex-parsing-f.xht
t421-rgb-hex3-a.xht
t421-rgb-hex3-expand-b.xht
t421-rgb-hex6-a.xht
t421-rgb-values-meaning-b.xht
t422-rgba-a0.0-a.xht
t422-rgba-a0.6-a.xht
t422-rgba-a1.0-a.xht
t422-rgba-clamping-a0.0-b.xht
t422-rgba-clamping-a1.0-b.xht
t422-rgba-clip-outside-device-gamut-b.xht
t422-rgba-func-int-a.xht
t422-rgba-func-no-mixed-f.xht
t422-rgba-func-pct-a.xht
t422-rgba-func-whitespace-b.xht
t422-rgba-values-meaning-b.xht
t423-transparent-1-a.xht
t423-transparent-2-a.xht
t424-hsl-basic-a.xht
t424-hsl-clip-outside-gamut-b.xht
t424-hsl-h-rotating-b.xht
t424-hsl-parsing-f.xht
t424-hsl-values-b-1.html
t424-hsl-values-b-10.html
t424-hsl-values-b-11.html
t424-hsl-values-b-12.html
t424-hsl-values-b-13.html
t424-hsl-values-b-14.html
t424-hsl-values-b-15.html
t424-hsl-values-b-2.html
t424-hsl-values-b-3.html
t424-hsl-values-b-4.html
t424-hsl-values-b-5.html
t424-hsl-values-b-6.html
t424-hsl-values-b-7.html
t424-hsl-values-b-8.html
t424-hsl-values-b-9.html
t425-hsla-basic-a.xht
t425-hsla-clip-outside-device-gamut-b.xht
t425-hsla-h-rotating-b.xht
t425-hsla-parsing-f.xht
t425-hsla-values-b.xht
t43-svg-keywords-a.xht
t44-currentcolor-background-b.xht
t44-currentcolor-border-b.xht
t44-currentcolor-inherited-c.xht
animation/color-composition.html
animation/color-interpolation.html
animation/opacity-interpolation.html
color-mix-basic-001.html
color-mix-currentcolor-visited.html
color-mix-currentcolor-visited-getcomputedstyle.html
nested-color-mix-with-currentcolor.html
relative-currentcolor-a98rgb-01.html
relative-currentcolor-displayp3-01.html
relative-currentcolor-hsl-01.html
relative-currentcolor-hsl-02.html
relative-currentcolor-hwb-01.html
relative-currentcolor-lab-01.html
relative-currentcolor-lch-01.html
relative-currentcolor-oklab-01.html
relative-currentcolor-oklch-01.html
relative-currentcolor-prophoto-01.html
relative-currentcolor-rec2020-01.html
relative-currentcolor-rec2020-02.html
relative-currentcolor-rgb-01.html
relative-currentcolor-rgb-02.html
relative-currentcolor-xyzd50-01.html
relative-currentcolor-xyzd65-01.html
parsing/color-mix-out-of-gamut.html
parsing/color-invalid.html
parsing/color-computed-color-mix-function.html
parsing/color-invalid-color-mix-function.html
parsing/color-valid-color-mix-function.html
parsing/color-computed-relative-color.html
parsing/color-invalid-relative-color.html
parsing/color-valid-relative-color.html
parsing/color-computed-color-contrast-function.html
parsing/color-invalid-color-contrast-function.html
parsing/color-valid-color-contrast-function.html
parsing/relative-color-out-of-gamut.html
Added steps for serializing a uint8_t alpha, moved from cssom-1
Restored parse-time clamping of HSL negative saturation to 0, which is current interop behavior from CSS Color 3
When interpolating, always convert colorspace, so that powerless components become missing
Clarified when alpha 1 is omitted from serialization
Removed redundant constraining of hue angles to [0,360] as this is already done.
Corrected description of ActiveCaption, which is a background.
Disambiguated opacity and alpha. Opacity property now uses opacity-value (which has different clamping behavior to alpha-value)
Clarified that carrying forward happens before premultiplication
Updated gamut mapping algorithm
Fixed a few issues regarding hue interpolation
Clarified that HWB white or black at 100% is insufficient criterion for an achromatic color; it is the sum which matters.
Avoid returning negative saturation in rgb to hsl conversion; adjust the hue to point to "the other side" instead
Use 64 bit accurate matrices for ProPhoto, which does not have a rational form
Oklab matrices recalculated for 64bit precision (returns same results as before, at 32 bit precision)
Consistently return output of GMA in the destination color space, even if no mapping is performed because the destination is unbounded
Added explanation for why one JND for Oklab is 0.02, not 2
Clarified that resolving sRGB values does not apply to the color() function
Moved alpha value definition up to the opacity property, clarified that opacity specified values are not clamped.
System Colors now explicitly permit spoofing, to preserve privacy
Corrected the inverse chromatic adaptation matrix for D50 to D65
Consistently distinguish linear Bradfrod from the original, more complicated, Bradford chromatic adaptation algorithm
In the gamut mapping algorithm, return clipped as the gamut mapped result, avoiding un-necessary steps
Updated chromatic adaptation matrices to higher precision
Added Add 'parse a css color' algorithm, so non-CSS specs using colors don't have to reinvent the machinery here.
Clarified that geometric gamut mapping must not project chroma back beyond the original color
Use the term "geometric" rather than "analytical" in gamut mapping discussion
Aligned prose for HSL into line with the grammar (percent and number both allowed)
Fixed an LCH alpha interpolation example, which was erroneously un-premultiplying the hue angle
Corrected the sRGB and display-p3 transfer function. (This only affected the result if a component had the exact value 10.31475 / 255, which is not possible at 8 or 10 bits per component)
Clarified that the specified values of system colors are still themselves
Added mention of PNG cICP chunk for tagging images
Described behaviour of hue increasing and decreasing when 0/360 is passed
Aligned description of powerlessnes in HSL with the other polar color models
Explicitly defined order of operations for color interpolation
Added mention of degenerate numeric constants in calc()
Clarified that calc() in sRGB has early resolution, and clamps the result
Clarified that HWB hue has the same disadvantages as HSL hue
Added luminance to lightness comparison and figure
Added descriptions and examples for hue interpolation keywords
Use normative prose for achromatic HWB colors
Corrected hue interpolation angle range; [0,360) not [0,360]
Expressed that displaying as black or white when L=0% or 100% is due to gamut mapping. Removed incorrect assertions of powerlessness
Dropped the confusing "representing black" and "representing white" comments
Clarified that opponent a and b are analogous
Specified RGB channels using reference ranges rather than prose, for consistency
Explicitly referenced percent reference ranges for percentage to number conversion when serializing Lab, LCH, Oklab, Oklch
Added gamut mapping section and defined a CSS gamut mapping algorithm as chroma reduction in Oklch with local MINDE.
Computed value of color(xyz ...) is color(xyz-d65 ...)
Added srgb-linear to interpolation color spaces
Updated Changes from Colors 3 section
Added Resolving Oklab and Oklch values section
Added srgb-linear color space
Moved @color-profile and device-cmyk to level 5 per CSSWG resolution
Defined interpolation color space
Clarified that matrices are row-major and linked to the matrix multiplication library
Split old Security & Privacy section into separate sections
Defined quirks-mode quirky hex colors
Removed fallback colors from device-cmyk
Host syntax that does not declare a default now uses Oklab by default
Added sample code for deltaE OK
Added sample conversion code for OKlab and Oklch
Added oklab() and oklch() functions
Added description of Oklab and Oklch
Added description of CIE LCH deficiencies
Allowed all components of a color to be "missing" via the ''none'' keyword,
defined when components are "powerless" and automatically become missing in some cases,
and fixed all references to "NaN" channels to use the "missing" concept.
Defined explicit x,y whitepoint values, use consistently throughout
Defined the term host syntax
Defined context for resolving override-color colors
Added a new pair of system colors
Corrected HSL and HWB sample code
Replaced table of HSL values with error-free version
Noted indeterminate hue ssue on near-neutral Lab values converted to LCH
Clarified which steps are linear combinations in RGB Lab interconversion
Added components descriptor to @color-profile, for use in CSS Color 5
All predefined RGB color spaces are defined over the extended range
Clarified that there is no gamut mapping or gamut clipping step prior to color interpolation
Clarified interpolation of legacy sRGB syntaxes
Removed the lab option from ''color()''
List steps to interconvert between predefined color spaces
Consistent use of the term color space (two words)
Provided more guidance on selecting color space for mixing
Recalculated an example to increase precision
Added hue interpolation example
Simplified ''color()'' syntax by removing the fallback options
Clarified the types of ICC profile that may be linked from @color-profile
Support for the rare ICC Named Colors was removed
Improved precision of standard whitepoint chromaticities
Removed a trademark from description of one predefined color space
Rephrased interpolation to be more generic wrt to interpolation space
Corrected Accessibility Considerations section
Clarified that the color space argument for ''color()'' is mandatory, even for sRGB
Clarified that currentColor is not restricted to sRGB
Small correction to the sRGB to XYZ to sRGB matrices, improve round-tripping
Clarified the rec2020 transfer function, citing the correct ITU Rec BT.2020-2 reference
Correct fallback examples to use the correct syntax
Don't force non-legacy colors to interpolate in a gamma-encoded space
Define premultiplied alpha interpolation
Start to address interpolation to and from currentColor
Define hue interpolation with NaN
Generalize color interpolation
Define interpolation to be in Lab, with override to LCG
Corrections to hue interpolation
Defined hue angle interpolation
Added interpolation section
Corrected syntax in some examples
Clarify exactly which components are allowed percentages, in ''color()''
Change to serialize ''lch()'' as itself rather than as ''lab()''
Minimum 10 bits per component precision for non-legacy sRGB in ''color()''
color space no longer optional in ''color()''
Consistent minimum precision between lab() and color(lab)
Clarified fallback procedure for the color() function – first valid in-gamut color, else first valid color which is then gamut mapped, else transparent black
Clarified difference between opacity property and colors with opacity, notably for rendering overlapping text glyphs
Added sample (but verified correct) code for ΔE2000
Added definition of previously-undefined term chromaticity, with examples; define chromaticity diagram.
Added explanation of color additivity, with examples
Added source links to WPT tests
Export definition of color, and valid color, for other specifications to reference
Define minimum number of bits per component, for serialization
Changed Lightness in Lab and LCH to be a percentage, for CSS compatibility
Clamping of color values clarified
Percentage opacity is now allowed
Define terms sRGB and linear-light sRGB, for use by other specs
Add new list of CSS system colors; renaming Text to CanvasText
Make system color keywords compute to themselves
Add computed/used entry for system colors
Rewrite intro to non-deprecated system colors to center their use around forced-colors mode rather than generic use
Consistent hyphenation of predefined color spaces
Restore text about non-opaque elements painting at layers even when not positioned
Initial value of the "color" property is now black
Clarify hue in LCH is modulo 360deg (change now reverted)
Clarify allowed range of L in LCH and Lab, and meaning of L=100
Update references for color spaces used in video
Add prophoto-rgb predefined color space
Correct black and white luminance levels for display-p3
Clarify display-p3 transfer function
Add a98-rgb color space, correct table of primary chromaticities
Clarify that currentColor's computed value is not the resolved color
Update syntax is examples to conform to latest specification
Remove the color-mod() function
Drop the "media" from propdef tables
Export, and consistently use, "transparent black" and "opaque black"
Clarify calculated values such as percents
Clarify required precision and rounding behavior for color channels
Clarify "color" property has no effect on color font glyphs (unless specifically referenced, e.g. with currentColor)
Clarify how color values are resolved
Clarify that HSL, HWB and named colors resolve to sRGB
Simplify conversion from device-cmyk to sRGB
Describe previous, comma-using color syntaxes as "legacy"; change examples to commaless form
Remove superfluous requirement that displayed colors be restricted to device gamut (like there was any other option!)
Rename P3 to display-p3; avoid claiming this is DCI P3, as these are not the same
Improved description of the parameters to the "color()" function
Disallow predefined spaces from "@color-profile" identifier
Add canonical order to "color", "color-adjust" and "opacity" property definitions
Switch definition of alpha compositing from SVG11 to CSS Compositing
Clarify sample conversion code is non-normative
Add Security and Privacy Considerations
Update several references to most current versions
Convert inline issues to links to GitHub issues
Minor editorial clarifications, formatting and markup improvements
Changes from Colors 3
The primary change, compared to CSS Color 3,
is that CSS colors are no longer restricted to the
narrow gamut of sRGB.
To support this, several brand new features have been added:
predefined, wide color gamut RGB color spaces
''lab()'', ''lch()'', ''oklab()'' and ''oklch()'' functions, for device-independent color
Other technical changes:
Serialization of <> is now specified here, rather than in the CSS Object Model
''hwb()'' function, for specifying sRGB colors in the HWB notation.
Addition of named color ''rebeccapurple''.
In addition, there have been some syntactic changes:
''rgb()'' and ''rgba()'' functions now accept <> rather than <>.
''hsl()'' and ''hsla()'' functions now accept <> as well as <> for hues.
''rgb()'' and ''rgba()'', and ''hsl()'' and ''hsla()'' are now aliases of each other
(all of them have an optional alpha).
''rgb()'', ''rgba()'', ''hsl()'', and ''hsla()'' have all gained a new syntax
consisting of space-separated arguments
and an optional slash-separated opacity.
All the color functions use this syntax form now,
in keeping with CSS's functional-notation design principles.
All uses of <> now accept <> as well as <>.
4 and 8-digit hex colors have been added, to specify transparency.
The ''none'' value has been added, to represent powerless components.
Security Considerations {#security}
===================================
The system colors,
if they actually correspond to the user's system colors,
pose a security risk,
as they make it easier for a malware site
to create user interfaces that appear to be from the system.
However, as several system colors are now defined to be "generic",
this risk is believed to be mitigated.
Privacy Considerations {#privacy}
===================================
This specification defines "system" colors,
which theoretically can expose details of the user's OS settings,
which is a fingerprinting risk.
Accessibility Considerations {#a11y-sec}
========================================
This specification encourages authors to not use
color alone as a distinguishing feature.
This specification encourages browsers to ensure
adequate contrast for specific system color foreground/background pairs.
A harder requirement with specific AA or AAA contrast ratios was considered,
but since browsers are often just passing along color choices made by the OS,
or selected by users (who may have particular requirements,
including lower contrast for people living with migraines or epileptic seizures), the CSSWG
was unable to require a specific contrast level.