1. 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 <color> 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.
2. Foreground Color: the color property
Name: | color |
---|---|
Value: | <color> |
Initial: | black |
Applies to: | all elements |
Inherited: | yes |
Percentages: | N/A |
Computed value: | computed color, see resolving color values |
Canonical order: | per grammar |
Animation type: | by computed value type |
This property describes the foreground fill color of an element’s text content. In addition, it provides the value that currentcolor resolves to.
There are several different ways to syntactically specify a given color.
For example, to specify lime green: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% */
- <color>
- The <color> type is defined in a later section.
Note: In general, this property, including its alpha component, has no effect on "color glyphs", such as emoji in some fonts, which are colored by a built-in palette. Some colored fonts are able to refer to the foreground color, such as palette entry 0xFFFF in COLR table of OpenType, and context-fill value in SVG-in-OpenType. In that case, the foreground color is set by this property, identical to how currentcolor value works.
3. Representing sRGB Colors: the <color> type
CSS colors in the sRGB color space are represented by a triplet of values—
While all colors share an underlying storage format, CSS contains several syntaxes for specifying <color> values. Some directly specify the sRGB color, such as the rgb() and rgba() functions and the hex notation. Others are more human-friendly to write and understand, and are converted to an sRGB color by CSS, such as the hsl() and hsla() functions, or the long list of named colors defined by CSS.
In total, the definition of <color> is:
<color> = <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hwb()> | <gray()> | <device-cmyk()> | <hex-color> | <named-color> | currentcolor | <deprecated-system-color>
Some operations work differently on achromatic colors. An achromatic color is a shade of gray: in the sRGB colorspace, a color is achromatic if the red, green, and blue channels are all the same value; in the HSL colorspace, a color is achromatic if the saturation is 0%; in the HWB colorspace, a color is achromatic if the sum of the whiteness and blackness is at least 100%.
For easy reference in other specifications, opaque black is an sRGB color with the red, green, and blue components all at their minimum value, and the alpha component at its maximum value (the same color as produced by rgb(0 0 0 / 100%)). Transparent black is the same color, but with the alpha component at the minimum instead (the same color as produced by rgb(0 0 0 / 0%)).
3.1. Notes On Using Colors
Although colors can add significant amounts of information to documents and make them more readable, color by itself should not be the sole means to convey important information. Please consider the W3C Web Content Accessibility Guidelines [WCAG20] when including color in your documents.
3.2. Colors in sRGB
Colors specified in CSS, HTML, and untagged images are in the sRGB color space ([SRGB]).
This is not yet reliably implemented across implementations, though it has been shown to be implementable. Implementing it compatibly may require notifying plugins to treat untagged colors in the same way to avoid issues with colors not matching each other within a page.
An untagged image is an image that is not explicitly assigned a color profile, as defined by the image format.
Note that this rule does not apply to videos, since untagged video should be presumed to be in ITU.
-
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]
4. Resolving Color values
Various properties accept <color> values. Unless an exception is explicitly defined, all such properties must resolve color values as defined below to determine the computed value and the used value for <color>.
-
If currentcolor is the specified value of the color property,
it is treated as if the specified value was inherit.
For all other properties that accept a <color>, the computed value of the currentcolor keyword is currentcolor, and the used value of currentcolor is the same as 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.
-
The computed value and used value of transparent is rgba(0, 0, 0, 0).
Gecko disagrees, and serializes any <color> with an alpha channel of 0 as transparent. No other browser does that though.
- The computed value and used value of named colors (including <deprecated-system-color> colors), 3 and 6 digits hex colors, 4 and 8 digits hex colors with an explicitly opaque alpha channel, comma separated rgb() colors without an alpha channel, comma separated rgba() colors with an explicitly opaque alpha channel, comma separated hsl() colors without an alpha channel, and comma separated hsla() colors with an explicitly opaque alpha channel, is the equivalent numeric value in comma separated rgb() notation omitting the alpha value.
- The computed value and used value of 4 and 8 digits hex colors with a non opaque alpha channel, comma separated rgba() colors with a non opaque alpha channel, and comma separated hsla() colors with a non opaque alpha channel, is the equivalent numeric value in comma separated rgba() notation with the alpha value.
Define if changing the working color space should have any impact on the above.
Various parts of the spec define the kind of clamping that should happen to the various numeric notations when the numbers specified are out of range, and do so with varrying precision, sometimes saying that this happens at computed value time, sometimes not saying when it happens, and sometimes not saying anything at all. Maybe this should be consolidated here.
Any future specification extending the syntax of <color> must define the how to resolve color values for the new extensions.
5. RGB Colors
There are several methods of directly specifying an sRGB color in terms of its RGBA channels.
5.1. The RGB functions: rgb() and rgba()
The rgb() function defines an sRGB color by specifying the red, green, and blue channels directly. Its syntax is:
rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) | rgb( <number>{3} [ / <alpha-value> ]? ) <alpha-value> = <number> | <percentage>
The first three arguments specify the 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. A <number> is equivalent to a <percentage>, but with a different range: 0 again represents the minimum value for the color channel, but 255 represents the maximum. These values come from the fact that many graphics engines store 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 to the closest value at the highest precision used, rounding up if two values are equally close.
The final argument, the <alpha-value>, specifies the alpha of the color. If given as a <number>, the useful range of the value is 0 (representing a fully transparent color) to 1 (representing a fully opaque color). If given as a <percentage>, 0% represents a fully transparent color, while 100% represents a fully opaque color. If omitted, it defaults to 100%.
Values outside these ranges are not invalid, but are clamped to the ranges defined here at computed-value time.
For legacy reasons, rgb() also supports an alternate syntax that separates all of its arguments with commas:
rgb() = rgb( <percentage>#{3} , <alpha-value>? ) | rgb( <number>#{3} , <alpha-value>? )
Also for legacy reasons, an rgba() function also exists, with an identical grammar and behavior to rgb().
5.2. The RGB hexadecimal notations: #RRGGBB
The CSS hex color notation allows a 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 <hash-token> 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.
- 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.
- 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.
6. Named Colors
In addition to the various numeric syntaxes for <color>s, CSS defines a large set of named colors that can be used instead, so that common colors can be written and read more easily. A <named-color> is written as an <ident>, accepted anywhere a <color> is. As usual for CSS-defined <ident>s, all of these keywords are case-insensitive.
The names resolve to colors in sRGB.
16 of CSS’s named colors come from HTML originally: 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. (Two special color values, transparent and currentcolor, are specially defined in their own sections.)
Note: The history of the X11 color system is interesting, and was excellently summarized by Alex Sexton in his talk “Peachpuffs and Lemonchiffons”.
The following table defines all of the opaque named colors, by giving equivalent numeric specifications in the other color syntaxes.
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.6.1. The transparent keyword
The keyword transparent specifies a transparent black. It is a type of <named-color>.
6.2. The currentcolor keyword
The keyword currentcolor represents value of the color property on the same element. Its used values is determined by resolving color values.
.foo { color : red; background-color : currentcolor; }
This is equivalent to writing:
.foo { color : red; background-color : red; }
< p >< em > Some< strong > really</ strong > emphasized text.</ em > < style > p { color : black ; } em { text-emphasis : dot ; } strong { color : red ; } </ style >
In the above example, the emphasis marks would be 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 it was originally introduced in SVG as a special attribute value spelled "currentColor", rather than a CSS value. Only later did CSS pick it up, at which point the capitalization stopped mattering, as CSS keywords are case-insensitive.
7. 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 color scheme, which is much 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() function is:
hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) <hue> = <number> | <angle>
The first argument specifies the hue. Hue is represented as an angle of the color circle (the rainbow, twisted around into a circle). The angle 0deg represents red (as does 360deg, 720deg, etc.), and the rest of the hues are spread around the circle, so 120deg represents green, 240deg represents blue, etc. 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.
The next two arguments are the saturation and lightness, respectively. For saturation, 100% is a fully-saturated, bright color, and 0% is a fully-unsaturated gray. For lightness, 50% represents the "normal" color, while 100% is white and 0% is black. If the saturation or lightness are less than 0% or greater than 100%, they are clipped to those values before being converted to an RGB color.
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%.
The advantage of HSL over RGB is that it is far more intuitive: one can guess at the colors they want, and then tweak. It is also easier to create sets of matching colors (by keeping the hue the same and varying the saturation and lightness).
HSL colors resolve to sRGB.
hsl(120deg 100% 50%) lime green hsl(120deg 100% 25%) dark green hsl(120deg 100% 75%) light green hsl(120deg 75% 85%) pastel green
For legacy reasons, hsl() also supports an alternate syntax that separates all of its arguments with commas:
hsl() = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
Also for legacy reasons, an hsla() function also exists, with an identical grammar and behavior to hsl().
7.1. Converting HSL colors to sRGB colors
Converting an HSL color to sRGB is straightforward mathematically. Here’s a simple implementation of the conversion algorithm in JavaScript. For simplicity, this algorithm assumes that the hue has been normalized to a number in the half-open range [0, 6), and the saturation and lightness have been normalized to the range [0, 1]. It returns an array of three numbers representing the red, green, and blue channels of the colors, normalized to the range [0, 1].
function hslToRgb( hue, sat, light) { if ( light<= .5 ) { var t2= light* ( sat+ 1 ); } else { var t2= light+ sat- ( light* sat); } var t1= light* 2 - t2; var r= hueToRgb( t1, t2, hue+ 2 ); var g= hueToRgb( t1, t2, hue); var b= hueToRgb( t1, t2, hue- 2 ); return [ r, g, b]; } function hueToRgb( t1, t2, hue) { if ( hue< 0 ) hue+= 6 ; if ( hue>= 6 ) hue-= 6 ; if ( hue< 1 ) return ( t2- t1) * hue+ t1; else if ( hue< 3 ) return t2; else if ( hue< 4 ) return ( t2- t1) * ( 4 - hue) + t1; else return t1; }
7.2. Examples of HSL colors
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.
8. HWB Colors: hwb() function
HWB (short for Hue-Whiteness-Blackness) is another method of specifying 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.
< input type = "color" >
.
The outer wheel is used to select the hue,
then the relative amounts of white and black are selected by clicking on the inner triangle. The syntax of the hwb() function is:
hwb() = hwb( <hue> <percentage> <percentage> [ / <alpha-value> ]? )
The first argument specifies the hue, and is interpreted identically to hsl().
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). Values outside of these ranges make the function invalid. If the sum of these two arguments is greater than 100%, then at computed-value time they are normalized to add up to 100%, with the same relative ratio.
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%.
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 white+black is equal to 100% (after normalization), it defines an achromatic color, or some shade of gray, without any hint of the chosen hue.
8.1. Converting HWB colors to sRGB colors
Converting an HWB color to sRGB is straightforward, and related to how one converts HSL to RGB. The following Javascript implementation of the algorithm assumes that the white and black components have already been normalized, so their sum is no larger than 100%, and have been converted into numbers in the range [0,1].
function hwbToRgb( hue, white, black) { var rgb= hslToRgb( hue, 1 , .5 ); for ( var i= 0 ; i< 3 ; i++ ) { rgb[ i] *= ( 1 - white- black); rgb[ i] += white; } return rgb; }
8.2. Examples of HWB Colors
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% |
9. Device-independent Colors: Lab and LCH
Physical measurements of a color are typically expressed as the Lab color space, created in 1976 by the CIE. Color conversions from one device to another 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 axis. L=0 is deep black (no light at all) while L=100 is a diffuse white (the illuminant is D50 white, a standardized daylight spectrum with a color temperature of 5000K, as reflected by a perfect diffuse reflector). Values greater than 100 would correspond to specular highlights, but their precise color is undefined in this specification. Usefully, L=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.
D50 is also the whitepoint used for the profile connection space in ICC color interconversion, the values used in image editors which offer Lab editing, and the value used by physical measurement devices such as spectrometers, 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 Bradford algorithm is the industry standard chromatic adaptation transform, and is easy to calculate as it is a simple matrix multiplication.
In Lab if two colors have the same L value,
they appear to have the same visual lightness—
LCH has the same L axis as Lab, but uses polar coordinates C (chroma) and H (hue). C is the geometric distance from the L axis and H is the angle from the positive a axis, with positive angles being more clockwise.
Note: The Lightness axis in Lab should not 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 even though visually, blue is much darker. In Lab, if two colors have the same measured L value, they have identical visual lightness. HSL and related polar RGB models were developed to give similar usability benefits for RGB that LCH gave to Lab.
9.1. Specifying Lab and LCH: the lab() and lch() functional notations
CSS allows colors to be directly expressed in Lab and LCH.
lab() = lab( <number> <number> <number> [ / <alpha-value> ]? )
The first argument specifies the CIE Lightness. This is typically a number between 0 (representing black) and 100 (representing white), similar to the lightness argument of hsl(). However, CIE Lightness can exceed this range on some systems, with extra-bright whites using a lightness up to 400. Values less than 0 must be clipped to 0; values greater than 100 are permitted (for forwards compatibility with High Dynamic Range (HDR), and must not be clipped.
The second and third arguments are the distances along the "a" and "b" axises in the Lab colorspace, 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 alpha value, separated by a slash, and interpreted identically to the <alpha-value> in rgb().
lch() = lch( <number> <number> <hue> [ / <alpha-value> ]? )
The first argument specifies the CIE Lightness, interpreted identically to the Lightness argument of lab().
The second argument is the chroma (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.
The third argument is the hue. It’s interpreted identically to the <hue> argument of hsl(), but doesn’t map hues to angles in the same way. Instead, 0deg points along the positive "a" axis, 90deg points along the positive "b" axis, 180deg points along the negative "a" axis, and 270deg points along the negative "b" axis. If the provided value is is negative, or is greater than or equal to 360deg, it is set to the value modulo 360.
There is an optional fourth alpha value, separated by a slash, and interpreted identically to the <alpha-value> in rgb().
Need to decide what, if anything, to do for high dynamic range on luminance.
9.2. Converting sRGB colors to Lab colors
Conversion from sRGB to Lab requires several steps, although in practice all but the first step are linear calculations and can be combined.
- Convert from sRGB to linear-light sRGB (undo gamma encoding)
- Convert from linear sRGB to CIE XYZ
- Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in Lab, with the Bradford transform
- Convert D50-adapted XYZ to Lab
There is sample JavaScript code for this conversion in §17 Sample code for color conversions.
9.3. Converting Lab colors to sRGB colors
Conversion from Lab to sRGB 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
- Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint used in sRGB, with the Bradford transform
- Convert from (D65-adapted) CIE XYZ to linear sRGB
- Convert from linear-light sRGB to sRGB (do gamma encoding)
9.4. Converting Lab colors to LCH colors
Conversion to LCH is trivial:
- H = atan2(b, a)
- C = sqrt(a^2 + b^2)
- L is the same
9.5. Converting LCH colors to Lab colors
Conversion to Lab is trivial:
- a = C cos(H)
- b = C sin(H)
- L is the same
10. Specifying Grays: the gray() functional notation
As decided at San Francisco, this syntax is an alias to Lab with a=b=0.
Grays are fully desaturated colors. The gray() functional notation simplifies specifying this common set of colors, so that only a single numerical parameter is required, and so that gray(50) is a visual mid-gray (perceptually equidistant between black and white).
gray() = gray( <number> [ / <alpha-value> ]? )
The first argument specifies the shade of gray, equal to the CIE Lightness, while the second optional argument specifies the alpha channel of the gray.
Note: In other words, gray(a / b) is equal to lab(a 0 0 / b).
10.1. Converting gray colors to sRGB colors
Conversion from gray to sRGB requires multiple steps; in practice all but the last step are linear calculations and can be combined.
- Convert to Lab by setting L to the gray value, a and b to 0
- Convert Lab to XYZ
- Adapt from D50 to D65 (Bradford transform)
- Convert from (D65-adapted) CIE XYZ to linear sRGB
- Convert from linear-light sRGB to sRGB (do gamma encoding)
11. Profiled, Device-dependent Colors
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 characterised. This characterization information is stored in a profile. The most common type of color profile is defined by the International Color Consortium (ICC) [ICC].
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.
CSS allows colors to be specified by reference to a color profile. This could be for example a calibrated CMYK printer, or an RGB colorspace (such as ProPhoto , widely used by photographers), or any other color or monochrome output device which has been characterized. In addition, for convenience, CSS provides two predefined RGB color spaces: image-p3 [DCI-P3], which is a wide gamut space typical of current wide-gamut monitors, and Rec. 2020 [Rec.2020], which is a ultra-wide gamut space capable of representing almost all visible real-world colors. Both are broadcast industry standards.
color: color(swopc 0.0134 0.8078 0.7451 0.3019); color: color(indigo 0.0941 0.6274 0.3372 0.1647 0 0.0706 0.1216); color: color(prophoto 0.9137 0.5882 0.4784); color: color(image-p3 0.3804 0.9921 0.1412);
All but the predefined colorspace example also need a matching @color-profile at-rule somewhere in the stylesheet, to connect the name with the profile data.
@color-profile swopc { src: url('http://example.org/swop-coated.icc');} @color-profile indigo { src: url('http://example.org/indigo-seven.icc');} profile prophoto { src: url('http://example.org/prophoto.icc');}
color() fallback should be like font list fallback, as decided at San Francisco. Recursive?
11.1. Specifying profiled colors: the color() function
The color() function allows a color to be specified in a particular colorspace (rather than the implicit sRGB colorspace that the other color functions operate in). Its syntax is:
color() = color( [ <ident>? [ <number>+ | <string> ] [ / <alpha-value> ]? ]# , <color>? )
The color function takes one or more comma-separated arguments, with each argument specifying a color, and later colors acting as "fallback" if an earlier color can’t be displayed (for example, if the colorspace it specifies hasn’t been loaded yet).
Each argument has the following form:
-
An optional <ident> denoting the colorspace. This can be one of the predefined colorspaces (such as image-p3), or one defined by a @color-profile rule. If omitted, it defaults to the predefined srgb color profile.
If the <ident> names a non-existent colorspace, this argument represents an invalid color.
-
Either one or more <number>s providing the parameter values that the colorspace takes, or a <string> giving the name of a color defined by the colorspace.
- If the colorspace takes numeric parameters
-
If more <number>s are provided than parameters that the colorspace takes,
the excess <number>s at the end are ignored.
If less <number>s are provided than parameters that the colorspace takes, the missing parameters default to 0. (This is particularly convenient for multichannel printers where the additional inks are spot colors or varnishes that most colors on the page won’t use.)
If a <string> is provided, this argument represents an invalid color.
- If the colorspace defines named colors
- If <number>s are provided, or a <string> is provided that doesn’t match any of the color names defined by the colorspace, this argument represents an invalid color.
-
An optional slash-separated <alpha-value>. This is interpreted the same way as the <alpha-value> in rgb(), and if omitted it defaults to 100%.
After one or more arguments of the above form, a final <color> argument using any CSS color syntax can be provided.
The color() function represents the color specified by the first of its arguments that represent a valid color (that is, the first argument that isn’t an invalid color). If all of its arguments represent invalid colors, color() represents opaque black.
11.2. Predefined colorspaces: srgb, image-p3, a98rgb, prophotorgb and rec2020.
The following colorspaces are predefined for use in the color() function. They can be used without any @color-profile rule.
Decided at San Francisco to add a larger set of common predefined spaces like AdobeRGB, ProPhoto RGB, and so on. Also coated and uncoated swop, etc, etc.
- srgb
-
The srgb [SRGB] colorspace accepts three numeric parameters,
representing the red, green, and blue channels of the color,
with each having a valid range of [0, 1].
The whitepoint is D65
(a daylight white, with a correlated color temperature of 6504°K).
[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 colorspace for CSS, identical to specifying a color with the rgb() function.
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 0.3127 3290 Transfer function see below White luminance 80.0 cd/m2 Black luminance 0.80 cd/m2 var Cl; if ( C<= 0.04045 ) Cl= C/ 12.92 ; else Cl= Math. pow(( C+ 0.055 ) / 1.055 , 2.4 ); C is the red, green or blue component.
- image-p3
-
The image-p3 colorspace accepts three numeric parameters,
representing the red, green, and blue channels of the color,
with each having a valid range of [0, 1].
It uses the same primary chromaticities as [DCI-P3],
but with a D65 whitepoint and the same transfer curve as sRGB.
It has the following characteristics:
x y Red chromaticity 0.680 0.320 Green chromaticity 0.265 0.690 Blue chromaticity 0.150 0.060 White chromaticity 0.3127 0.3290 Transfer function same as srgb White luminance 80.0 cd/m2 Black luminance 0.80 cd/m2 - a98rgb
-
The a98rgb colorspace accepts three numeric parameters,
representing the red, green, and blue channels of the color,
with each having a valid range of [0, 1]. The transfer curve is
a gamma function, close to but not exactly 1/2.2.
a98rgb is compatible with Adobe® RGB (1998).
Adobe® RGB (1998) uses primaries originally derived from the SMPTE 240M standard; errors in the original conversion turned out to produce a colorspace that was useful for digital photography, so Adobe® RGB (1998) is a common wider-gamut colorspace for photographic images. The a98rgb colorspace allows CSS to specify colors that will match colors in such images having the same RGB values.
It has the following characteristics:
x y Red chromaticity 0.680 0.320 Green chromaticity 0.265 0.690 Blue chromaticity 0.150 0.060 White chromaticity 0.3127 0.3290 Transfer function 256/563 White luminance 160.0 cd/m2 Black luminance 0.5557 cd/m2 - prophotorgb
-
The prophotorgb colorspace accepts three numeric parameters,
representing the red, green, and blue channels of the color,
with each having a valid range of [0, 1]. The transfer curve is
a gamma function with a value of 1/1.8.
The white point is D50, the same as is used by CIE Lab. Thus,
conversion to Lab does not require the chromatic adaptation step.
The ProPhoto RGB space uses primaries chosen to allow a wide color gamut and to minimise hue shifts under tonal manipulation. It is often used in digital photography as a wide gamut colorspace for the master version of photographic images. The prophotorgb colorspace allows CSS to specify colors that will match colors in such images having the same RGB values.
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.7347 0.2653 Green chromaticity 0.1596 0.8404 Blue chromaticity 0.0366 0.0001 White chromaticity 0.3457 0.3585 Transfer function 1/1.800 White luminance 160.0 to 640.0 cd/m2 Black luminance See text - rec2020
-
The rec2020 [Rec.2020] colorspace accepts three numeric parameters,
representing the red, green, and blue channels of the color,
with each having a valid range of [0, 1]. ITU Reference 2020 is used for High Definition, 4k and 8k television.
It has the following characteristics:
x y Red chromaticity 0.708 0.292 Green chromaticity 0.170 0.797 Blue chromaticity 0.131 0.046 White chromaticity 0.3120 0.3290 Transfer function 1/2.4 (see note) Note: Rec2020 references a different transfer curve for cameras. However this curve is never used in production cameras or 2020 displays.
"In typical production practice the encoding function of image sources is adjusted so that the final picture has the desired look, as viewed on a reference monitor having the reference decoding function of Recommendation ITU-R BT.1886, in the reference viewing environment defined in Recommendation ITU-R BT.2035."
The transfer function (1886) for reference Rec.2020 displays is gamma 2.4 [Rec.2020]
11.2.1. Converting predefined colorspaces to Lab
For both predefined 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-corrected RGB to linear-light RGB (undo gamma encoding)
- Convert from linear RGB to CIE XYZ
- Convert from a D65 whitepoint (used by both image-p3 and rec2020) to the D50 whitepoint used in Lab, with the Bradford transform
- Convert D50-adapted XYZ to Lab
Canvas proposes adding a 16bit half-float linear rec2020 space
11.2.2. Converting Lab to predefined colorspaces
Conversion from Lab to image-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
- Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint used in sRGB, with the Bradford transform
- Convert from (D65-adapted) CIE XYZ to linear RGB
- Convert from linear-light RGB to RGB (do gamma encoding)
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 the source and destination gamuts.
11.3. Specifying a color profile: the @color-profile at-rule
The @color-profile rule defines and names a color profile which can later be used in the color() function to specify a color. It’s defined as:
@color-profile = @color-profile <custom-ident> { <declaration-list> }
The <custom-ident> gives the color profile’s name. All of the predefined colorspace keywords (srgb, image-p3, a98rgb, prophotorgb, rec2020) are excluded from this <custom-ident>, as they’re predefined by this specification and always available.
The @color-profile rule accepts the descriptors defined in this specification.
Name: | src |
---|---|
For: | @color-profile |
Value: | <url> |
Initial: | n/a |
The src descriptor specifies the URL to retrieve the color-profile information from.
local() to use locally installed profiles. Profile stack like font-face rather than a single url. Avoid flash of uncalibrated color.
Name: | rendering-intent |
---|---|
For: | @color-profile |
Value: | relative-colorimetric | absolute-colorimetric | perceptual | saturation |
Initial: | relative-colorimetric |
Color profiles contain “rendering intents”, which define how to map their color to smaller gamuts than they’re defined over. Often a profile will contain only a single intent, but when there are multiple, the rendering-intent descriptor chooses one of them to use.
The four possible rendering intents are [ICC]:
- relative-colorimetric
-
Media-relative colorimetric is required to leave source colors that fall
inside the destination medium gamut unchanged relative to the respective
media white points. Source colors that are out of the destination medium
gamut are mapped to colors on the gamut boundary using a variety of
different methods.
Note: the media-relative colorimetric rendering intent is often used with black point compensation, where the source medium black point is mapped to the destination medium black point as well. This method must map the source white point to the desination white point. If black point compensation is in use, the source black point must also be mapped to the destination black point. Adaptation algorithms should be used to adjust for the change in white point. Relative relationships of colors inside both source and destination gamuts should be preserved. Relative relationships of colors outside the destination gamut may be changed.
- absolute-colorimetric
-
ICC-absolute colorimetric is required to leave source colors that fall
inside the destination medium gamut unchanged relative to the adopted
white (a perfect reflecting diffuser). Source colors that are out of the
destination medium gamut are mapped to colors on the gamut boundary using a
variety of different methods. This method produces the most accurate
color matching of in-gamut colors, but will result in highlight clipping
if the destination medium white point is lower than the source medium
white point. For this reason it is recommended for use only in applications
that need exact color matching and where highlight clipping is not a concern.
This method MUST disable white point matching and black point matching when converting colors. In general, this option is not recommended except for testing purposes.
- perceptual
-
This method is often the preferred choice for images, especially when there are
substantial differences between the source and destination (such as a screen display
image reproduced on a reflection print). It takes the colors of the source image
and re-optimizes the appearance for the destination medium using proprietary
methods. This re-optimization may result in colors within both the source
and destination gamuts being changed, although perceptual transforms are
supposed to maintain the basic artistic intent of the original in the
reproduction. They will not attempt to correct errors in the source image.
Note: With v2 ICC profiles there is no specified perceptual reference medium, which can cause interoperability problems. When v2 ICC profiles are used it may be safer to use the media-relative colorimetric rendering intent with black point compensation, instead of the perceptual rendering intent, unless the specific source and destination profiles to be used have been checked to ensure the combination produces the desired result.
This method should maintain relative color values among the pixels as they are mapped to the target device gamut. This method may change pixel values that were originally within the target device gamut, in order to avoid hue shifts and discontinuities and to preserve as much as possible the overall appearance of the scene.
- saturation
- This option was created to preserve the relative saturation (chroma) of the original, and to keep solid colors pure. However, it experienced interoperability problems like the perceptual intent, and as solid color preservation is not amenable to a reference medium solution using v4 profiles does not solve the problem. Use of this rendering intent is not recommended unless the specific source and destination profiles to be used have been checked to ensure the combination produces the desired result. This option should preserve the relative saturation (chroma) values of the original pixels. Out of gamut colors should be converted to colors that have the same saturation but fall just inside the gamut.
RESOLVED: Do black point compensation when converting from profile to another. This will depend on the rendering intent and is mentioned there already. Does that suffice? What about black point compensation for the flare correction built into sRGB?
12. Working Color Space
Resolved at San Francisco to add a working-color-space at-rule, which affects the entire document. Compositing, interpolation, blending use this. Initial value is sRGB. linear-sRGB, p3, rec2020, and lab were also discussed as values. Chris to read the canvas spec to see what it does there, particularly for the "optimal" value.
13. Device-dependent CMYK Colors: the device-cmyk() function
While screens typically display colors directly with RGB pixels, printers often represent colors in different ways. In particular, one of the most common print-based ways of representing colors is with CMYK: a combination of cyan, magenta, yellow, and black which yields a particular color on that device. The device-cmyk() function allows authors to specify a color in this way:
device-cmyk() = device-cmyk( <cmyk-component>{4} [ / <alpha-value> ]? , <color>? ) <cmyk-component> = <number> | <percentage>
The arguments of the device-cmyk() function specify the cyan, magenta, yellow, and black components, in order, as a number between 0 and 1 or a percentage between 0% and 100%. These two usages are equivalent, and map to each other linearly. Values less than 0 or 0%, or greater than 1 or 100%, are not invalid; instead, they are clamped to 0/0% or 1/100%.
The fifth 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%.
The sixth argument specifies the fallback color, used when the user agent doesn’t know how to accurately transform the CMYK color to RGB. If omitted, it defaults to the CMYK color naively converted to RGBA.
RESOLVED: If you accurately describe the output device’s color profile in an @color-profile rule then a sane implementation will not alter your colors so this is sufficient as a replacement for device-cmyk in general and provides a good RGB fallback automatically.
Typically, print-based applications will actually store the used colors as CMYK, and send them to the printer in that form. Unfortunately, CSS cannot do that; various CSS features require an RGB color, so that compositing/blending/etc. can be done. As such, CMYK colors must be converted to an equivalent RGB color. This is not trivial, like the conversion from HSL or HWB to RGB; the precise conversion depends on the precise characteristics of the output device.
If the user agent has information about the output device such that it believes it can accurately convert the CMYK color to a correct RGB color, the computed value of the device-cmyk() function must be that RGBA color. Otherwise, the computed value must be the fallback color.
color: device-cmyk(0 81% 81% 30%); color: rgb(178 34 34); color: firebrick;
Note: these colors might not match precisely if the browser knows a more precise conversion between CMYK and RGB colors. It’s recommended that if authors use any CMYK colors in their document, that they use only CMYK colors in their document to avoid any color-matching difficulties.
13.1. Converting Between Uncalibrated CMYK and RGB-Based Colors
This section now needs to clearly distinguish between calibrated (icc-based) color on the one hand, and uncalibrated device-cmyk on the other. This particularly affects conversion to and from RGB.
While most colors defined in this specification are directly compatible with RGBA, and thus can be mechanically and consistently converted back and forth with it, CMYK colors are not directly compatible; a given CMYK color will map to various RGBA colors depending on the physical characteristics of the output device.
Ideally, the user agent will be aware of the output device’s color profiles for RGBA and CMYK. If this is true, then the user agent must convert between CMYK and RGBA colors (and vice versa) by first converting the color into an appropriate device-independent color space, such as CIELab, and then converting into the output color space, using the appropriate color profiles for each operation.
This is not always possible, however. In that case, the user agent must use the following naive conversion algorithms.
To naively convert from CMYK to RGBA:
If a fallback color was specified, return that color (converting it to RGB as well, if necessary). Otherwise:
red
= 1 - min( 1 , cyan* ( 1 - black) + black) green
= 1 - min( 1 , magenta* ( 1 - black) + black) blue
= 1 - min( 1 , yellow* ( 1 - black) + black) - Alpha is same as for input color.
To naively convert from RGBA to CMYK:
black
= 1 - max( red, green, blue) cyan
= ( 1 - red- black) / ( 1 - black), or0 if black is1 magenta
= ( 1 - green- black) / ( 1 - black), or0 if black is1 yellow
= ( 1 - blue- black) / ( 1 - black), or0 if black is1 - alpha is the same as the input color
- fallback color must be set to the input color
14. 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: | <alpha-value> |
Initial: | 1 |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Computed value: | specified number, clamped to the range [0,1] |
Canonical order: | per grammar |
Animation type: | by computed value type |
- <alpha-value>
- The opacity to be applied to the element. It is interpreted identically to its definition in rgb(), except that the resulting opacity is applied to the entire element, rather than a particular color.
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.
If opacity has a value less than 1, the element forms a stacking context for its children. This means that any content outside of it cannot be layered in z-order between pieces of content inside of it, and vice versa. If the element is in a context where the z-index property applies, the auto value is treated as 0 for the element. See section 9.9 and Appendix E of [CSS21] for more information on stacking contexts. The rules in this paragraph do not apply to SVG elements, since SVG has its own rendering model ([SVG11], Chapter 3).
14.1. Simple alpha compositing
When drawing, implementations must handle alpha according to the rules in Section 5.1 Simple alpha compositing of [Compositing].
15. Preserving Colors in Different-Capability Devices: the color-adjust property
On most monitors, the color choices that authors make have no significant difference in terms of how the device performs; displaying a document with a white background or a black background is approximately equally easy.
However, some devices have limitations and other qualities that make this assumption untrue. For example, printers tend to print on white paper; a document with a white background thus has to spend no ink on drawing that background, while a document with a black background will have to expend a large amount of ink filling in the background color. This tends to look fairly bad, and sometimes has deleterious physical effects on the paper, not to mention the vastly increased printing cost from expending the extra ink. Even fairly small differences, such as coloring text black versus dark gray, can be quite different when printing, as it switches from using a single black ink to a mixture of cyan, magenta, and yellow ink, resulting in higher ink usage and lower resolution.
As a result, in some circumstances user agents will alter the styles an author specifies in some particular context, adjusting them to be more appropriate for the output device and to accommodate what they assume the user would prefer. However, in some cases the document may be using colors in important, well-thought-out ways that the user would appreciate, and so the document would like some way to hint to the user agent that it might want to respect the page’s color choices. The color-adjust property controls this.
Name: | color-adjust |
---|---|
Value: | economy | exact |
Initial: | economy |
Applies to: | all elements |
Inherited: | yes |
Percentages: | N/A |
Computed value: | specified keyword |
Canonical order: | per grammar |
Animation type: | discrete |
The color-adjust property provides a hint to the user-agent about how it should treat color and style choices that might be expensive or generally unwise on a given device, such as using light text on a dark background in a printed document. If user agents allow users to control this aspect of the document’s display, the user preference must be respected more strongly than the hint provided by color-adjust. It has the following values:
- economy
-
The user agent should make adjustments to the page’s styling
as it deems necessary and prudent for the output device.
For example, if the document is being printed, a user agent might ignore any backgrounds and adjust text color to be sufficiently dark, to minimize ink usage.
- exact
-
This value indicates that the page is using color and styling on the specified element
in a way which is important and significant,
and which should not be tweaked or changed except at the user’s request.
For example, a mapping website offering printed directions might "zebra-stripe" the steps in the directions, alternating between white and light gray backgrounds. Losing this zebra-striping and having a pure-white background would make the directions harder to read with a quick glance when distracted in a car.
16. 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 HTML4, XHTML1, XHTML1.1, XHTML Basic, and other XHTML Family documents.
html { color : black; } /* traditional desktop user agent colors for hyperlinks */ :link { color : blue; } :visited { color : purple; }
The default background of the root element must be transparent. The default color of the canvas (the surface on which the document is painted) is UA-dependent, but is recommended to be white, especially if the above color rules are used.
17. Sample code for color conversions
This section is not normative.
// sRGB-related functions function lin_sRGB( RGB) { // convert an array of sRGB values in the range 0.0 - 1.0 // to linear light (un-companded) form. // https://en.wikipedia.org/wiki/SRGB return RGB. map( function ( val) { if ( val< 0.04045 ) { return val/ 12.92 ; } return Math. pow(( val+ 0.055 ) / 1.055 , 2.4 ); }); } function gam_sRGB( RGB) { // convert an array of linear-light sRGB values in the range 0.0-1.0 // to gamma corrected form // https://en.wikipedia.org/wiki/SRGB return RGB. map( function ( val) { if ( val> 0.0031308 ) { return 1.055 * Math. pow( val, 1 / 2.4 ) - 0.055 ; } return 12.92 * val; }); } function lin_sRGB_to_XYZ( rgb) { // convert an array of linear-light sRGB values to CIE XYZ // using sRGB’s own white, D65 (no chromatic adaptation) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= math. matrix([ [ 0.4124564 , 0.3575761 , 0.1804375 ], [ 0.2126729 , 0.7151522 , 0.0721750 ], [ 0.0193339 , 0.1191920 , 0.9503041 ] ]); return math. multiply( M, rgb). valueOf(); } function XYZ_to_lin_sRGB( XYZ) { // convert XYZ to linear-light sRGB var M= math. matrix([ [ 3.2404542 , - 1.5371385 , - 0.4985314 ], [ - 0.9692660 , 1.8760108 , 0.0415560 ], [ 0.0556434 , - 0.2040259 , 1.0572252 ] ]); return math. multiply( M, XYZ). valueOf(); } // image-3-related functions function lin_P3( RGB) { // convert an array of image-p3 RGB values in the range 0.0 - 1.0 // to linear light (un-companded) form. return lin_sRGB( RGB); // same as sRGB } function gam_P3( RGB) { // convert an array of linear-light image-p3 RGB in the range 0.0-1.0 // to gamma corrected form return gam_sRGB( RGB); // same as sRGB } function lin_P3_to_XYZ( rgb) { // convert an array of linear-light image-p3 values to CIE XYZ // using D65 (no chromatic adaptation) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= math. matrix([ [ 0.4865709486482162 , 0.26566769316909306 , 0.1982172852343625 ], [ 0.2289745640697488 , 0.6917385218365064 , 0.079286914093745 ], [ 0.0000000000000000 , 0.04511338185890264 , 1.043944368900976 ] ]); // 0 was computed as -3.972075516933488e-17 return math. multiply( M, rgb). valueOf(); } function XYZ_to_lin_P3( XYZ) { // convert XYZ to linear-light P3 var M= math. matrix([ [ 2.493496911941425 , - 0.9313836179191239 , - 0.40271078445071684 ], [ - 0.8294889695615747 , 1.7626640603183463 , 0.023624685841943577 ], [ 0.03584583024378447 , - 0.07617238926804182 , 0.9568845240076872 ] ]); return math. multiply( M, XYZ). valueOf(); } // ProPhotoRGB functions function lin_ProPhoto( RGB) { // convert an array of ProPhotoRGB values in the range 0.0 - 1.0 // to linear light (un-companded) form. // Transfer curve is gamma 1.0 with a small linear portion return RGB. map( function ( val) { if ( val< 0.031248 ) { return val/ 16 ; } return Math. pow( val, 1.8 ); }); } function gam_ProPhoto( RGB) { // convert an array of linear-light ProPhotoRGB in the range 0.0-1.0 // to gamma corrected form // Transfer curve is gamma 1.0 with a small linear portion return RGB. map( function ( val) { if ( val> 0.001953 ) { return Math. pow( val, 1 / 1.8 ); } return 16 * val; }); } function lin_ProPhoto_to_XYZ( rgb) { // convert an array of linear-light ProPhotoRGB values to CIE XYZ // using D50 (so no chromatic adaptation needed afterwards) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= Math. matrix([ [ 0.7977604896723027 , 0.13518583717574031 , 0.0313493495815248 ], [ 0.2880711282292934 , 0.7118432178101014 , 0.00008565396060525902 ], [ 0.0 , 0.0 , 0.8251046025104601 ] ]); return Math. multiply( M, rgb). valueOf(); } function XYZ_to_lin_ProPhoto( XYZ) { // convert XYZ to linear-light ProPhotoRGB var M= Math. matrix([ [ 1.3457989731028281 , - 0.25558010007997534 , - 0.05110628506753401 ], [ - 0.5446224939028347 , 1.5082327413132781 , 0.02053603239147973 ], [ 0.0 , 0.0 , 1.2119675456389454 ] ]); return Math. multiply( M, XYZ). valueOf(); } // a98rgb functions function lin_a98rgb( RGB) { // convert an array of a98rgb values in the range 0.0 - 1.0 // to linear light (un-companded) form. return RGB. map( function ( val) { return Math. pow( val, 563 / 256 ); }); } function gam_a98rgb( RGB) { // convert an array of linear-light a98rgb in the range 0.0-1.0 // to gamma corrected form return RGB. map( function ( val) { return Math. pow( val, 256 / 563 ); }); } function lin_a98rgb_to_XYZ( rgb) { // convert an array of linear-light a98rgbRGB values to CIE XYZ // using D50 (so no chromatic adaptation needed afterwards) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= Math. matrix([ [ 0.5766690429101305 , 0.1855582379065463 , 0.1882286462349947 ], [ 0.29734497525053605 , 0.6273635662554661 , 0.07529145849399788 ] , [ 0.02703136138641234 , 0.07068885253582723 , 0.9913375368376388 ] ]); return Math. multiply( M, rgb). valueOf(); } function XYZ_to_lin_a98rgb( XYZ) { // convert XYZ to linear-light a98rgbRGB var M= Math. matrix([ [ 2.0415879038107465 , - 0.5650069742788596 , - 0.34473135077832956 ], [ - 0.9692436362808795 , 1.8759675015077202 , 0.04155505740717557 ], [ 0.013444280632031142 , - 0.11836239223101838 , 1.0151749943912054 ] ]); return Math. multiply( M, XYZ). valueOf(); } //Rec. 2020-related functions function lin_2020( RGB) { // convert an array of Rec. 2020 RGB values in the range 0.0 - 1.0 // to linear light (un-companded) form. const α= 1.09929682680944 ; const β= 0.018053968510807 ; return RGB. map( function ( val) { if ( val< β* 4.5 ) { return val/ 4.5 ; } return Math. pow(( val+ α- 1 ) / α, 2.4 ); }); } //check with standard this really is 2.4 and 1/2.4, not 0.45 was wikipedia claims function gam_2020( RGB) { // convert an array of linear-light Rec. 2020 RGB in the range 0.0-1.0 // to gamma corrected form const α= 1.09929682680944 ; const β= 0.018053968510807 ; return RGB. map( function ( val) { if ( val> β) { return α* Math. pow( val, 1 / 2.4 ) - ( α- 1 ); } return 4.5 * val; }); } function lin_2020_to_XYZ( rgb) { // convert an array of linear-light Rec. 2020 values to CIE XYZ // using D65 (no chromatic adaptation) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= math. matrix([ [ 0.6369580483012914 , 0.14461690358620832 , 0.1688809751641721 ], [ 0.2627002120112671 , 0.6779980715188708 , 0.05930171646986196 ], [ 0.000000000000000 , 0.028072693049087428 , 1.060985057710791 ] ]); // 0 is actually calculated as 4.994106574466076e-17 return math. multiply( M, rgb). valueOf(); } function XYZ_to_lin_2020( XYZ) { // convert XYZ to linear-light Rec. 2020 var M= math. matrix([ [ 1.7166511879712674 , - 0.35567078377639233 , - 0.25336628137365974 ], [ - 0.6666843518324892 , 1.6164812366349395 , 0.01576854581391113 ], [ 0.017639857445310783 , - 0.042770613257808524 , 0.9421031212354738 ] ]); return math. multiply( M, XYZ). valueOf(); } // Chromatic adaptation function D65_to_D50( XYZ) { // Bradford chromatic adaptation from D65 to D50 // The matrix below is the result of three operations: // - convert from XYZ to retinal cone domain // - scale components from one reference white to another // - convert back to XYZ // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html var M= math. matrix([ [ 1.0478112 , 0.0228866 , - 0.0501270 ], [ 0.0295424 , 0.9904844 , - 0.0170491 ], [ - 0.0092345 , 0.0150436 , 0.7521316 ] ]); return math. multiply( M, XYZ). valueOf(); } function D50_to_D65( XYZ) { // Bradford chromatic adaptation from D50 to D65 var M= math. matrix([ [ 0.9555766 , - 0.0230393 , 0.0631636 ], [ - 0.0282895 , 1.0099416 , 0.0210077 ], [ 0.0122982 , - 0.0204830 , 1.3299098 ] ]); return math. multiply( M, XYZ). valueOf(); } // Lab and LCH function XYZ_to_Lab( XYZ) { // Assuming XYZ is relative to D50, convert to CIE Lab // from CIE standard, which now defines these as a rational fraction var ε= 216 / 24389 ; // 6^3/29^3 var κ= 24389 / 27 ; // 29^3/3^3 var white= [ 0.96422 , 1.00000 , 0.82521 ]; // D50 reference white // compute xyz, which is XYZ scaled relative to reference white var xyz= XYZ. map(( value, i) => value/ white[ i]); // now compute f var f= xyz. map( value=> value> ε? Math. cbrt( value) : ( κ* value+ 16 ) / 116 ); return [ ( 116 * f[ 1 ]) - 16 , // L 500 * ( f[ 0 ] - f[ 1 ]), // a 200 * ( f[ 1 ] - f[ 2 ]) // b ]; } function Lab_to_XYZ( Lab) { // Convert Lab to D50-adapted XYZ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var κ= 24389 / 27 ; // 29^3/3^3 var ε= 216 / 24389 ; // 6^3/29^3 var white= [ 0.96422 , 1.00000 , 0.82521 ]; // D50 reference white var f= []; // compute f, starting with the luminance-related term f[ 1 ] = ( Lab[ 0 ] + 16 ) / 116 ; f[ 0 ] = Lab[ 1 ] / 500 + f[ 1 ]; f[ 2 ] = f[ 1 ] - Lab[ 2 ] / 200 ; // compute xyz var xyz= [ Math. pow( f[ 0 ], 3 ) > ε? Math. pow( f[ 0 ], 3 ) : ( 116 * f[ 0 ] - 16 ) / κ, Lab[ 0 ] > κ* ε? Math. pow(( Lab[ 0 ] + 16 ) / 116 , 3 ) : Lab[ 0 ] / κ, Math. pow( f[ 2 ], 3 ) > ε? Math. pow( f[ 2 ], 3 ) : ( 116 * f[ 2 ] - 16 ) / κ]; // Compute XYZ by scaling xyz by reference white return xyz. map(( value, i) => value* white[ i]); } function Lab_to_LCH( Lab) { // Convert to polar form var hue= Math. atan2( Lab[ 2 ], Lab[ 1 ]) * 180 / Math. PI; return [ Lab[ 0 ], // L is still L Math. sqrt( Math. pow( Lab[ 1 ], 2 ) + Math. pow( Lab[ 2 ], 2 )), // Chroma hue>= 0 ? hue: hue+ 360 // Hue, in degrees [0 to 360) ]; } function LCH_to_Lab( LCH) { // Convert from polar form return [ LCH[ 0 ], // L is still L LCH[ 1 ] * Math. cos( LCH[ 2 ] * Math. PI/ 180 ), // a LCH[ 1 ] * Math. sin( LCH[ 2 ] * Math. PI/ 180 ) // b ]; }
Appendix A: Deprecated CSS System Colors
Earlier versions of CSS defined several additional named color keywords, the <deprecated-system-color>s, which were meant to take their value from operating system themes. These color names have been deprecated, however, as they are insufficient for their original purpose (making website elements look like their native OS counterparts), and represent a security risk, as it makes it easier for a webpage to "spoof" a native OS dialog.
User agents must support these keywords, but should map them to "default" values, not based on the user’s OS settings (for example, mapping all the "background" colors to white and "foreground" colors to black). Authors must not use these keywords.
- ActiveBorder
- Active window border.
- ActiveCaption
- Active window caption.
- AppWorkspace
- Background color of multiple document interface.
- Background
- Desktop background.
- ButtonFace
- The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.
- 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.
- 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.
- ButtonText
- Text on push buttons.
- CaptionText
- Text in caption, size box, and scrollbar arrow box.
- GrayText
- Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.
- Highlight
- Item(s) selected in a control.
- HighlightText
- Text of item(s) selected in a control.
- InactiveBorder
- Inactive window border.
- InactiveCaption
- Inactive window caption.
- InactiveCaptionText
- Color of text in an inactive caption.
- InfoBackground
- Background color for tooltip controls.
- InfoText
- Text color for tooltip controls.
- Menu
- Menu background.
- MenuText
- Text in menus.
- Scrollbar
- Scroll bar gray area.
- 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.
- ThreeDFace
- The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
- 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.
- 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.
- 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.
- Window
- Window background.
- WindowFrame
- Window frame.
- WindowText
- Text in windows.
Acknowledgments
Thanks to Brad Pettit both for writing up color-profiles, and for implementing it. Thanks to Steven Pemberton for his write up on HSL colors. Thanks especially to the feedback from Marc Attinasi, Bert Bos, Joe Clark, fantasai, Patrick Garies, Tony Graham, Ian Hickson, Susan Lesch, Alex LeDonne, Cameron McCormack, Krzysztof Maczyński, Chris Moschini, Chris Murphy, Christoph Päper, David Perrell, Jacob Refstrup, Dave Singer, Jonathan Stanley, Andrew Thompson, Russ Weakley, Etan Wexler, David Woolley, Boris Zbarsky, Steve Zilles, the XSL FO subgroup of the XSL working group, and all the rest of the www-style community.
And thanks to Chris Lilley for being the resident CSS Color expert.
Changes
Changes since Working Draft of 05 July 2016
- Initial value of the "color" property is now black
- Clarify hue in LCH is modulo 360deg
- Clarify allowed range of L in LCH and Lab, and meaning of L=100
- Update references for colorspaces used in video
- Add ProPhotoRGB predefined colorspace
- Correct black and white luminance levels for image-p3
- Clarify image-p3 transfer function
- Add a98rgb colorspace
- 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
- 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 image-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
- Minor editorial clarifications, formatting and markup improvements
Changes from Colors 3
- rgb() and rgba() functions now accept <number> rather than <integer>.
- hsl() and hsla() functions now accept <angle> as well as <number> 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 <alpha-value> now accept <percentage> as well as <number>.
- 4 and 8-digit hex colors have been added, to specify transparency.
Several brand new features have been added:
- gray() function, for specifying grays compactly. (And maybe allowing specification via luminance.)
- hwb() function, for specifying colors in the HWB notation.
- lab() and lch() functions, for device-independent color
- color() function and @color-profile at-rule, for profiled device-dependent color.
- device-cmyk() function, for specifying uncalibrated colors in an output-device-specific CMYK colorspace.
- Addition of named color rebeccapurple.
18. Security and Privacy Considerations
This specification defines "system" colors, which theoretically can expose details of the user’s OS settings, which is a fingerprinting risk. However, these values are now defined to be settings-neutral, and should be implemented in a generic way that does not actually expose system colors.
The system colors, if they actually correspond to the user’s system colors, also pose a security risk, as they make it easier for a malware site to create a dialog that appears to be a system dialog. However, as they are now defined to be "generic", this risk should be eliminated.