CSS Color Module Level 4

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-color-4/
Latest published version:
https://www.w3.org/TR/css-color-4/
Previous Versions:
Implementation Report:
https://wpt.fyi/results/css/css-color
Feedback:
CSSWG Issues Repository
Editors:
Tab Atkins Jr. (Google)
Chris Lilley (W3C)
Lea Verou (Invited Expert)
Former Editor:
L. David Baron (Mozilla)
Suggest an Edit for this Spec:
GitHub Editor
Test Suite:
https://wpt.fyi/results/css/css-color/

Abstract

This specification describes CSS <color> values, and properties for foreground color and group opacity.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-color” in the title, like this: “[css-color] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list [email protected].

This document is governed by the 18 August 2025 W3C Process Document.

1. Introduction

This section is not normative.

Tests

This section is not normative, it does not need tests.


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 color spaces than sRGB; previously, the more saturated colors outside the sRGB gamut could not be used in CSS even if the display device supported them.

A draft implementation report is available.

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

Tests

This section provides definitions used later, it does not need tests.


A color is a definition (numeric or textual) of the human visual perception of a light or a physical object illuminated with light. The objective study of human color perception is termed colorimetry.

The color of a physical object depends on how much light it reflects at each visible wavelength, plus the actual color of the light illuminating it (again, the amount of light at each wavelength). It is measured by a spectrophotometer .

The color of something that emits light (including colors on a computer screen) depends on how much light it emits at each visible wavelength. It is measured by a spectroradiometer.

If two objects have different spectra, but still produce the same physical sensation, we say they have the same color. We can calculate whether two colors are the same by converting the spectra to CIE XYZ (three numbers).

For example a green leaf, a photograph of that leaf displayed on a computer screen, and a print of that photograph, are all producing a green sensation by different means. If the screen and the printer are calibrated, the green in the leaf, and the photo, and the print will look the same.

A color space is an organization of colors with respect to an underlying colorimetric model, such that there is a clear, objectively-measurable meaning for any color in that color space. This also means that the same color can be expressed in multiple color spaces, or transformed from one color space to another, while still looking the same.

A leaf is measured with a spectrophotometer and found to have the color lch(51.2345% 21.2 130) which is lab(51.2345% -13.6271 16.2401).

This same color could be expressed in various color spaces:

 color(sRGB 0.41587 0.503670 0.36664);
 color(display-p3 0.43313 0.50108 0.37950);
 color(a98-rgb 0.44091 0.49971 0.37408);
 color(prophoto-rgb 0.36589 0.41717 0.31333);
 color(rec2020 0.6295 0.9657 0.3633);

An additive color space means that the coordinate system is linear in light intensity. The CIE XYZ color space is an additive color space. The Y component of XYZ is the luminance, the light intensity per unit area, or 'how bright it is'. Luminance is measured in candelas per square meter. cd/m², also called nits.

In an additive color space, calculations can be done to accurately predict color mixing. Most RGB spaces are not additive, because the components are gamma encoded. Undoing this gamma encoding produces linear-light values.

For example, if a light fixture contains two identical colored lights, and only one is switched on, and the color is measured to be color(xyz 0.13 0.12 0.04), then the color when both are switched on will be exactly twice that, color(xyz 0.26 0.24 0.08).

If we have two differently colored spotlights shining on a stage, and one has the measured value color(xyz 0.15 0.24 0.17) while the other is color(xyz 0.11 0.06 0.06) then we can accurately predict that if the colored beams are made to overlap, the color of the mixture will be the sum of the XYZ component values, or color(xyz 0.26 0.30 0.23).

A chromaticity is a color measurement where the lightness component has been factored out. From the identical lights example above, the u',v' chromaticity with one light is (0.2537, 0.5268) and the chromaticity is the same with both lights (they are the same color, it is just brighter).

Chromaticities are additive, so they accurately predict the chromaticity (but not the resulting lightness) of a mixture. Being two-dimensional, chromaticity is easily represented on a chromaticity diagram to predict the chromaticity of a color mixture. Any two colors can be mixed, and the resulting colors will lie on the line joining them on the diagram. Three colors form a plane, and the resulting colors will lie in the triangle they form on the diagram.

uv chromaticity diagram of the display-p3 color space
A chromaticity diagram showing (in solid colors) the display-p3 color space and for comparison (faded) the sRGB color space. The white point (D65) is also shown.

Thus, once linearized, RGB color spaces are additive, and their gamut is defined by the chromaticities of the red, green and blue primaries, plus the chromaticity of the white point (the color formed by all three primaries at full intensity).

Most color spaces use one of a few daylight-simulating white points, which are named by the correlated color temperature (CCT) [Understanding_CCT] of the corresponding black-body radiator. For example, D65 is a daylight whitepoint corresponding to a correlated color temperature of 6500 Kelvin (actually 6504, because the value of Plank’s constant has changed since the color was originally defined).

To avoid cumulative round-trip errors, it is important that the identical chromaticity values are used consistently, at all places in a calculation. Thus, for maximum compatibility, for this specification, the following two standard daylight-simulating white points are defined:

Name  x y CCT
D50 0.345700 0.358500 5003K
D65 0.312700 0.329000 6504K

When the measured physical characteristics (such as the chromaticities of the primary colors it uses, or the colors produced in response to a given set of inputs) of a color space or a color-producing device are known, it is said to be characterized.

If in addition adjustments have been made so that a device meets calibration targets such as white point, neutrality of greys, predictability and consistency of tone response, then it is said to be calibrated.

Real physical devices cannot yet produce every possible color that the human eye can see. The range of colors that a given device can produce is termed the gamut (not to be confused with gamma). Devices with a limited gamut cannot produce very saturated colors, like those found in a rainbow.

A top-down view of three gamuts, plotted in Oklab with the positive a-axis towards the right and the positive b-axis towards the top; looking down the l-axis so white and neutrals are in the center. The largest of the three gamuts is ITU Rec BT.2020; the medium-sized one is Display P3, and the smallest is sRGB. Rendering by Alexey Ardov.

The gamuts of different color spaces may be compared by looking at the volume (in cubic Lab units) of colors that can be expressed. The following table examines the predefined color spaces available in CSS.

color space Volume (million Lab units)
sRGB 0.820
display-p3 1.233
a98-rgb 1.310
prophoto-rgb 2.896
rec2020 2.042

A color in CSS is either an invalid color, as described below for each syntactic form, or a valid color.

Any color which is not an invalid color is a valid color.

A color may be a valid color but still be outside the range of colors that can be produced by an output device (a screen, projector, or printer)

It is said to be out of gamut.

Each valid color is either in-gamut for a particular output device (screen, or printer) or it is out of gamut.

For example, given a screen which covers 100% of the display-p3 color space, but no more, the following color is out of gamut:
 color(prophoto-rgb 0.88 0.45 0.10)

because, expressed in display-p3, one or more coordinates are either greater that 1.0 or less than 0.0:

 color(display-p3 1.0844 0.43 0.1)

This color is valid, and could, for example, be used as a gradient stop, but would need to be CSS gamut mapped for display, producing a similar-looking but lower chroma (less saturated) color.

3. Applying Color in CSS

3.1. Accessibility and Conveying Information By Color

Tests

This section provides authoring guidance, it does not need tests.


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.

1.4.1 Use of Color: Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element

3.2. Foreground Color: the color property

Name: color
Value: <color>
Initial: CanvasText
Applies to: all elements and text
Inherited: yes
Percentages: N/A
Computed value: computed color, see resolving color values
Canonical order: per grammar
Animation type: by computed value type
Tests

This property specifies the primary foreground color of the element. This is used as the fill color of its text content, and in addition specifies the used value that currentcolor resolves to, which allows indirect references to this foreground color and affects the initial values of various other color properties such as border-color and text-emphasis-color.

<color>
Sets the primary foreground color to the specified <color>.
The <color> type provides multiple ways to syntactically specify a given color. For example, the following declarations all specify the sRGB color “lime”:
em { color:  lime; }   /* color keyword  */
em { color:  rgb(0 255 0); } /* RGB range 0-255   */
em { color:  rgb(0% 100% 0%); } /* RGB range 0%-100% */
em { color:  color(sRGB 0 1 0); } /* sRGB range 0.0-1.0 */

When applied to text, this property, including its alpha component, has no effect on “color glyphs” (such as the emoji in some fonts), which are colored by a built-in palette. However, some colored fonts are able to refer to a contextual “foreground color”, such as by palette entry 0xFFFF in the COLR table of OpenType, or by the context-fill value in SVG-in-OpenType. In such cases, the foreground color is set by this property, identical to how it sets the currentcolor value.

3.3. 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: <opacity-value>
Initial: 1
Applies to: all elements
Inherited: no
Percentages: map to the range [0,1]
Computed value: specified number, clamped to the range [0,1]
Canonical order: per grammar
Animation type: by computed value type
Tests
<opacity-value>
The opacity to be applied to the element. The resulting opacity is applied to the entire element, rather than a particular color.

Opacity values outside the range [0,1] are not invalid, and are preserved in specified values, but are clamped to the range [0, 1] in computed values.

Tests

Opacity in CSS is represented using the <opacity-value> syntax, for example in the opacity property.

<opacity-value> = <number> | <percentage>

Represented as a <number>, the useful range of the value is 0 (representing full transparency) to 1 (representing full opacity). It can also be written as a <percentage>, which computes to the equivalent <number> (0% to 0, 100% to 1).

The opacity property applies the specified opacity to the element as a whole, including its contents, rather than applying it to each descendant individually. This means that, for example, an opaque child occluding part of the element’s background will continue to do so even when opacity is less than 1, but the element and child as a whole will show the underlying page through themselves.

It also means that the glyphs corresponding to all characters in the element are treated as a whole; any overlapping portions do not increase the opacity.

Tests
overlapping glyphs rendered correctly, and incorrectly
Correct and incorrect rendering of text with an opacity value of less than one, whose glyphs overlap.

If separate opacity for each glyph is desired, it can be achieved by using a color value which includes alpha, rather than setting the opacity property.

If a box has opacity less than 1, it forms a stacking context for its children. (This prevents its contents from interleaving in the z-axis with content outside it.)

Tests

Furthermore, if the z-index property applies to the box, the auto value is treated as 0 for the element; it is otherwise painted on the same layer within its parent stacking context as positioned elements with stack level 0 (as if it were a positioned element with z-index:0).

See section 9.9 and Appendix E of [CSS2] for more information on stacking contexts.

These rules about z-order do not apply to SVG elements, since SVG has its own rendering model ([SVG11], Chapter 3).

The value of the opacity property does not affect hit testing.

3.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 color space, without including the sRGB ICC profile.

Similarly, PNG specifies a compact means (the cICP chunk) to explicitly tag an image as being one of various SDR or HDR color spaces, such as Display P3 or BT.2100 HLG, without including an ICC profile.

Tagged RGB images, and tagged images using a transformation of RGB such as YCbCr, if the color profile or other identifying information is valid, must be treated as being in the specified color space.

Tests

For example, when a browser running on a system with a Display P3 monitor displays an JPEG image tagged as being in the ITU Rec BT.2020 [Rec.2020] color space, it must convert the colors from ITU Rec BT.2020 to Display P3 so that they display correctly. It must not treat the ITU Rec BT.2020 values as if they were Display P3 values, which would produce incorrect colors.

If the color profile or other identifying information is invalid, the image is treated as described for untagged images.

3.5. Color Spaces of Untagged Colors

For compatibility, colors specified in HTML, and untagged images must be treated as being in the sRGB color space ([SRGB]) unless otherwise specified.

Tests

An untagged image is an image that is not explicitly assigned a color profile, as defined by the image format.

This rule does not apply to untagged videos, since untagged video should be presumed to be in an ITU-defined color space.

4. Representing Colors: the <color> type

Tests

This section describes a type, it is primarily tested where that type is used.


Colors in CSS are represented as a list of color components, also sometimes called “channels”, representing axises in the color space. Each component has a minimum and maximum value, and can take any value between those two. Additionally, every color is accompanied by an alpha component, indicating how transparent it is, and thus how much of the backdrop one can see through the color.

CSS has several syntaxes for specifying color values:

The color functions use CSS functional notation to represent colors in a variety of color spaces by specifying their component coordinates. Some of these use a cylindrical polar color model, specifying color by 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 others use a rectangular orthogonal color model, specifying color using three orthogonal component axes.

The color functions available in Level 4 are

For easy reference in other specifications, opaque black is defined as the color rgb(0 0 0 / 100%); transparent black is the same color, but fully transparent—​i.e. rgb(0 0 0 / 0%).

Tests

4.1. The <color> syntax

Tests

This section provides definitions used later, it does not need tests.


Colors in CSS are represented by the <color> type:

<color> = <color-base> | currentColor | <system-color>

<color-base> = <hex-color> | <color-function> | <named-color> | transparent
<color-function> = <rgb()> | <rgba()> |
              <hsl()> | <hsla()> | <hwb()> |
              <lab()> | <lch()> | <oklab()> | <oklch()> |
              <color()>

An absolute color is a <color> whose computed value has an absolute, colorimetric interpretation. This means that the value is not:

The colors that resolve to sRGB are:

The functions that support legacy color syntax are:

The <hsl()>, <hsla()>, <hwb()>, <lch()>, and <oklch()> color functions are cylindrical polar color representations using a <hue> angle; the other color functions use rectangular orthogonal color representations.

4.1.1. Modern (Space-separated) Color Function Syntax

All of the absolute color functional forms first defined in this specification use the modern color syntax, meaning:

The following represents a saturated sRGB red that is 50% opaque:

rgb(100% 0% 0% / 50%)

4.1.2. Legacy (Comma-separated) Color Function Syntax

For Web compatibility, the syntactic forms of rgb(), rgba(), hsl(), and hsla(), (those defined in earlier specifications) also support a legacy color syntax which has the following differences:

The following represents a saturated sRGB red that is 50% opaque:

rgba(100%, 0%, 0%, 0.5)

For the color functions introduced in this or subsequent levels, where there is no Web compatibility issue, the legacy color syntax is invalid.

4.2. Representing Transparency in Colors: the <alpha-value> syntax

Tests

This section provides definitions used later, it does not need tests.


<alpha-value> = <number> | <percentage>

Unless otherwise specified, an <alpha-value> component of a color defaults to 100% when omitted. Values outside the range [0,1] are not invalid, but are clamped to that range at parsed-value time.

4.3. Representing Cylindrical-coordinate Hues: the <hue> syntax

Tests

This section provides definitions used later, it does not need tests.


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 and is the canonical unit.

This number is normalized to the range [0,360).

For example, in hsl(-540 0 0) or hsl(540 0 0), the <hue> component is normalized to 180 degrees.

In hsl(360 0 0) the <hue> component is normalized to 0 degrees.

In hsl(calc(-infinity) 0 0) or hsl(calc(infinity) 0 0), the <hue> component is again normalized to 0 degrees.

Note: The angles and spacing corresponding to particular hues depend on the color space. For example, in HSL and HWB, which use the sRGB color space, sRGB green is 120 degrees. In LCH, sRGB green is 134.39 degrees, display-p3 green is 136.01 degrees, a98-rgb green is 145.97 degrees and prophoto-rgb green is 141.04 degrees (because these are all different shades of green).

<hue> components are the most common components to become powerless; any color sufficiently close to the central achromatic axis will have a powerless hue component.

4.4. “Missing” Color Components and the none Keyword

In certain cases, a color can have one or more missing color components.

In this specification, this happens automatically due to hue-based interpolation for some colors (such as white); other specifications can define additional situations in which components are automatically missing.

It can also be specified explicitly, by providing the keyword none for a component in a color function. All color functions (with the exception of those using the legacy color syntax) allow any of their components to be specified as none.

This should be done with care, and only when the particular effect of doing so is desired.

Tests

For handling of missing components in situations which combine two colors, such as color interpolation, see § 12.2 Interpolating with Missing Components.

For all other purposes, a missing component behaves as a zero value, in the appropriate unit for that component: 0, 0%, or 0deg. This includes rendering the color directly, converting it to another color space, performing computations on the color component values, etc.

If a color with a missing component is serialized or otherwise presented directly to an author, then for legacy color syntax it represents that component as a zero value; otherwise, it represents that component as being the none keyword.

A missing hue is common when interpolating in cylindrical color spaces. For example, using the color-mix() function specified in [CSS-COLOR-5] one could write color-mix(in hsl, white 30%, green 70%). Since white is an achromatic color, it has a missing hue when expressed in hsl() (effectively hsl(none 0% 100%)), since any hue will produce the same color) which means that the color-mix function will treat it as having the same hue as green (effectively hsl(120deg 0% 100%)), and then interpolate based on those components.

The result will be a color that truly looks like a blend of green and white, rather than perhaps looking reddish (if whites hue was defaulted to 0deg).

Explicitly specifying missing components can be useful to achieve an effect where you only want to interpolate certain components of a color.

For example, to animate a color to "grayscale", no matter what the color is, one can interpolate it with oklch(none 0 none). This will take the hue and lightness from the starting color, but animate its chroma down to 0, rendering it into an equal-lightness gray with a steady hue across the whole animation.

Doing this manually would require matching the hue and lightness of the starting color explicitly.

4.4.1. “Powerless” Color Components

Individual color syntaxes can specify that, in some cases, a given component of their syntax becomes a powerless color component. This indicates that the value of the component doesn’t affect the rendered color; any value you give it will result in the same color displayed in the screen.

For example, in hsl(), the hue component is powerless when the saturation component is 0%; a 0% saturation indicates a grayscale color, which has no hue at all, so 0deg and 180deg, or any other angle, will give the exact same result.

If a powerless component is manually specified, it acts as normal; the fact that it’s powerless has no effect.

However, if a color is automatically produced by color space conversion, then any powerless components in the result must instead be set to missing, instead of whatever value was produced by the conversion process.

When performing color space conversion to a cylindrical polar color space, user agents shall treat a hue component as powerless if the chroma (or other measure of colorfulness, such as saturation in hsl) is less than the epsilon (ε) specified for that color space. For example, a gray color converted into oklch() may, due to numerical errors, have an extremely small chroma rather than precisely 0%; as a result, the hue component is powerless.

4.5. Parsing a <color> Value

Tests

This section provides a definition referenced elsewhere, it does not need tests.


To parse a CSS <color> value, given a string input, and an optional context element element:
  1. Parse input as a <color>. If the result is failure, return failure; otherwise, let color be the result.

  2. Let used color be the result of resolving color to a used color. If the value of other properties on the element a <color> is on is required to do the resolution (such as resolving a currentcolor or system color), use element if it was passed, or the initial values of the properties if not.

  3. Return used color.

Note: This algorithm is not intented to parse a CSS <color> value specified in a CSS stylesheet or with a CSSOM interface, but in other places like HTML attributes or Canvas interfaces.

5. sRGB Colors

CSS colors in the sRGB color space are represented by a triplet of values—​red, green, and blue—​identifying a point in the sRGB color space [SRGB]. This is an internationally-recognized, device-independent color space, and so is useful for specifying colors that will be displayed on a computer screen, but is also useful for specifying colors on other types of devices, like printers.

CSS also allows the use of non-sRGB color spaces, as described in § 10 Predefined Color Spaces.

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() and rgba() functions define an sRGB color by specifying the r, g and b (red, green, and blue) components directly. Their syntax is:

rgb() = [ <legacy-rgb-syntax> | <modern-rgb-syntax> ]
rgba() = [ <legacy-rgba-syntax> | <modern-rgba-syntax> ]
<legacy-rgb-syntax> =   rgb( <percentage>#{3} , <alpha-value>? ) |
                  rgb( <number>#{3} , <alpha-value>? )
<legacy-rgba-syntax> = rgba( <percentage>#{3} , <alpha-value>? ) |
                  rgba( <number>#{3} , <alpha-value>? )
<modern-rgb-syntax> = rgb(
  [ <number> | <percentage> | none]{3}
  [ / [<alpha-value> | none] ]?  )
<modern-rgba-syntax> = rgba(
  [ <number> | <percentage> | none]{3}
  [ / [<alpha-value> | none] ]?  )
Percentages Allowed for r, g and b
Percent reference range  For r, g and b: 0% = 0.0, 100% = 255.0 For alpha: 0% = 0.0, 100% = 1.0
Tests

The first three arguments specify the r, g and b (red, green, and blue) components of the color, respectively. 0% represents the minimum value for that color component in the sRGB gamut, and 100% represents the maximum value.

The percentage reference range of the color components comes from the historical fact that many graphics engines stored the color components internally as a single byte, which can hold integers between 0 and 255. Implementations should honor the precision of the component as authored or calculated wherever possible. If this is not possible, the component should be rounded towards +∞.

The final argument, the <alpha-value>, specifies the alpha of the color. If omitted, it defaults to 100%.

Tests

Values outside these ranges are not invalid, but are clamped to the ranges defined here at parsed-value time.

For historical reasons, rgb() and rgba() also support a legacy color syntax.

Tests

5.2. The RGB Hexadecimal Notations: #RRGGBB

The CSS hex color notation allows an sRGB color to be specified by giving the components 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 component 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 component, and the last pair specifies the blue. The alpha component of the color is fully opaque.
In other words, #00ff00 represents the same color as rgb(0 255 0) (a lime green).
8 digits
The first 6 digits are interpreted identically to the 6-digit notation. The last pair of digits, interpreted as a hexadecimal number, specifies the alpha component of the color, where 00 represents a fully transparent color and ff represent a fully opaque color.
In other words, #0000ffcc represents the same color as rgb(0 0 100% / 80%) (a slightly-transparent blue).
3 digits
This is a shorter variant of the 6-digit notation. The first digit, interpreted as a hexadecimal number, specifies the red component of the color, where 0 represents the minimum value and f represents the maximum. The next two digits represent the green and blue components, respectively, in the same way. The alpha component 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 component of the color, where 0 represents the minimum value and f represents the maximum. The next three digits represent the green, blue, and alpha components, respectively.
Tests

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—​each with their own advantages or use cases.

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.

Named Numeric Color name Hex rgb Decimal
aliceblue #f0f8ff 240 248 255
antiquewhite #faebd7 250 235 215
aqua #00ffff 0 255 255
aquamarine #7fffd4 127 255 212
azure #f0ffff 240 255 255
beige #f5f5dc 245 245 220
bisque #ffe4c4 255 228 196
black #000000 0 0 0
blanchedalmond #ffebcd 255 235 205
blue #0000ff 0 0 255
blueviolet #8a2be2 138 43 226
brown #a52a2a 165 42 42
burlywood #deb887 222 184 135
cadetblue #5f9ea0 95 158 160
chartreuse #7fff00 127 255 0
chocolate #d2691e 210 105 30
coral #ff7f50 255 127 80
cornflowerblue #6495ed 100 149 237
cornsilk #fff8dc 255 248 220
crimson #dc143c 220 20 60
cyan #00ffff 0 255 255
darkblue #00008b 0 0 139
darkcyan #008b8b 0 139 139
darkgoldenrod #b8860b 184 134 11
darkgray #a9a9a9 169 169 169
darkgreen #006400 0 100 0
darkgrey #a9a9a9 169 169 169
darkkhaki #bdb76b 189 183 107
darkmagenta #8b008b 139 0 139
darkolivegreen #556b2f 85 107 47
darkorange #ff8c00 255 140 0
darkorchid #9932cc 153 50 204
darkred #8b0000 139 0 0
darksalmon #e9967a 233 150 122
darkseagreen #8fbc8f 143 188 143
darkslateblue #483d8b 72 61 139
darkslategray #2f4f4f 47 79 79
darkslategrey #2f4f4f 47 79 79
darkturquoise #00ced1 0 206 209
darkviolet #9400d3 148 0 211
deeppink #ff1493 255 20 147
deepskyblue #00bfff 0 191 255
dimgray #696969 105 105 105
dimgrey #696969 105 105 105
dodgerblue #1e90ff 30 144 255
firebrick #b22222 178 34 34
floralwhite #fffaf0 255 250 240
forestgreen #228b22 34 139 34
fuchsia #ff00ff 255 0 255
gainsboro #dcdcdc 220 220 220
ghostwhite #f8f8ff 248 248 255
gold #ffd700 255 215 0
goldenrod #daa520 218 165 32
gray #808080 128 128 128
green #008000 0 128 0
greenyellow #adff2f 173 255 47
grey #808080 128 128 128
honeydew #f0fff0 240 255 240
hotpink #ff69b4 255 105 180
indianred #cd5c5c 205 92 92
indigo #4b0082 75 0 130
ivory #fffff0 255 255 240
khaki #f0e68c 240 230 140
lavender #e6e6fa 230 230 250
lavenderblush #fff0f5 255 240 245
lawngreen #7cfc00 124 252 0
lemonchiffon #fffacd 255 250 205
lightblue #add8e6 173 216 230
lightcoral #f08080 240 128 128
lightcyan #e0ffff 224 255 255
lightgoldenrodyellow #fafad2 250 250 210
lightgray #d3d3d3 211 211 211
lightgreen #90ee90 144 238 144
lightgrey #d3d3d3 211 211 211
lightpink #ffb6c1 255 182 193
lightsalmon #ffa07a 255 160 122
lightseagreen #20b2aa 32 178 170
lightskyblue #87cefa 135 206 250
lightslategray #778899 119 136 153
lightslategrey #778899 119 136 153
lightsteelblue #b0c4de 176 196 222
lightyellow #ffffe0 255 255 224
lime #00ff00 0 255 0
limegreen #32cd32 50 205 50
linen #faf0e6 250 240 230
magenta #ff00ff 255 0 255
maroon #800000 128 0 0
mediumaquamarine #66cdaa 102 205 170
mediumblue #0000cd 0 0 205
mediumorchid #ba55d3 186 85 211
mediumpurple #9370db 147 112 219
mediumseagreen #3cb371 60 179 113
mediumslateblue #7b68ee 123 104 238
mediumspringgreen #00fa9a 0 250 154
mediumturquoise #48d1cc 72 209 204
mediumvioletred #c71585 199 21 133
midnightblue #191970 25 25 112
mintcream #f5fffa 245 255 250
mistyrose #ffe4e1 255 228 225
moccasin #ffe4b5 255 228 181
navajowhite #ffdead 255 222 173
navy #000080 0 0 128
oldlace #fdf5e6 253 245 230
olive #808000 128 128 0
olivedrab #6b8e23 107 142 35
orange #ffa500 255 165 0
orangered #ff4500 255 69 0
orchid #da70d6 218 112 214
palegoldenrod #eee8aa 238 232 170
palegreen #98fb98 152 251 152
paleturquoise #afeeee 175 238 238
palevioletred #db7093 219 112 147
papayawhip #ffefd5 255 239 213
peachpuff #ffdab9 255 218 185
peru #cd853f 205 133 63
pink #ffc0cb 255 192 203
plum #dda0dd 221 160 221
powderblue #b0e0e6 176 224 230
purple #800080 128 0 128
rebeccapurple #663399 102 51 153
red #ff0000 255 0 0
rosybrown #bc8f8f 188 143 143
royalblue #4169e1 65 105 225
saddlebrown #8b4513 139 69 19
salmon #fa8072 250 128 114
sandybrown #f4a460 244 164 96
seagreen #2e8b57 46 139 87
seashell #fff5ee 255 245 238
sienna #a0522d 160 82 45
silver #c0c0c0 192 192 192
skyblue #87ceeb 135 206 235
slateblue #6a5acd 106 90 205
slategray #708090 112 128 144
slategrey #708090 112 128 144
snow #fffafa 255 250 250
springgreen #00ff7f 0 255 127
steelblue #4682b4 70 130 180
tan #d2b48c 210 180 140
teal #008080 0 128 128
thistle #d8bfd8 216 191 216
tomato #ff6347 255 99 71
turquoise #40e0d0 64 224 208
violet #ee82ee 238 130 238
wheat #f5deb3 245 222 179
white #ffffff 255 255 255
whitesmoke #f5f5f5 245 245 245
yellow #ffff00 255 255 0
yellowgreen #9acd32 154 205 50

Note: this list of colors and their definitions is a superset of the list of named colors defined by SVG 1.1.

For historical reasons, this is also referred to as the X11 color set.

Note: The history of the X11 color system is interesting, and was excellently summarized by Alex Sexton in their talk “Peachpuffs and Lemonchiffons”.

Tests

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.

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 Adjustment 1 § 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.

Tests

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:

AccentColor
 Background of accented user interface controls.
AccentColorText
 Text of accented user interface controls.
ActiveText
 Text in active links. For light backgrounds, traditionally red.
ButtonBorder
 The base border color for push buttons.
ButtonFace
 The face background color for push buttons.
ButtonText
 Text on push buttons.
Canvas
 Background of application content or documents.
CanvasText
 Text in application content or documents.
Field
 Background of input fields.
FieldText
 Text in input fields.
GrayText
 Disabled text. (Often, but not necessarily, gray.)
Highlight
 Background of selected text, for example from ::selection.
HighlightText
 Text of selected text.
LinkText
 Text in non-active, non-visited links. For light backgrounds, traditionally blue.
Mark
 Background of text that has been specially marked (such as by the HTML mark element).
MarkText
 Text that has been specially marked (such as by the HTML mark element).
SelectedItem
 Background of selected items, for example a selected checkbox.
SelectedItemText
 Text of selected items.
VisitedText
 Text in visited links. For light backgrounds, traditionally purple.
Tests

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:

Additionally, GrayText is expected to be readable, though possibly at a lower contrast rating, over any of the backgrounds.

To maintain consistency with widget accent color styling, AccentColor takes its value from accent-color, unless Forced Colors Mode is enabled. AccentColorText takes its value from the contrasting foreground color to AccentColor as is described for widget accent color styling.

For example, the system color combinations in the browser you are currently using:

Canvas with CanvasText: CanvasText

Canvas with LinkText: LinkText

Canvas with VisitedText: VisitedText

Canvas with ActiveText: ActiveText

Canvas with GrayText: GrayText

Canvas with ButtonBorder and adjacent Canvas: CanvasTextAdjacent

ButtonFace with ButtonText: ButtonText

ButtonFace with ButtonText and ButtonBorder: ButtonText

ButtonFace with GrayText: GrayText

Field with FieldText: FieldText

Field with GrayText: GrayText

Mark with MarkText: MarkText

Mark with GrayText: GrayText

Highlight with HighlightText: HighlightText

Highlight with GrayText: GrayText

SelectedItem with SelectedItemText: SelectedItemText

AccentColor with AccentColorText: AccentColorText

AccentColor with GrayText: GrayText

Earlier versions of CSS defined additional <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 § 21 Privacy Considerations and § 20 Security Considerations.

User Agents may, to mitigate privacy and security risks such as fingerprinting, elect to return fixed values for the used value of system colors which do not reflect customisation or theming choices made by the user.

6.3. The transparent keyword

The keyword transparent specifies a transparent black. It is a type of <named-color>.

Tests

6.4. The currentcolor keyword

The keyword currentcolor represents value of the color property on the same element. Unlike <named-color>s, it is not restricted to sRGB; the value can be any <color>. Its used values is determined by resolving color values.

Tests
Here’s a simple example showing how to use the currentcolor keyword:
.foo {
  color:  red;
  background-color:  currentcolor;
}

This is equivalent to writing:

.foo {
  color:  red;
  background-color:  red;
}
For example, the text-emphasis-color property [CSS3-TEXT-DECOR], whose initial value is currentcolor, by default matches the text color even as the color property changes across elements.
<p><em>Some <strong>really</strong> emphasized text.</em>
<style>
p { color: black; }
em { text-emphasis: dot; }
strong { color: red; }
</style>

rendered emphasized text with the word 'really' in red with red emphasis dots

In the above example, the emphasis marks are black over the text "Some" and "emphasized text", but red over the text "really".

Note: Multi-word keywords in CSS usually separate their component words with hyphens. currentcolor doesn’t, because (deep breath) it was originally introduced in SVG as a property value, "current-color" with the usual CSS spelling. It (along with all other properties and their values) then became presentation attributes and attribute values, as well as properties, to make generation with XSLT easier. Then all of the presentation attributes were changed from hyphenated to camelCase, because the DOM had an issue with hyphen meaning "minus". But then, they didn’t follow CSS conventions anymore so all the properties and property values that were already part of CSS were changed back to hyphenated! currentcolor was not a part of CSS at that time, so remained camelCased. Only later did CSS pick it up, at which point the capitalization stopped mattering, as CSS keywords are ASCII case-insensitive.

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 [HSL] color scheme, which is more intuitive to use, but still maps easily back to RGB colors.

HSL colors are specified as a triplet of hue, saturation, and lightness. The syntax of the hsl() and hsla() functions is:

hsl() = [ <legacy-hsl-syntax> | <modern-hsl-syntax> ]
hsla() = [ <legacy-hsla-syntax> | <modern-hsla-syntax> ]
<modern-hsl-syntax> = hsl(
    [<hue> | none]
    [<percentage> | <number> | none]
    [<percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
<modern-hsla-syntax> = hsla(
    [<hue> | none]
    [<percentage> | <number> | none]
    [<percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
<legacy-hsl-syntax> = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
<legacy-hsla-syntax> = hsla( <hue>, <percentage>, <percentage>, <alpha-value>? )
Percentages Allowed for S and L
Percent reference range  for S and L: 0% = 0.0, 100% = 100.0
Powerless hue ε S <= 0.001
Tests

The first argument specifies the hue angle.

In HSL (and HWB) the angle 0deg represents sRGB primary red (as does 360deg, 720deg, etc.), and the rest of the hues are spread around the circle, so 120deg represents sRGB primary green, 240deg represents sRGB primary blue, etc.

The next two arguments are the saturation and lightness, respectively. For saturation, 100% or 100 is a fully-saturated, bright color, and 0% or 0 is a fully-unsaturated gray. For lightness, 50% or 50 represents the "normal" color, while 100% or 100 is white and 0% or 0 is black.

For historical reasons, if the saturation is less than 0% it is clamped to 0% at parsed-value time, before being converted to an sRGB color.

Tests

The final argument specifies the alpha component of the color. It’s interpreted identically to the fourth argument of the rgb() function. If omitted, it defaults to 100%.

HSL colors resolve to sRGB.

If the saturation of an HSL color is 0% or 0, then the hue component is powerless.

For example, an ordinary red, the same color you would see from the keyword  red or the hex notation  #f00, is represented in HSL as  hsl(0deg 100% 50%).

An advantage of HSL over RGB is that it is more intuitive: people can guess at the colors they want, and then tweak.

For example, the following colors can all be generated off of the basic "green" hue, just by varying the other two arguments:
hsl(120deg 100% 50%) lime green
hsl(120deg 100% 25%) dark green
hsl(120deg 100% 75%) light green
hsl(120deg 75% 85%)  pastel green

A disadvantage of HSL over OkLCh is that hue manipulation changes the visual lightness, and that hues are not evenly spaced apart.

It is thus easier in HSL to create sets of matching colors (by keeping the hue the same and varying the saturation and lightness), compared to manipulating the sRGB component values; however, because the lightness is simply the mean of the gamma-corrected red, green and blue components it does not correspond to the visual perception of lightness across hues.

For example,  blue is represented in HSL as  hsl(240deg 100% 50%) while  yellow is  hsl(60deg 100% 50%). Both have an HSL Lightness of 50%, but clearly the yellow looks much lighter than the blue.

In OkLCh, sRGB blue is  oklch(0.452 0.313 264.1) while sRGB yellow is  oklch(0.968 0.211 109.8). The OkLCh Lightnesses of 0.452 and 0.968 clearly reflect the visual lightnesses of the two colors.

The hue angle in HSL is not perceptually uniform; colors appear bunched up in some areas and widely spaced in others.

For example, the pair of hues  hsl(220deg 100% 50%) and  hsl(250deg 100% 50%) have an HSL hue difference of 250-220 = 30deg and look fairly similar, while another pair of colors  hsl(50deg 100% 50%) and  hsl(80deg 100% 50%), which also have a hue difference of 80-50 = 30deg, look very different.

In OkLCh, the same pair of colors  oklch(0.533 0.26 262.6) and  oklch(0.462 0.306 268.9) have a hue difference of 268.9 - 262.6 = 6.3deg while the second pair  oklch(0.882 0.181 94.24) and  oklch(0.91 0.245 129.9) have a hue difference of 129.9 - 94.24 = 35.66deg, correctly reflecting the visual separation of hues.

For historical reasons, hsl() and hsla() also support a legacy color syntax.

Tests

7.1. Converting HSL Colors to sRGB

Converting an HSL color to sRGB is straightforward mathematically. Here’s a sample implementation of the conversion algorithm in JavaScript. It returns an array of three numbers representing the red, green, and blue components of the colors, which for colors in the sRGB gamut will be in the range [0, 1].

This code assumes that parse-time clamping of negative saturation has already been applied.

/**
 * @param {number} hue - Hue as degrees 0..360
 * @param {number} sat - Saturation in reference range [0,100]
 * @param {number} light - Lightness in reference range [0,100]
 * @return {number[]} Array of sRGB components; in-gamut colors in range [0..1]
 */
function hslToRgb(hue, sat, light) {

    sat /= 100;
    light /= 100;

    function f(n) {
        let k = (n + hue/30) % 12;
        let a = sat * Math.min(light, 1 - light);
        return light - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
    }

    return [f(0), f(8), f(4)];
}

7.2. Converting sRGB Colors to HSL

Conversion in the reverse direction proceeds similarly.

Special care is taken to deal with intermediate negative values of saturation, which can be produced by colors far outside the sRGB gamut.

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number[]} Array of HSL values: Hue as degrees 0..360, Saturation and Lightness in reference range [0,100]
 */
function rgbToHsl (red, green, blue) {
    let max = Math.max(red, green, blue);
    let min = Math.min(red, green, blue);
    let [hue, sat, light] = [NaN, 0, (min + max)/2];
    let d = max - min;
    let epsilon = 1 / 100000;   // max Sat is 1, in this code

    if (d !== 0) {
        sat = (light === 0 || light === 1)
            ? 0
            : (max - light) / Math.min(light, 1 - light);

        switch (max) {
            case red:   hue = (green - blue) / d + (green < blue ? 6 : 0); break;
            case green: hue = (blue - red) / d + 2; break;
            case blue:  hue = (red - green) / d + 4;
        }

        hue = hue * 60;
    }

    // Very out of gamut colors can produce negative saturation
    // If so, just rotate the hue by 180 and use a positive saturation
    // see https://github.com/w3c/csswg-drafts/issues/9222
    if (sat < 0) {
        hue += 180;
        sat = Math.abs(sat);
    }

    if (hue >= 360) {
        hue -= 360;
    }

    if (sat <= epsilon) {
        hue = NaN;
    }

    return [hue, sat * 100, light * 100];
}

7.3. Examples of HSL Colors

This section is not normative.

Tests

This section is not normative, it does not need tests.


The tables below illustrate a wide range of possible HSL colors. Each table represents one hue, selected at 30° intervals, to illustrate the common "core" hues: red, yellow, green, cyan, blue, magenta, and the six intermediary colors between these.

In each table, the X axis represents the saturation while the Y axis represents the lightness.

0° Reds
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
30° Reds-Yellows (=Oranges)
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
60° Yellows
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
90° Yellow-Greens
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
120° Greens
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
150° Green-Cyans
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
180° Cyans
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
210° Cyan-Blues
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
240° blues
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
270° Blue-Magentas
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
300° Magentas
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
330° Magenta-Reds
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%

8. HWB Colors: hwb() function

HWB (short for Hue-Whiteness-Blackness) [HWB] is another method of specifying sRGB colors, similar to HSL', but often even easier for humans to work with. It describes colors with a starting hue, then a degree of whiteness and blackness to mix into that base hue.

Many color-pickers are based on the HWB color system, due to its intuitiveness.

HWB colors resolve to sRGB.

This is a screenshot of Chrome’s color picker, shown when a user activates an <input type="color">. The outer wheel is used to select the hue, then the relative amounts of white and black are selected by clicking on the inner triangle.

The syntax of the hwb() function is:

hwb() = hwb(
  [<hue> | none]
  [<percentage> | <number> | none]
  [<percentage> | <number> | none]
  [ / [<alpha-value> | none] ]? )
Percentages Allowed for W and B
Percent reference range  for W and B: 0% = 0.0, 100% = 100.0
Powerless hue ε W + B >= 99.999

The first argument specifies the hue, and is defined identically to hsl(); this means it suffers the same disadvantages such as hue uniformity.

The second argument specifies the amount of white to mix in, as a percentage from 0% (no whiteness) to 100% (full whiteness). Similarly, the third argument specifies the amount of black to mix in, also from 0% (no blackness) to 100% (full blackness).

For example,  hwb(150 20% 10%) is the same color as  hsl(150 77.78% 55%) and  rgb(20% 90% 55%).

Values outside of these ranges are not invalid; hue angles outside the range [0,360) will be normalized to that range and values of white and black which sum to 100% or greater will produce achromatic colors as described below.

The resulting color can be thought of conceptually as a mixture of paint in the chosen hue, white paint, and black paint, with the relative amounts of each determined by the percentages.

If the sum white+black is greater than or equal to 100%, it defines an achromatic color, i.e. a shade of gray; when converted to sRGB the R, G and B values are identical and have the value white / (white + black).

For example, in the color  hwb(45 40% 80%) white and black adds to 120, so this is an achromatic color whose R, G and B components are 40 / 40 + 80 = 0.33  rgb(33.33% 33.33% 33.33%).

Achromatic HWB colors no longer contain any hint of the chosen hue. In this case, the hue component is powerless.

The fourth argument specifies the alpha component of the color. It’s interpreted identically to the fourth argument of the rgb() function. If omitted, it defaults to 100%.

There is no Web compatibility issue with hwb, which is new in this level of the specification, and so hwb() does not support a legacy color syntax that separates all of its arguments with commas. Using commas inside hwb() is an error.

Tests

8.1. Converting HWB Colors to sRGB

Converting an HWB color to sRGB is straightforward, and related to how one converts HSL to RGB. The following Javascript implementation of the algorithm first normalizes the white and black components, so their sum is no larger than 100%.

/**
 * @param {number} hue -  Hue as degrees 0..360
 * @param {number} white -  Whiteness in reference range [0,100]
 * @param {number} black -  Blackness in reference range [0,100]
 * @return {number[]} Array of RGB components 0..1
 */
function hwbToRgb(hue, white, black) {
    white /= 100;
    black /= 100;
    if (white + black >= 1) {
        let gray = white / (white + black);
        return [gray, gray, gray];
    }
    let rgb = hslToRgb(hue, 100, 50);
    for (let i = 0; i < 3; i++) {
        rgb[i] *= (1 - white - black);
        rgb[i] += white;
    }
    return rgb;
}

8.2. Converting sRGB Colors to HWB

Conversion in the reverse direction proceeds similarly.

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number} Hue as degrees 0..360
 */
function rgbToHue(red, green, blue) {
    // Similar to rgbToHsl, except that saturation and lightness are not calculated, and
    // potential negative saturation is ignored.
    let max = Math.max(red, green, blue);
    let min = Math.min(red, green, blue);
    let hue = NaN;
    let d = max - min;

    if (d !== 0) {
        switch (max) {
            case red:   hue = (green - blue) / d + (green < blue ? 6 : 0); break;
            case green: hue = (blue - red) / d + 2; break;
            case blue:  hue = (red - green) / d + 4;
        }

        hue *= 60;
    }

    if (hue >= 360) {
        hue -= 360;
    }

    return hue;
}

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number[]} Array of HWB values: Hue as degrees 0..360, Whiteness and Blackness in reference range [0,100]
 */
function rgbToHwb(red, green, blue) {
    let epsilon = 1 / 100000;  // account for multiply by 100
    var hue = rgbToHue(red, green, blue);
    var white = Math.min(red, green, blue);
    var black = 1 - Math.max(red, green, blue);
    if (white + black >= 1 - epsilon) {
        hue = NaN;
    }
    return([hue, white*100, black*100]);
}

8.3. Examples of HWB Colors

This section is not normative.

Tests

This section is not normative, it does not need tests.


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: CIE Lab and LCH, Oklab and OkLCh

9.1. CIE Lab and LCH

This section is not normative.

Tests

This section is not normative, it does not need tests.


Physical measurements of a color are typically expressed in the CIE L*a*b* [CIELAB] color space, created in 1976 by the CIE and commonly referred to simply as Lab. Color conversions from one device to another may also use Lab as an intermediate step. Derived from human vision experiments, Lab represents the entire range of color that humans can see.

Lab is a rectangular coordinate system with a central Lightness (L) axis. This value is usually written as a unitless number; for compatibility with the rest of CSS, it may also be written as a percentage. 100% means an L value of 100, not 1.0. L=0% or 0 is deep black (no light at all) while L=100% or 100 is a diffuse white.

Usefully, L=50% or 50 is mid gray, by design, and equal increments in L are evenly spaced visually: the Lab color space is intended to be perceptually uniform.

This figure shows, to the left, the Lightness axis of the CIE Lab color space. Twenty-one neutral swatches are shown (L=0%, L=5%, to L=100%). The steps are equally spaced, visually. To the right, the same number of steps in luminance are equally spaced in light energy but not equally spaced visually.

The a and b axes convey hue; positive values along the a axis are a purplish red while negative values are the complementary color, a green. Similarly, positive values along the b axis are yellow and negative are the complementary blue/violet. Desaturated colors have small values of a and b and are close to the L axis; saturated colors lie far from the L axis.

The illuminant is D50 white, a standardized daylight spectrum with a color temperature of 5000K, as reflected by a perfect diffuse reflector; it approximates the color of sunlight on a sunny day. D50 is also the whitepoint used for the profile connection space in ICC color interconversion, the whitepoint used in image editors which offer Lab editing, and the value used by physical measurement devices such as spectrophotometers and spectroradiometers, when they report measured colors in Lab.

Conversion from colors specified using other white points is called a chromatic adaptation transform, which models the changes in the human visual system as we adapt to a new lighting condition. The linear Bradford algorithm [ICC] (a simplification of the original Bradford algorithm [Bradford-CAT]) is the industry standard chromatic adaptation transform, and is easy to calculate as it is a simple matrix multiplication.

CIE LCH has the same L axis as Lab, but uses polar coordinates C (chroma) and H (hue), making it a polar, cylindrical coordinate system. C is the geometric distance from the L axis and H is the angle from the positive a axis, towards the positive b axis.

This figure shows the L=50 plane of the CIE Lab color space. 20 degree increments in CIE LCH are displayed as circles at three levels of Chroma: 20, 40 and 60. All the 20 Chroma colors fit inside sRGB gamut, some of 40 and 60 Chroma are outside. These out of gamut colors are visualized as grey, with a red warning outer stroke.

Note: The L axis in Lab and LCH is not to be confused with the L axis in HSL. For example, in HSL, the sRGB colors blue (#00F) and yellow (#FF0) have the same value of L (50%) even though visually, blue is much darker. This is much clearer in Lab: sRGB blue is lab(29.567% 68.298 -112.0294) while sRGB yellow is lab(97.607% -15.753 93.388). In Lab and LCH, if two colors have the same measured L value, they have identical visual lightness. HSL and related polar RGB models were developed in an attempt to give similar usability benefits for RGB that LCH gave to Lab, but are significantly less accurate.

Although the use of CIE Lab and LCH is widespread, it is known to have some problems. In particular:

Hue linearity
In the blue region (LCH Hue between 270° and 330°), visual hue departs from what LCH predicts. Plotting a set of blues of the same hue and differing Chroma, which should lie on a straight line from the neutral axis, instead form a curve. Put another way, as a saturated blue has it’s Chroma progressively reduced, it becomes noticeably purple.
Hue uniformity
While hues in LCH are in general evenly spaced, (and far better than HSL or HWB), uniformity is not perfect.
Over-prediction of high Chroma differences
For high Chroma colors, changes in Chroma are less noticeable than for more neutral colors.

These deficiencies affect, for example, creation of evenly spaced gradients, gamut mapping from one color space to a smaller one, and computation of the visual difference between two colors.

To compensate for this, formulae to predict the visual difference between two colors (delta E) have been made more accurate over time (but also, much more complex to compute). The current industry standard formula, delta E 2000, works well to mitigate some of the Lab and LCH problems. A sample implementation is given in § 19.1 ΔE2000.

This does not help with hue curvature, however.

9.2. Oklab and OkLCh

This section is not normative.

Tests

This section is not normative, it does not need tests.


Recently, Oklab, an improved Lab-like space has been developed [Oklab]. The corresponding polar form is called OkLCh. It was produced by numerical optimization of a large dataset of visually similar colors, and has improved hue linearity, hue uniformity, and chroma uniformity compared to CIE LCH.

Like CIE Lab, there is a central lightness L axis which is usually written as a unitless number in the range [0,1]; for compatibility with the rest of CSS, it may be written as a percentage. 100% means an L value of 1.0. L=0% or 0.0 is deep black (no light at all) while L=100% or 1.0 is a diffuse white.

Note: Unlike CIE Lab, which assumes adaptation to the diffuse white, Oklab assumes adaptation to the color being defined, which is intended to make it scale invariant.

As with CIE Lab, the a and b axes convey hue; positive values along the a axis are a purplish red while negative values are the complementary color, a green. Similarly, positive values along the b axis are yellow and negative are the complementary blue/violet.

The illuminant is D65, the same white point as most RGB color spaces.

OkLCh has the same L axis as Oklab, but uses polar coordinates C (chroma) and H (hue).

Note: Unlike CIE LCH, where Chroma can reach values of 200 or more, OkLCh Chroma ranges to 0.5 or so. The hue angles between CIE LCH and OkLCh are broadly similar, but not identical.

diagram showing purpling in CIE LCH
A constant CIE LCH hue slice, showing the sRGB gamut around primary blue. A noticeable purpling is immediately evident.
diagram showing hue constancy in OkLCh
A constant OkLCh hue slice, showing the sRGB gamut around primary blue. The visual hue remains constant.

Because Oklab is more perceptually uniform than CIE Lab, the color difference is a straightforward distance in 3D space (root sum of squares). Although trivial, a sample implementation is give in § 19.2 ΔEOK.

9.3. 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> | none]
      [ <percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ / [<alpha-value> | none] ]? )
Percentages Allowed for L, a and b
Percent reference range  for L: 0% = 0.0, 100% = 100.0
for a and b: -100% = -125, 100% = 125
Tests

In Lab, the first argument specifies the CIE Lightness, L. This is a number between 0% or 0 and 100% or 100 Values less than 0% or 0 must be clamped to 0% at parsed-value time; values greater than 100% or 100 are clamped to 100% at parsed-value time.

The second and third arguments are the distances along the "a" and "b" axes in the Lab color space, as described in the previous section. These values are signed (allow both positive and negative values) and theoretically unbounded (but in practice do not exceed ±160 for real-world colors).

There is an optional fourth <alpha-value> component, separated by a slash, representing the alpha component.

If the lightness of a Lab color (after clamping) is 0%, or 100% the color will be displayed as black, or white, respectively due to gamut mapping to the display.

 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);
 lab(29.69% 44.888% -29.04%)
lch() = lch( [<percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ <hue> | none]
      [ / [<alpha-value> | none] ]? )
Percentages Allowed for L and C
Percent reference range  for L: 0% = 0.0, 100% = 100.0
for C: 0% = 0, 100% = 150
Powerless hue ε C <= 0.0015
Tests

In CIE LCH the first argument specifies the CIE Lightness L, interpreted identically to the Lightness argument of lab().

The second argument is the chroma C, (roughly representing the "amount of color"). Its minimum useful value is 0, while its maximum is theoretically unbounded (but in practice does not exceed 230). If the provided value is negative, it is clamped to 0 at parsed-value time.

The third argument is the hue angle H. It’s interpreted similarly to the <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> component, separated by a slash, representing the alpha component.

If the chroma of an LCH color is 0%, the hue component is powerless. If the lightness of an LCH color (after clamping) is 0%, or 100%, the color will be displayed as black, or white, respectively due to gamut mapping to the display.

 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);
 lch(29.69% 45.553% 327.1)

There is no Web compatibility issue with lab or lch', which are new in this level of the specification, and so lab() and lch() do not support a legacy color syntax that separates all of their arguments with commas. Using commas inside these functions is an error.

9.4. Specifying Oklab and OkLCh: the oklab() and oklch() functional notations

CSS allows colors to be directly expressed in Oklab and OkLCh.

oklab() = oklab( [ <percentage> | <number> | none]
    [ <percentage> | <number> | none]
    [ <percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
Percentages Allowed for L, a and b
Percent reference range  for L: 0% = 0.0, 100% = 1.0
for a and b: -100% = -0.4, 100% = 0.4
Tests

In Oklab the first argument specifies the Oklab Lightness. This is a number between 0% or 0 and 100% or 1.0.

Values less than 0% or 0.0 must be clamped to 0% at parsed-value time; values greater than 100% or 1.0 are clamped to 100% at parsed-value time.

The second and third arguments are the distances along the "a" and "b" axes in the Oklab color space, as described in the previous section. These values are signed (allow both positive and negative values) and theoretically unbounded (but in practice do not exceed ±0.5).

There is an optional fourth <alpha-value> component, separated by a slash, representing the alpha component.

If the lightness of an Oklab color is 0% or 0, or 100% or 1.0, the color will be displayed as black, or white, respectively due to gamut mapping to the display.

 oklab(40.101% 0.1147 0.0453);
 oklab(59.686% 0.1009 0.1192);
 oklab(0.65125 -0.0320 0.1274);
 oklab(66.016% -0.1084 0.1114);
 oklab(72.322% -0.0465 -0.1150);
 oklab(42.1% 41% -25%)
oklch() = oklch( [ <percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ <hue> | none]
      [ / [<alpha-value> | none] ]? )
Percentages Allowed for L and C
Percent reference range  for L: 0% = 0.0, 100% = 1.0
for C: 0% = 0.0 100% = 0.4
Powerless hue ε C <= 0.000004
Tests

In OkLCh the first argument specifies the OkLCh Lightness L, interpreted identically to the Lightness argument of oklab().

The second argument is the chroma C. Its minimum useful value is 0, while its maximum is theoretically unbounded (but in practice does not exceed 0.5). If the provided value is negative, it is clamped to 0 at parsed-value time.

The third argument is the hue angle H. It’s interpreted similarly to the <hue> arguments of hsl() and lch(), but doesn’t map hues to angles in the same way. 0deg points along the positive "a" axis (toward purplish red), (as does 360deg, 720deg, etc.); 90deg points along the positive "b" axis (toward mustard yellow), 180deg points along the negative "a" axis (toward greenish cyan), and 270deg points along the negative "b" axis (toward sky blue).

There is an optional fourth <alpha-value> component, separated by a slash, representing the alpha component.

If the chroma of an OkLCh color is 0% or 0, the hue component is powerless. If the lightness of an OkLCh color is 0% or 0, or 100% or 1.0, the color will be displayed as black, or white, respectively due to gamut mapping to the display.

 oklch(40.101% 0.12332 21.555);
 oklch(59.686% 0.15619 49.7694);
 oklch(0.65125 0.13138 104.097);
 oklch(0.66016 0.15546 134.231);
 oklch(72.322% 0.12403 247.996);
 oklch(42.1% 48.25% 328.4)

There is no Web compatibility issue with oklab or oklch', which are new in this level of the specification, and so oklab() and oklch() do not support a legacy color syntax that separates all of their arguments with commas. Using commas inside these functions is an error.

9.5. Converting Lab or Oklab colors to LCH or OkLCh colors

Conversion to the polar form is trivial:

  1. C = sqrt(a^2 + b^2)
  2. if (C > epsilon) H = atan2(b, a) else H is missing
  3. L is the same

For extremely small values of a and b (near-zero Chroma), although the visual color does not change from being on the neutral axis, small changes to the values can result in the reported hue angle swinging about wildly and being essentially random. In CSS, this means the hue is powerless, and treated as missing when converted into LCH or OkLCh; in non-CSS contexts this might be reflected as a missing value, such as NaN.

9.6. Converting LCH or OkLCh colors to Lab or Oklab colors

Conversion to the rectangular form is trivial:

  1. If H is missing, a = b = 0
  2. Otherwise,
    1. a = C cos(H)
    2. b = C sin(H)
  3. L is the same

10. Predefined Color Spaces

CSS provides several predefined color spaces including display-p3 [Display-P3], which is a wide gamut space typical of current wide-gamut monitors, prophoto-rgb, widely used by photographers and rec2020 [Rec.2020], which is a broadcast industry standard, ultra-wide gamut space capable of representing almost all visible real-world colors.

10.1. Specifying Predefined Colors: the