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.
In particular, it allows specifying colors in other colorspaces than sRGB; previously, the more saturated colors outside the sRGB gamut could not be used in CSS even if the display device supported them. In addition to the family of RGB colorspaces, colorspaces with other primaries such as CMYK or KCMYGOV are supported.
A draft implementation report is available.
1.1. 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.
2. 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. If two objects have different spectra, but produce the same physical sensation, we say they have the same color.
A colorspace 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 colorspace. This also means that the same color can be expressed in multiple colorspaces, or transformed from one colorspace to another, while 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 colorspaces:
color ( sRGB0.41587 0.503670 0.36664 ); color ( display-p30.43313 0.50108 0.37950 ); color ( a98-rgb0.44091 0.49971 0.37408 ); color ( prophoto-rgb0.36589 0.41717 0.31333 ); color ( rec20200.42210 0.47580 0.35605 );
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 colorspace or a color-producing device are known, it is said to be characterized. 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.
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 colorspaces may be compared by looking at the volume (in cubic LAB units) of colors that can be expressed. The following table examines the predefined colorspaces available in CSS.
Colorspace | Volume (million Lab units) |
---|---|
sRGB | 0.820 |
display-p3 | 1.233 |
a98-rgb | 1.310 |
prophoto-rgb | 2.896 |
rec2020 | 2.042 |
lab | 6.578 |
3. Foreground Color: the color property
Name: | color |
---|---|
Value: | <color> |
Initial: | CanvasText |
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.
4. Representing Colors: the <color> type
CSS has several syntaxes for specifying color values. Some directly specify an sRGB color by its channels, such as the hex color notation or rgb() function. Others are more human-friendly to write and understand, such as the hsl() and lch() functions, or the long list of named colors.
4.1. The <color> syntax
Colors are represented as a list of 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 behind the color.
Colors in CSS are represented by the <color> type:
<color> = <hex-color> | <named-color> | currentcolor | transparent | <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hwb()> | <lab()> | <lch()> | <color()> | <device-cmyk()> | <system-color>
The color-functions are <rgb()>, <rgba()>, <hsl()>, <hsla()>, <hwb()>, <lab()>, <lch()>, and <color()>.
Of those, <hsl()>, <hsla()>, <hwb()> and <lch()> are cylindrical polar color representations, which specify color using a <hue> 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 other colorspaces are rectangular orthogonal color representations, which specify color using three (or more, in the case of CMYK or CMYKOGV) orthogonal component axes.
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—
4.2. The <hue> 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> = <number> | <angle>
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 angles and spacing corresponding to particular hues depend on the colorspace. For example, in HSL and HWB, which use the sRGB colorspace, 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).
For colors very close to the neutral axis, the hue angle becomes indeterminate (for example, in Lab, minute changes in near-zero a and b values give huge changes in LCH hue angle). Therefore, sometimes a hue angle of NaN (not a number) may be returned. This impacts, for example, hue interpolation.
4.3. Accessibility and Conveying Information By Color
Although colors can add significant information to documents and make them more readable, color by itself should not be the sole means to convey important information. Authors should consider the W3C Web Content Accessibility Guidelines [WCAG21] when using color in their documents.
4.4. 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 colorspace, without including the sRGB 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.
If the color profile or other identifying information is invalid, the image is treated as untagged images
For example, when a browser running on a system with a Display P3 monitor displays an JPEG image tagged as being in the Adobe® RGB (1998) [AdobeRGB] colorspace, it must convert the colors from Adobe® RGB (1998) to Display P3 so that they display correctly. It must not treat the Adobe® RGB (1998) values as if they were Display P3 values, which would produce incorrect colors.
4.5. Color Spaces of Untagged Colors
Colors specified in HTML, and untagged images must be treated as being in the sRGB color space ([SRGB]) unless otherwise specified.
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 untagged videos, since untagged video should be presumed to be in an ITU-defined colorspace.
-
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
4.6. Resolving <color> Values
Unless otherwise specified for a particular property, specified colors are resolved to computed colors and then further to used colors as described below.
The resolved value of a <color> is its used value.
4.6.1. Resolving sRGB values
This applies to:
-
hwb() values
-
While the function names and named colors are ASCII case-insensitive, the specified value does not preserve any mixed casing and is treated as being all lowercase.
For example, the first form below is treated as identical to the second:
HsL ( 39 100 % 50 % ) hsl ( 39 100 % 50 % ) Similarly the first form below is treated as identical to the second:
pUrPlE purple
The computed and used value is the corresponding sRGB color, paired with the specified alpha channel and defaulting to opaque if unspecified).
4.6.2. Resolving Lab and LCH values
This applies to lab() and lch() values.
The computed and used value is the corresponding CIE Lab color paired with the specified alpha channel (as a <number>, not a <percentage>; and defaulting to opaque if unspecified).
4.6.3. Resolving values of the color() function
The computed and used value is the color in the specified colorspace, paired with the specified alpha channel (as a <number>, not a <percentage>; and defaulting to opaque if unspecified).
For example, the computed value of
color ( display-p30.823 0.6554 0.2537 /1 )
is
color ( display-p30.823 0.6554 0.2537 )
4.6.4. Resolving device-cmyk values
The computed and used value is the specified device-specific CMYK color, (with components as <number>, not <percentage>) paired with the specified alpha channel (as a <number>, not a <percentage>; and defaulting to opaque if unspecified).
The actual value can vary based on the operation; for rendering to a CMYK-capable device, it may be rendered as a CMYK color; for blending with non-CMYK colors or rendering to a non-CMYK device, it must be converted as specified in § 12 Device-dependent CMYK Colors: the device-cmyk() function.
For example,
device-cmyk ( 0 % 70 % 20 % 0 % )
has the specified and actual value
device-cmyk ( 0 0.7 0.2 0 )
and will, if the implementation understands ICC profiles and has the recommeneded profile installed, have the used value
lab ( 63.673 % 51.577 5.811 )
As with all colors, the used value is not available to script.
4.6.5. Resolving other colors
This applies to system colors, deprecated-colors, transparent, and currentcolor.
Each <system-color> keyword computes to itself. Its used value is the corresponding color in its color space.
The computed and used value of transparent is transparent black.
The currentcolor keyword computes to itself.
In the color property, the used value of 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.
4.7. Serializing <color> Values
This section updates and replaces that part of CSS Object Model, section Serializing CSS Values, which relates to serializing <color> values.
In this section, the strings used in the specification and the corresponding characters are as follows.
String | Character |
---|---|
" " | U+0020 SPACE |
"," | U+002C COMMA |
"-" | U+002D HYPHEN-MINUS |
"." | U+002E FULL STOP |
"/" | U+002F SOLIDUS |
The string "." shall be used as a decimal separator, regardless of locale, and there shall be no thousands separator.
4.7.1. Serializing alpha values
This applies to any <color> value which can take an optional alpha value.
If the alpha is exactly 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 a <number>, not a <percentage>. 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, 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 up the value of 5 because the following digit is 6) or any longer, rounded serialization of the same form.
4.7.2. Serializing sRGB values
The serialized form of the following sRGB values:
-
hwb() values
is derived from the computed value and thus, uses either the rgb() or rgba() form (depending on whether the alpha is exactly 1, or not), with lowercase letters for the function name.
For compatibility, the sRGB component values are serialized in <number> form, not <percentage>). 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 compatibiity, 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 <number> not <integer>. Thus, any higher precision than eight bits is indicated with a fractional part.
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 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, not truncated.
Note: authors of scripts which expect color values returned from getComputedStyle to have <integer> component values, are advised to update them to cope with <number>.
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)".
The serialized value of
goldenrod
is the string "rgb(218, 165, 32)" and not the string "rgb(218.000, 165.000, 32.000)"
4.7.3. Serializing Lab and LCH values
The serialized form of lch() and lab() values is derived from the computed value and uses the lab() form, with lowercase letters for the function name.
The component values are serialized in base 10; the Lightness component is serialized as <percentage>, while the a and b component values are serialized as <number>. A single ASII space character " " must be used as the separator between the component values.
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 305 )
is the string "lab(37% 60.23 -86.01)" and not "lab(37.00% 60.23 -86.01)" or "lab(37.% 60.23 -86.01)" or "lch(37% 105 305)".
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 twelve bit precision; this will result in at least two decimal places unless trailing zeroes have been omitted. (16bit, or half float, is recommended for internal storage). Values must be rounded, not truncated.
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.
4.7.4. Serializing values of the color() function
The serialized form of color() values is derived from the computed value and uses the color() form, with lowercase letters for the function name and the colorspace name.
The component values are serialized in base 10, as <number>. A single ASCII space character " " must be used as the separator between the component values, and also between the colorspace name and the first color component.
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 ( dIsPlAy-P30.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.
The serialized value of the color in
@color-profile --swop5c{ src : url ( 'https://example.org/SWOP2006_Coated5v2.icc' ); } .header{ background-color : color ( --swop5c0 % 70.0 % 20.00 % .0 % ); }
is the string "color(--swop5c 0 0.7 0.2 0)"
If the colorspace is sRGB, the colorspace must be omitted in the serialized result.
The precision with which color() component values are retained, and thus the number of significant figures in the serialized value, is not defined in this specification, but for RGB and CMYK colorspaces must at least be sufficient to round-trip values with eight bit precision; this will result in at least two decimal places unless trailing zeroes have been omitted. (16bit, or half float, is recommended for internal storage). Values must be rounded, not truncated.
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-rgb0.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.
4.7.5. Serializing device-cmyk values
The serialized form of device-cmyk() values is derived from the computed value and uses the device-cmyk() form, with lowercase letters for the function name.
The component values are serialized in base 10, as <number>. A single ASCII space character " " must be used as the separator between the component values.
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 the color
device-cmyk ( 0 81 % 81 % 30 % )
is the string "device-cmyk(0 0.81 0.81 0.3)"
The precision with which device-cmyk() 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 values with eight bit precision; this will result in at least two decimal places unless trailing zeroes have been omitted. Values must be rounded, not truncated.
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 black ("k") color component value from the alpha value.
4.7.6. Serializing other colors
This applies to system colors, deprecated-colors, transparent, and currentcolor.
The serialized form of these values is derived from the computed value and uses lowercase letters for the color name.
The serialized form of transparent is the string "rgba(0, 0, 0, 0)".
The serialized form of currentColor is the string "currentcolor".
5. sRGB Colors
CSS colors in the sRGB color space
are represented by a triplet of values—
CSS also allows the use of non-sRGB colorspaces, as described in § 10.2 Predefined colorspaces: srgb, display-p3, a98-rgb, prophoto-rgb, rec2020, xyz and lab'..
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.
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 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 <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. Color Keywords
In addition to the various numeric syntaxes for <color>s,
CSS defines several sets of color keywords that can be used instead—
6.1. 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 <ident>, accepted anywhere a <color> is. As usual for CSS-defined <ident>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.
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 his talk “Peachpuffs and Lemonchiffons”.
6.2. System Colors
In general, the <system-color> 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 <system-color> keywords also respond to light mode or dark mode changes.
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 active, authors should use the <system-color> keywords as color values in properties other than those listed in CSS Color Adjust §3.1 Properties Affected by Forced Colors Mode, to ensure legibility and consistency across the page and avoid an uncoordinated mishmash of user-forced and page-chosen colors.
When the values of <system-color> 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 <system-color> keywords are defined as follows:
- Canvas
- Background of application content or documents.
- CanvasText
- Text in application content or documents.
- LinkText
- Text in non-active, non-visited links. For light backgrounds, traditionally blue.
- VisitedText
- Text in visited links. For light backgrounds, traditionally purple.
- ActiveText
- Text in active links. For light backgrounds, traditionally red.
- ButtonFace
- The face background color for push buttons.
- ButtonText
- Text on push buttons.
- ButtonBorder
- The base border color for push buttons.
- Field
- Background of input fields.
- FieldText
- Text in input fields.
- Highlight
- Background of selected items/text.
- HighlightText
- Text of selected items/text.
- 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). - GrayText
- Disabled text. (Often, but not necessarily, gray.)
Note: As with all other 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.
-
ButtonFace or Field background with a ButtonBorder border and adjacent color Canvas'
-
Highlight background with HighlightText foreground.
Additionally, GrayText is expected to be readable, though possibly at a lower contrast rating, over any of the backgrounds.
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 FieldText
Mark with GrayText GrayText
Highlight with HighlightText HighlightText
Highlight with GrayText GrayText
Earlier versions of CSS defined additional <system-color>s, which have since been deprecated. These are documented in Appendix A: Deprecated CSS System Colors.
Note: The <system-color>s incur some privacy and security risk, as detailed in § 16 Security and Privacy Considerations.
6.3. The transparent keyword
The keyword transparent specifies a transparent black. It is a type of <named-color>.
6.4. 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 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 isse 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.
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> ]? )
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% 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 clamped to those values at computed value time, before being converted to an sRGB 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%.
HSL colors resolve to sRGB.
An advantage of HSL over RGB is that it is more intuitive: people can guess at the colors they want, and then tweak.
hsl ( 120 deg 100 % 50 % ) lime greenhsl ( 120 deg 100 % 25 % ) dark greenhsl ( 120 deg 100 % 75 % ) light greenhsl ( 120 deg 75 % 85 % ) pastel green
An advantage of HSL over LCH is that, regardless of manipulation, the result always lies inside the sRGB gamut. A disadvantage of HSL over LCH 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 maipulating 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.
In LCH, sRGB blue is lch(29.6% 131.2 301.3) while sRGB yellow is lch(97.6% 94.7 99.6). The LCH Lightnesses of 29.6% and 97.6% 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.
In LCH, the same pair of colors lch(42.1% 97.4 290.6) and lch(30.8% 129.7 302.1) have a hue difference of 302.1-290.6 = 11.5deg while the second pair lch(86.8% 86.2 87.3) and lch(92.0% 98.8 119.1) have a hue difference of 119.1-87.3 = 31.8deg, correctly reflecting the visual separation of hues.
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.
<https://github.com/w3c/csswg-drafts/issues/3088>
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.
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 are not invalid, but are clamped to the ranges defined here at computed-value time. If the sum of these two arguments is greater than 100%, then at computed-value time they are further 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, i.e. 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 first normalizes the white and black components, so their sum is no larger than 100%.
white/= 100 ; black/= 100 ; if ( white+ black>= 1 ) { var gray= white/ ( white+ black); return [ gray, gray, gray]; } 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. Converting sRGB colors to HWB colors
Conversion in the reverse direction proceeds similarly.
function rgbToHwb( red, green, blue) { var hsl= rgbToHsl( rgb); var white= Math. min(... rgb); var black= 1 - Math. max(... rgb); return ([ hsl[ 0 ], white* 100 , black* 100 ]); }
8.3. 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 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 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. This value is usually written as a unitless number; for compatibility with the rest of CSS, it is written as a percentage. 100% means an L value of 100, not 1.0. L=0% is deep black (no light at all) while L=100% is a diffuse white
Values of L 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.
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 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 [Bradford-CAT] is the industry standard chromatic adaptation transform, and is easy to calculate as it is a simple matrix multiplication.
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 Lightness 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.
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( <percentage> <number> <number> [ / <alpha-value> ]? )
The first argument specifies the CIE Lightness. This is typically a number between 0% (representing black) and 100% (representing white), 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 clamped to 0% at computed-value time; values greater than 100% are permitted (for forwards compatibility with High Dynamic Range (HDR), and must not be clamped.
The second and third arguments are the distances along the "a" and "b" axes 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().
lab ( 29.2345 % 39.3825 20.0664 ); lab ( 52.2345 % 40.1645 59.9971 ); lab ( 60.2345 , -5.3654 58.956 ); lab ( 62.2345 % -34.9638 47.7721 ); lab ( 67.5345 % -8.6911 -41.6019 );
lch() = lch( <percentage> <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 at computed-value time.
The third argument is the hue angle. It’s interpreted similarly to the <hue> 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 alpha value, separated by a slash, and interpreted identically to the <alpha-value> in rgb().
lch ( 29.2345 % 44.2 27 ); lch ( 52.2345 % 72.2 56.2 ); lch ( 60.2345 % 59.2 95.2 ); lch ( 62.2345 % 59.2 126.2 ); lch ( 67.5345 % 42.5 258.2 );
9.2. Converting RGB colors to Lab colors
Conversion from an RGB colorspace to Lab requires several steps, although in practice all but the first step are linear calculations and can be combined. Taking sRGB as an example:
- Convert from sRGB to linear-light sRGB (undo gamma encoding). This has the same gamut as sRGB, but is now additive, so that arithmetic operations on the values give the correct results.
- 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 [Bradford-CAT]
- Convert D50-adapted XYZ to Lab
There is sample JavaScript code for this conversion for various RGB colorspaces, in § 15 Sample code for color conversions.
9.3. Converting Lab colors to RGB colors
For display on a screen, Lab colors will need to be converted to the colorspace of the display.
Conversion from Lab to an RGB space also requires multiple steps, and again in practice all but the last step are linear calculations and can be combined. Taking display-p3 as an example,
- Convert Lab to (D50-adapted) XYZ
- Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint used in display-p3, with the Bradford transform
- Convert from (D65-adapted) CIE XYZ to linear-light display-p3
- Convert from linear-light display-p3 to display-p3 (do gamma encoding)
There is sample JavaScript code for this conversion for various RGB colorspaces, in § 15 Sample code for color conversions.
9.4. Converting CMYK colors to Lab
Conversion from a calibrated CMYK colorspace to Lab is typically dones by looking up the Lab values in an ICC profile.
9.5. Converting Lab colors to CMYK
For print, Lab colors will need to be converted to the colorspace of the display.
This is typically dones by looking up the CMYK values in an ICC profile.
9.6. 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.7. Converting LCH colors to Lab colors
Conversion to Lab is trivial:
- a = C cos(H)
- b = C sin(H)
- L is the same
10. Profiled, Device-dependent Colors
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, or any other color or monochrome output device which has been characterized.
In addition, for convenience, CSS provides several predefined color spaces including display-p3' [DCI-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.
These can be used without supplying a separate profile.
color : color ( --swopc0.0134 0.8078 0.7451 0.3019 ); color : color ( --indigo0.0941 0.6274 0.3372 0.1647 0 0.0706 0.1216 ); color : color ( prophoto-rgb0.9137 0.5882 0.4784 ); color : color ( display-p30.3804 0.9921 0.1412 );
The colors not using a predefined colorspace are distinguished by their use of <dashed-ident> and 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' );}
10.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 most of the other color functions operate in). Its syntax is:
color() = color( [ [<ident> | <dashed-ident>]? [ <number-percentage>+ | <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 represents either an invalid color, as described below, or a valid color.
A color may be a valid color but still be outside the range of colors that can be produced by an output device (a screen, projector, or printer). It is said to be out of gamut for that colorspace.
An out of gamut color has component values less than 0 or 0%, or greater than 1 or 100%. These are are not invalid; instead, they are gamut-mapped using a relative colorimetric intent which brings the values within the range 0/0% to 1/100% at computed-value time.
Each valid color is either in-gamut for the output device (screen, or printer), or it is out of gamut.
Each argument has the following form:
-
An optional <ident> or <dashed-ident> denoting the colorspace. If this is an <ident> it denotes one of the predefined colorspaces (such as display-p3); if it is a <dashed-ident> it denotes a custom colorspace, defined by a @color-profile rule. If omitted, it defaults to the predefined srgb color profile.
If the <ident> names a non-existent colorspace (a name that does not match one of the predefined colorspaces), or a predefined but unsupprted colorspace, this argument represents an invalid color.
If the <dashed-ident> names a non-existent colorspace ( a name that does not match an color profile’s name, or which matches but the corresponding profile has not loaded, or does not represent a valid profile), this argument represents an invalid color.
-
Either one or more <number>s or <percentage>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
-
For custom colorspaces, specified component values less than 0 or 0%, or greater than 1 or 100% are not invalid; they are clamped to the valid range at computed value time. This is because ICC profiles typically do not accept out of range input values.
For predefined colorspaces, specified component values less than 0 or 0%, or greater than 1 or 100% are not invalid; these out of gamut colors are gamut mapped to the valid range at computed value time, with a relative colorimetric intent.
If more <number>s or <percentage>s are provided than parameters that the colorspace takes, the excess <number>s at the end are ignored. The color is still a valid color.
If fewer <number>s or <percentage>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.) The color is still a valid color.
If a <string> is provided, this argument represents an invalid color.
- If the colorspace defines named colors
- If <number>s or <percentage>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.
This final argument can again be either a valid color or an invalid color; and if valid it can be either in-gamut for the output device, or out of gamut.
A color which is either an invalid color or an out of gamut color can’t be displayed.
The color() function represents the color specified by the first of its arguments that can be displayed, (that is, the first argument that isn’t an invalid color and isn’t out of gamut). If all of its arguments can’t be displayed], color() represents opaque black.
color ( rec20200.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
color ( rec20200.42053 0.979780 0.00579 , color ( display-p3 ( 0 1 0 )));
On a display-p3 screen, because the first-choice color can’t be displayed and because a fallback has been given which is in-gamut for the display, the fallback color will be used.
color ( prophoto-rgb0.4835 0.9167 0.2188 , display-p30.3499 0.9800 0.1399 , rgb ( 33.44 % 96.68 % 21.07 % ))
color ( profoto-rgb0.4835 0.9167 0.2188 , display-p30.3499 0.9800 0.1399 , rgb ( 33.44 % 96.68 % 21.07 % ))
10.2. Predefined colorspaces: srgb, display-p3, a98-rgb, prophoto-rgb, rec2020, xyz and lab'.
The following colorspaces are predefined for use in the color() function. They are used without any @color-profile rule.
- 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 0.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.
- display-p3
-
The display-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.
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:
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 - a98-rgb
-
The a98-rgb 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.
a98-rgb is compatible with Adobe® RGB (1998) [AdobeRGB].
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 a98-rgb 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.6400 0.3300 Green chromaticity 0.2100 0.7100 Blue chromaticity 0.1500 0.0600 White chromaticity 0.3127 0.3290 Transfer function 256/563 White luminance 160.0 cd/m2 Black luminance 0.5557 cd/m2 - prophoto-rgb
-
The prophoto-rgb 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,
and a small linear portion near black.
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 hyper-saturated, non physically realizable primaries. These were chosen to allow a wide color gamut and in particular, 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 prophoto-rgb 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.734699 0.265301 Green chromaticity 0.159597 0.840403 Blue chromaticity 0.036598 0.000105 White chromaticity 0.345704 0.358540 Transfer function see below White luminance 160.0 to 640.0 cd/m2 Black luminance See text var Cl; if ( C<= ( 16 / 512 )) Cl= C/ 16 ; else Cl= Math. pow( C, 1.8 ); C is the red, green or blue component.
- 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.
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:
x y Red chromaticity 0.708 0.292 Green chromaticity 0.170 0.797 Blue chromaticity 0.131 0.046 White chromaticity 0.3127 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] - xyz
- The xyz colorspace accepts three numeric parameters, representing the X,Y and Z values. It represents the CIE XYZ colorspace, scaled such that diffuse white has a luminance (Y) of 1.0 and, if necessary, chromatically adapted to a D50 reference white. Values greater than 1.0 are allowed and must not be clamped; they represent colors brighter than diffuse white. Values less than 0 are uncommon, but can occur as a result of chromatic adaptation, and likewise must not be clamped.
- lab
- The lab [CIELAB] colorspace accepts one percentage and two numeric parameters, representing the L, a, and b values. The meaning and valid ranges are the same as the lab() function.
10.2.1. Converting predefined colorspaces to Lab
For all 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 sRGB, display-p3, a98-rgb and rec2020) to the D50 whitepoint used in Lab, with the Bradford transform. prophoto-rgb' already has a D50 whitepoint.
- Convert D50-adapted XYZ to Lab
10.2.2. Converting Lab to predefined colorspaces
Conversion from Lab to 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
- Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint used in sRGB and most other RGB spaces, with the 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)
10.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 [<dashed-ident> | device-cmyk] { <declaration-list> }
The <dashed-ident> gives the color profile’s name,
by which it will be used in a CSS stylesheet.
Alternatively, the device-cmyk keyword means that this color profile
will be used to resolve colors specified in device-cmyk.
The @color-profile rule accepts the descriptors defined in this specification.
Name: | src |
---|---|
For: | @color-profile |
Value: | <url> |
Initial: | n/a |
Name: | rendering-intent |
---|---|
For: | @color-profile |
Value: | relative-colorimetric | absolute-colorimetric | perceptual | saturation |
Initial: | relative-colorimetric |
- 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 destination white point. If black point compensation is in use, the source black point must also be mapped to the destination black point. Adaptation algorithms should be used to adjust for the change in white point. Relative relationships of colors inside both source and destination gamuts should be preserved. Relative relationships of colors outside the destination gamut may be changed.
- absolute-colorimetric
- ICC-absolute colorimetric is required to leave source colors that fall inside the destination medium gamut unchanged relative to the adopted white (a perfect reflecting diffuser). Source colors that are out of the destination medium gamut are mapped to colors on the gamut boundary using a variety of different methods. This method produces the most accurate color matching of in-gamut colors, but will result in highlight clipping if the destination medium white point is lower than the source medium white point. For this reason it is recommended for use only in applications that need exact color matching and where highlight clipping is not a concern. This method MUST disable white point matching and black point matching when converting colors. In general, this option is not recommended except for testing purposes.
- perceptual
- This method is often the preferred choice for images, especially when there are substantial differences between the source and destination (such as a screen display image reproduced on a reflection print). It takes the colors of the source image and re-optimizes the appearance for the destination medium using proprietary methods. This re-optimization may result in colors within both the source and destination gamuts being changed, although perceptual transforms are supposed to maintain the basic artistic intent of the original in the reproduction. They will not attempt to correct errors in the source image. Note: With v2 ICC profiles there is no specified perceptual reference medium, which can cause interoperability problems. When v2 ICC profiles are used it 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.
10.4. CSS and print: using calibrated CMYK and other printed colorspaces
The @color-profile at-rule is not restricted to RGB colorspaces. While screens typically display colors directly in RGB, printers often represent colors with CMYK. Calibrated four color print with Cyan, Magenta, Yellow and Black (CMYK), or high-fidelity wide gamut printing with additional inks such as Cyan Magenta Yellow Green Orange Violet Black (CMYGOVB) can also be done in CSS, provided you have an ICC profile corresponding to the combination of inks, paper, total ink coverage and equipment you will use.Here the color() function first states the name we have given the profile, then gives the percentage of cyan, magenta, yellow, and black. In this profile, this resolves to the color Lab(63.673303% 51.576902 5.811058) which is rgb(93.124, 44.098% 57.491%).@color-profile --fogra39{ src : url ( 'https://example.org/Coated_Fogra39L_VIGC_300.icc' ); } .header{ background-color : color ( --fogra390 % 70 % 20 % 0 % ); }
A color checker, used for ensuring color fidelity in the print and photographic industries. Averaged measured Lab values are available for each patch. The rectangles show the Lab values, converted to sRGB. The circles, which are barely visible, show the Lab values, passed through an ICC profile to convert them to CMYK. The CMYK values are then passed through the same ICC profile in reverse, to yield new Lab values. These are then converted to sRGB for display.
The one patch with a more visible circle (third row, first patch) is because the color is slightly outside the gamut of the FOGRA55 CMYK space used.
The table below shows, for each patch, the DeltaE 2000 between the original Lab and the Lab value after round-tripping through CMYK. A DeltaE 2000 of 1 or more is just visible.
0.06 | 0.07 | 0.03 | 0.04 | 0.06 | 0.17 |
0.03 | 0.75 | 0.05 | 0.06 | 0.03 | 0.02 |
1.9 | 0.04 | 0.06 | 0.05 | 0.02 | 0.05 |
0.03 | 0.08 | 0.03 | 0.03 | 0.04 | 0.80 |
In this profile, this amount of CMYK (the same percentages as the previous example) resolves to the color Lab(64.965217% 52.119710 5.406966) which is rgb(94.903% 45.248% 59.104%).@color-profile --swop5c{ src : url ( 'https://example.org/SWOP2006_Coated5v2.icc' ); } .header{ background-color : color ( --swop5c0 % 70 % 20 % 0 % ); }
This example does not use illustrative swatches, because most of the colors are outside of sRGB. This CMYK color corresponds to Lab(56.596645% -58.995875 28.072154) or LCH(56.596645% 65.33421077211648, 154.5533771086801). In sRGB this would be rgb(-60.568% 62.558% 32.390%) which, as the large negative red component shows, is out of gamut. Reducing the chroma until the result is in gamut gives LCH(56.596645% 51 154.5533771086801) which is rgb(8.154% 60.9704% 37.184%) and this is used as the fallback color. For wide gamut screens, the color is inside the display-p3 gamut so no fallback need be specified. However, for the curious, the color is display-p3(0.1658 0.6147 0.3533).@media ( color-gamut: srgb) { .header{ background-color : color ( --fogra3990 % 0 % 90 % 0 % , rgb ( 8.154 % 60.9704 % 37.184 % )); } } @media print, ( color-gamut: p3){ .header{ background-color : color ( --fogra3990 % 0 % 90 % 0 % ); } }
- Pantone Orange 021C
- Pantone Green C
- Pantone Violet C
@color-profile --fogra55beta{ src : url ( 'https://example.org/2020_13.003_FOGRA55beta_CL_Profile.icc' ); } .dark_skin{ background-color : color ( --fogra55beta0.183596 0.464444 0.461729 0.612490 0.156903 0.000000 0.000000 ); } .light_skin{ background-color : color ( --fogra55beta0.070804 0.334971 0.321802 0.215606 0.103107 0.000000 0.000000 ); } .blue_sky{ background-color : color ( --fogra55beta0.572088 0.229346 0.081708 0.282044 0.000000 0.000000 0.168260 ); } .foliage{ background-color : color ( --fogra55beta0.314566 0.145687 0.661941 0.582879 0.000000 0.234362 0.000000 ); } .blue_flower{ background-color : color ( --fogra55beta0.375515 0.259934 0.034849 0.107161 0.000000 0.000000 0.308200 ); } .bluish_green{ background-color : color ( --fogra55beta0.397575 0.010047 0.223682 0.031140 0.000000 0.317066 0.000000 ); }
11. Working Color Space
Issue(300):12. Device-dependent CMYK Colors: the device-cmyk() function
Sometimes, when a given printer has not been calibrated, but the output for particular ink combinations is known through experimentation, or via a printed sample swatchbook, it is useful to express CMYK colors in a device-dependent way. Note: because the actual resulting color may be unknown, CSS processors may attempt to approximate it. This approximation is likely to be visually very far from the actual printed result. The device-cmyk() function allows authors to specify a color in this way:device-cmyk() = device-cmyk( <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% at computed-value time. 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. Typically, print-based applications will actually store the used colors as CMYK, and send them to the printer in that form. However, such colors do not have a colorimetric interpretation, and thus cannot be used in gradients, compositing, blending and so on. As such, Device CMYK colors must be converted to an equivalent color. This is not trivial, like the conversion from HSL or HWB to RGB; the precise conversion depends on the precise characteristics of the output device.
- If the user, author, or user-agent stylesheet has an @color-profile definition for device-cmyk, and the resource specified by the src descriptor can be retrieved, and the resource is a valid CMYK ICC profile, and the user agent can process ICC profiles, the computed value of the device-cmyk() function must be the Lab value of the CMYK color.
- Otherwise, if a valid fallback color has been specified, the computed value of the device-cmyk() function must be that fallback color.
- Otherwise, the computed value of the device-cmyk() function must be the sRGB value of the CMYK color, as converted with the following naive conversion algorithm.
color : device-cmyk ( 0 81 % 81 % 30 % ); color : rgb ( 178 34 34 ); color : firebrick;
color : device-cmyk ( 0 81 % 81 % 30 % ); color : lab ( 45.060 % 45.477 35.459 ) color:rgb ( 70.690 % 26.851 % 19.724 % );
A color checker, used for ensuring color fidelity in the print and photographic industries. Averaged measured Lab values are available for each patch. The rectangles show the Lab values, converted to sRGB. The circles show the Lab values, passed through an ICC profile to convert them to CMYK. The CMYK value are then naively converted to sRGB.
The table below shows, for each patch, the DeltaE 2000 between the original Lab and the Lab value after round-tripping through CMYK. A DeltaE 2000 of 1 or more is just visible, while 5 or more is just a different color altogether.
11.33 | 9.36 | 5.66 | 7.52 | 12.39 | 21.58 |
6.40 | 8.79 | 11.77 | 17.16 | 11.91 | 3.97 |
12.1 | 17.00 | 3.38 | 1.94 | 18.08 | 14.97 |
1.89 | 6.56 | 7.85 | 8.76 | 9.82 | 10.29 |
12.1. Naively Converting Between Uncalibrated CMYK and sRGB-Based Colors
To naively convert from CMYK to RGBA: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.
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
13. 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: | map to the range [0,1] |
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.
13.1. Simple alpha compositing
When drawing, implementations must handle alpha according to the rules in Section 5.1 Simple alpha compositing of [Compositing].14. 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, HTML5, XHTML1, XHTML1.1, XHTML Basic, and other XHTML Family documents./* traditional desktop user agent colors for hyperlinks */ :link{ color : LinkText; } :visited{ color : VisitedText; } :active{ color : ActiveText; } /* a reasonable, conservative default for device-cmyk */ @color-profile device-cmyk{ src : url ( 'https://drafts.csswg.org/css-color-4/ICCprofiles/Coated_Fogra39L_VIGC_300.icc' ); }
15. Sample code for color conversions
This section is not normative. For clarity, a library is used for matrix multiplication.// Sample code for color conversions // Conversion can also be done using ICC profiles and a Color Management System // For clarity, a library is used for matrix multiplication (multiply-matrices.js) // 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 // TODO for negative values, extend linear portion on reflection of axis, then add pow below that 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 // TODO for negative values, extend linear portion on reflection of axis, then add pow below that 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 // also // https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz var M= [ [ 0.4124564 , 0.3575761 , 0.1804375 ], [ 0.2126729 , 0.7151522 , 0.0721750 ], [ 0.0193339 , 0.1191920 , 0.9503041 ] ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_sRGB( XYZ) { // convert XYZ to linear-light sRGB var M= [ [ 3.2404542 , - 1.5371385 , - 0.4985314 ], [ - 0.9692660 , 1.8760108 , 0.0415560 ], [ 0.0556434 , - 0.2040259 , 1.0572252 ] ]; return multiplyMatrices( M, XYZ); } // image-p3-related functions function lin_P3( RGB) { // convert an array of display-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 display-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 display-p3 values to CIE XYZ // using D65 (no chromatic adaptation) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= [ [ 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 multiplyMatrices( M, rgb); } function XYZ_to_lin_P3( XYZ) { // convert XYZ to linear-light P3 var M= [ [ 2.493496911941425 , - 0.9313836179191239 , - 0.40271078445071684 ], [ - 0.8294889695615747 , 1.7626640603183463 , 0.023624685841943577 ], [ 0.03584583024378447 , - 0.07617238926804182 , 0.9568845240076872 ] ]; return multiplyMatrices( M, XYZ); } // prophoto-rgb functions function lin_ProPhoto( RGB) { // convert an array of prophoto-rgb values in the range 0.0 - 1.0 // to linear light (un-companded) form. // Transfer curve is gamma 1.8 with a small linear portion // TODO for negative values, extend linear portion on reflection of axis, then add pow below that const Et2= 16 / 512 ; return RGB. map( function ( val) { if ( val<= Et2) { return val/ 16 ; } return Math. pow( val, 1.8 ); }); } function gam_ProPhoto( RGB) { // convert an array of linear-light prophoto-rgb in the range 0.0-1.0 // to gamma corrected form // Transfer curve is gamma 1.8 with a small linear portion // TODO for negative values, extend linear portion on reflection of axis, then add pow below that const Et= 1 / 512 ; return RGB. map( function ( val) { if ( val>= Et) { return Math. pow( val, 1 / 1.8 ); } return 16 * val; }); } function lin_ProPhoto_to_XYZ( rgb) { // convert an array of linear-light prophoto-rgb 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= [ [ 0.7977604896723027 , 0.13518583717574031 , 0.0313493495815248 ], [ 0.2880711282292934 , 0.7118432178101014 , 0.00008565396060525902 ], [ 0.0 , 0.0 , 0.8251046025104601 ] ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_ProPhoto( XYZ) { // convert XYZ to linear-light prophoto-rgb var M= [ [ 1.3457989731028281 , - 0.25558010007997534 , - 0.05110628506753401 ], [ - 0.5446224939028347 , 1.5082327413132781 , 0.02053603239147973 ], [ 0.0 , 0.0 , 1.2119675456389454 ] ]; return multiplyMatrices( M, XYZ); } // a98-rgb functions function lin_a98rgb( RGB) { // convert an array of a98-rgb values in the range 0.0 - 1.0 // to linear light (un-companded) form. // negative values are also now accepted return RGB. map( function ( val) { return Math. pow( Math. abs( val), 563 / 256 ) * Math. sign( val); }); } function gam_a98rgb( RGB) { // convert an array of linear-light a98-rgb in the range 0.0-1.0 // to gamma corrected form // negative values are also now accepted return RGB. map( function ( val) { return Math. pow( Math. abs( val), 256 / 563 ) * Math. sign( val); }); } function lin_a98rgb_to_XYZ( rgb) { // convert an array of linear-light a98-rgb values to CIE XYZ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html // has greater numerical precision than section 4.3.5.3 of // https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf // but the values below were calculated from first principles // from the chromaticity coordinates of R G B W // see matrixmaker.html var M= [ [ 0.5766690429101305 , 0.1855582379065463 , 0.1882286462349947 ], [ 0.29734497525053605 , 0.6273635662554661 , 0.07529145849399788 ], [ 0.02703136138641234 , 0.07068885253582723 , 0.9913375368376388 ] ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_a98rgb( XYZ) { // convert XYZ to linear-light a98-rgb var M= [ [ 2.0415879038107465 , - 0.5650069742788596 , - 0.34473135077832956 ], [ - 0.9692436362808795 , 1.8759675015077202 , 0.04155505740717557 ], [ 0.013444280632031142 , - 0.11836239223101838 , 1.0151749943912054 ] ]; return multiplyMatrices( M, XYZ); } //Rec. 2020-related functions function lin_2020( RGB) { // convert an array of rec2020 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 rec2020 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 rec2020 values to CIE XYZ // using D65 (no chromatic adaptation) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= [ [ 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 multiplyMatrices( M, rgb); } function XYZ_to_lin_2020( XYZ) { // convert XYZ to linear-light rec2020 var M= [ [ 1.7166511879712674 , - 0.35567078377639233 , - 0.25336628137365974 ], [ - 0.6666843518324892 , 1.6164812366349395 , 0.01576854581391113 ], [ 0.017639857445310783 , - 0.042770613257808524 , 0.9421031212354738 ] ]; return multiplyMatrices( M, XYZ); } // 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= [ [ 1.0478112 , 0.0228866 , - 0.0501270 ], [ 0.0295424 , 0.9904844 , - 0.0170491 ], [ - 0.0092345 , 0.0150436 , 0.7521316 ] ]; return multiplyMatrices( M, XYZ); } function D50_to_D65( XYZ) { // Bradford chromatic adaptation from D50 to D65 var M= [ [ 0.9555766 , - 0.0230393 , 0.0631636 ], [ - 0.0282895 , 1.0099416 , 0.0210077 ], [ 0.0122982 , - 0.0204830 , 1.3299098 ] ]; return multiplyMatrices( M, XYZ); } // 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 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 <deprecated-colors> sub-type, and are defined as:
- ActiveBorder
- Active window border. Same as ButtonBorder.
- ActiveCaption
- Active window caption. Same as CanvasText.
- 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.
- Window
- Window background. Same as Canvas.
- WindowFrame
- Window frame. Same as ButtonBorder.
- WindowText
- Text in windows. Same as CanvasText.
Acknowledgments
Thanks for the feedback from Emilio Cobos Álvarez, Marc Attinasi, Chris Bai, Amelia Bellamy-Royds, Lars Borg, Mike Bremford, Bert Bos, Andreu Botella, Dan Burzo, Joe Clark, Max Derhak, fantasai, Simon Fraser, Patrick Garies, Tony Graham, Ian Hickson, Phil Green, Dean Jackson, Andreas Kraushaar, Pierre-Anthony Lemieux, Susan Lesch, Alex LeDonne, Cameron McCormack, Krzysztof Maczyński, Chris Moschini, Chris Murphy, Jonathan Neal, Chris Needham, Christoph Päper, Steven Pemberton, David Perrell, Brad Pettit, Jacob Refstrup, Craig Revie, Melanie Richards, Florian Rivoal, Joseph Salowey, imon Sapin, Dave Singer, Igor Snitkin, Jonathan Stanley, Andrew Thompson, Lea Verou, Mark Watson, Russ Weakley, Natalie Weizenbaum, Etan Wexler, David Woolley, Boris Zbarsky, Steve Zilles, the XSL FO subgroup of the XSL working group, the Color on the Web community group and all the rest of the www-style community.
Changes
Changes since Working Draft of 5 November 2019
- Export some terms for use in other specifications
- Update requirement from WCAG 2.0 to 2.1
- Fully specify Unicode characters used for serialization
- Define serialization of special named colors
- Define serializatin of device-cmyk()
- Define serialization of color()
- Fully define RGB serialization, in maximally web-compatible way
- Define serialization of Lab and LCH
- Fully define serialization of alpha values
- Consistency pass to avoid accidental RFC2119
- Add IDs to all the examples, to enable referencing
- Separate resolved color and serialized color sections
- (Security) ICC profiles have no executable code
- Define what out-of-range means for profiled colors
- Clarify out-of-range clamping
- Add examples of specified values
- Clarify computed values
- Resst fingerprinting, with mandatory mappings for deprecated system colors
- Added explanatory note on history and reason for standardizing X11 colors
- Correct hwb sample code
- Add table of DeltaE2000 values for MacBeth patches
- Add note on ICC profe Internet Media type
- Add reference to PNG sRGB chunk
- Clarify CMYK to Lab interconversion
- Clarify RGB to Lab interconversion
- More comparison of HSL vs. LCH
- More descripton for Rec BT.2020 colorspace
- Updated description of prophoto-rgb
- Removed duplicate "keywords" from Value Definitions section
- Added an example of an invalid color
- Added example with multiple fallbacks
- Assorted typos and markup fixes
- Clarify handling for undeclared custom colorspaces
- Clarify some examples and explanatory notes
- Handling for valid and invalid ICC profiles
- Define handling for images with explicit tagged colorspace
- Define clorspace for 4k, SDR video
- State that user contrast settings mst take precedence
- Clarify meaning of system colors outside for forced-color mode
- Update default style rules
- Add CIE XYZ colorspace to color()
- Greater clarity on hue angles, NaN explicitly allowed
- Improve section on system color pairings, require AA accessible contrast
- Warn of interaction between overlapping glyphs and the opacity property
- Correct grammar in color definition
- Improve description of Highlight/HighlightText
- Correct prophoto-rgb transfer function
- More precision for prophoto-rgb primaries
- Started to define "can’t be displayed"
- Removed paragraph about canvas surface
- Added the buttonborder, mark, and marktext system colors
- Added reverse conversion, sRGB to HWB
- Clarified polar spaces are cylindrical, not spherical
- Added an Accessibility Considerations section
- Started to describe chroma-reduction gamut mapping rather than per-component clipping
- Corrected white chromaticity for rec2020
- Made device-cmyk available by @color-profile; updated CMYK to color algorithm to only use naive conversion as a last resort
- Added print-oriented CMYK and KCMYOGV examples
- User-defined colorspaces now dashed-ident, making predefined colorspaces extensible without clashes
- Added lab option to the color() function
- Added normative reference for CIE Lab
- Clarified that prophoto-rgb uses D50 whitepoint so does not require adaptation
- Clarified direction of increasing angle in LCH
- Clarified that color names are ASCII case insensitive
- Initial value of the "color" property is now CanvasText
- Removed confusing gray() function per CSS WG resolution
- Collect scattered definitions into new Color terminology section
- Add helpful figures and more examples
- Minor editorial clarifications, spell check, fixing typos, bikeshed markup fixes
Changes since Working Draft of 05 July 2016
- 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 colorspaces
- 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 colorspaces used in video
- Add prophoto-rgb predefined colorspace
- Correct black and white luminance levels for display-p3
- Clarify display-p3 transfer function
- Add a98-rgb colorspace, 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
- 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:
- hwb() function, for specifying colors in the HWB notation.
- predefined, wide color gamut RGB colorspaces
- lab() and lch() functions, for device-independent color
- color() function and @color-profile at-rule, for profiled device-dependent color, including calibrated CMYK.
- device-cmyk() function, for specifying uncalibrated colors in an output-device-specific CMYK colorspace.
- Serialization of <color> now specified here, rather than in CSS Object Model
- Addition of named color rebeccapurple.
16. 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.
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 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.
This specification adds to CSS the on-demand downloading of ICC profiles. These do not contain executable code, and thus do not consitiute an increased security risk.
17. Accessibility Considerations
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 migraine or epilepsy sufferers), the CSSWG was unable to require a specific contrast level.