CSS Color Module Level 4

Status: ED
Work Status: Exploring
ED: http://dev.w3.org/csswg/css-color/
Shortname: css-color
Group: csswg
Level: 4
Editor: Tab Atkins Jr., Google, http://xanthir.com/contact
Editor: Chris Lilley, W3C, chris@w3.org
Former Editor: L. David Baron, Mozilla Corporation, dbaron@dbaron.org
Abstract: This specification describes CSS <> values and properties for foreground color and group opacity.
Ignored Terms: stacking context, double, octet, DOMString

Introduction

This section is not normative. This module describes CSS properties which allow authors to specify the foreground color and opacity of the text content of an element. This module also describes in detail the CSS <> value type. It not only defines the color-related properties and values that already exist in CSS1 and CSS2, but also defines new properties and values.

Foreground Color: the 'color' property

	Name: color
	Value: <>
	Initial: UA-defined, see prose
	Applies to: all elements
	Inherited: yes
	Percentages: N/A
	Media: visual
	Computed value: an RGBA color
	
This property describes the foreground fill color of an element's text content. In addition, it provides the value that ''currentcolor'' resolves to. If the ''currentcolor'' keyword is set on the 'color' property itself, it is treated as ''color: inherit''. The initial value of this property is ''black''. 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.0,100.0,0.0); }      /* RGB range 0%-100% */
		
<>
The <> type is defined in a later section.

Representing sRGB Colors: the <> type

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. (See [[COLORIMETRY]].) 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. The components are also sometimes called "channels". Each channel has a minimum and maximum value, and can take any value between those two. While all colors share an underlying storage format, CSS contains several syntaxes for specifying <> values. Some directly specify the sRGB color, such as the ''rgb()'' and ''rgba()'' functions and the hex notation. Others are more human-friendly to write and understand, and are converted to an sRGB color by CSS, such as the ''hsl()'' and ''hsla()'' functions, or the long list of named colors defined by CSS. In total, the definition of <> is:
	<color> = <> | <> | <> | <> |
	          <> | <> | <> | <> |
	          <> | <> | currentcolor |
	          <>
	
Some operations work differently on achromatic colors. An achromatic color is a shade of gray: in the RGB colorspace, a color is achromatic if the red, green, and blue channels are all the same value; in the HSL colorspace, a color is achromatic if the saturation is ''0%''; in the HWB colorspace, a color is achromatic if the sum of the whiteness and blackness is at least ''100%''.

Notes On Using Colors

Although colors can add significant amounts of information to documents and make them more readable, color by itself should not be the sole means to convey important information. Please consider the W3C Web Content Accessibility Guidelines [[WCAG20]] when including color in your documents.

RGB Colors

There are several methods of directly specifying a color in terms of its RGBA channels.

The RGB functions: ''rgb()'' and ''rgba()''

The ''rgb()'' function defines an RGB color by specifying the red, green, and blue channels directly. Its syntax is:
	rgb() = rgb( <>#{3} )
	rgba() = rgba( <>#{3} , <> )
	<rgb-component> = <> | <>
	<alpha-value> = <> | <>
	
The three <>s 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 <> is equivalent to a <>, 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. However, the CSS syntax allows full <>s, not just <>s, for authoring convenience. Some devices can output colors technically outside of the sRGB gamut, represented by channels with values less than ''0%'' or greater than ''100%''. For this reason, values outside of the 0%-100% range are allowed, but are clamped to the device's gamut.) The final argument, the <>, specifies the alpha of the color. If given as a <>, the useful range of the value is ''0'' (representing a fully transparent color) to ''1'' (representing a fully opaque color). If given as a <>, ''0%'' represents a fully transparent color, while ''100%'' represents a fully opaque color. Values outside these ranges are not invalid, but are clamped to the ranges defined here at computed-value time. If omitted, it defaults to ''100%''.

The RGB hexadecimal notations: ''#RRGGBB''

The CSS hex color notation allows a color to be specified by giving the channels as hexadecimal numbers, which is similar to how colors are often written directly in computer code. It's also shorter than writing the same color out in ''rgb()'' notation. The syntax of a <hex-color> is a <> token whose value consists of 3, 4, 6, or 8 hexadecimal digits. In other words, a hex color is written as a hash character, "#", followed by some number of digits 0-9 or letters a-f (the case of the letters doesn't matter - ''#00ff00'' is identical to ''#00FF00''). The number of hex digits given determines how to decode the hex notation into an RGB color:
6 digits
The first pair of digits, interpreted as a hexadecimal number, specifies the red channel of the color, where ''00'' represents the minimum value and ''ff'' (255 in decimal) represents the maximum. The next pair of digits, interpreted in the same way, specifies the green channel, and the last pair specifies the blue. The alpha channel of the color is fully opaque.
In other words, ''#00ff00'' represents the same color as ''rgb(0, 255, 0)'' (a lime green).
8 digits
The first 6 digits are interpreted identically to the 6-digit notation. The last pair of digits, interpreted as a hexadecimal number, specifies the alpha channel of the color, where ''00'' represents a fully transparent color and ''ff'' represent a fully opaque color.
In other words, ''#0000ffcc'' represents the same color as ''rgba(0, 0, 100%, 80%)'' (a slightly-transparent blue).
3 digits
This is a shorter variant of the 6-digit notation. The first digit, interpreted as a hexadecimal number, specifies the red channel of the color, where ''0'' represents the minimum value and ''f'' represents the maximum. The next two digits represent the green and blue channels, respectively, in the same way. The alpha channel of the color is fully opaque.
This syntax is often explained by saying that it's identical to a 6-digit notation obtained by "duplicating" all of the digits. For example, the notation ''#123'' specifies the same color as the notation ''#112233''. This method of specifying a color has lower "resolution" than the 6-digit notation; there are only 4096 possible colors expressible in the 3-digit hex syntax, as opposed to approximately 17 million in 6-digit hex syntax.
4 digits
This is a shorter variant of the 8-digit notation, "expanded" in the same way as the 3-digit notation is. The first digit, interpreted as a hexadecimal number, specifies the red channel of the color, where ''0'' represents the minimum value and ''f'' represents the maximum. The next three digits represent the green, blue, and alpha channels, respectively.

Named Colors

In addition to the various numeric syntaxes for <>s, CSS defines a large set of named colors that can be used instead, so that common colors can be written and read more easily. A <named-color> is written as an <>, accepted anywhere a <> is. As usual for CSS-defined <>s, all of these keywords are case-insensitive. 16 of CSS's named colors come from HTML originally: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow. Most of the rest come from one version of the X11 color system, used in Unix-derived systems to specify colors for the console. (Two special color values, ''transparent'' and ''currentcolor'', are specially defined in their own sections.) Note: The history of the X11 color system is interesting, and was excellently summarized by Alex Sexton in his talk “Peachpuffs and Lemonchiffons”. The following table defines all of the opaque named colors, by giving equivalent numeric specifications in the other color syntaxes.
Named Numeric Color name Hex rgb Decimal
  aliceblue#f0f8ff240,248,255
  antiquewhite#faebd7250,235,215
  aqua#00ffff0,255,255
  aquamarine#7fffd4127,255,212
  azure#f0ffff240,255,255
  beige#f5f5dc245,245,220
  bisque#ffe4c4255,228,196
  black#0000000,0,0
  blanchedalmond#ffebcd255,235,205
  blue#0000ff0,0,255
  blueviolet#8a2be2138,43,226
  brown#a52a2a165,42,42
  burlywood#deb887222,184,135
  cadetblue#5f9ea095,158,160
  chartreuse#7fff00127,255,0
  chocolate#d2691e210,105,30
  coral#ff7f50255,127,80
  cornflowerblue#6495ed100,149,237
  cornsilk#fff8dc255,248,220
  crimson#dc143c220,20,60
  cyan#00ffff0,255,255
  darkblue#00008b0,0,139
  darkcyan#008b8b0,139,139
  darkgoldenrod#b8860b184,134,11
  darkgray#a9a9a9169,169,169
  darkgreen#0064000,100,0
  darkgrey#a9a9a9169,169,169
  darkkhaki#bdb76b189,183,107
  darkmagenta#8b008b139,0,139
  darkolivegreen#556b2f85,107,47
  darkorange#ff8c00255,140,0
  darkorchid#9932cc153,50,204
  darkred#8b0000139,0,0
  darksalmon#e9967a233,150,122
  darkseagreen#8fbc8f143,188,143
  darkslateblue#483d8b72,61,139
  darkslategray#2f4f4f47,79,79
  darkslategrey#2f4f4f47,79,79
  darkturquoise#00ced10,206,209
  darkviolet#9400d3148,0,211
  deeppink#ff1493255,20,147
  deepskyblue#00bfff0,191,255
  dimgray#696969105,105,105
  dimgrey#696969105,105,105
  dodgerblue#1e90ff30,144,255
  firebrick#b22222178,34,34
  floralwhite#fffaf0255,250,240
  forestgreen#228b2234,139,34
  fuchsia#ff00ff255,0,255
  gainsboro#dcdcdc220,220,220
  ghostwhite#f8f8ff248,248,255
  gold#ffd700255,215,0
  goldenrod#daa520218,165,32
  gray#808080128,128,128
  green#0080000,128,0
  greenyellow#adff2f173,255,47
  grey#808080128,128,128
  honeydew#f0fff0240,255,240
  hotpink#ff69b4255,105,180
  indianred#cd5c5c205,92,92
  indigo#4b008275,0,130
  ivory#fffff0255,255,240
  khaki#f0e68c240,230,140
  lavender#e6e6fa230,230,250
  lavenderblush#fff0f5255,240,245
  lawngreen#7cfc00124,252,0
  lemonchiffon#fffacd255,250,205
  lightblue#add8e6173,216,230
  lightcoral#f08080240,128,128
  lightcyan#e0ffff224,255,255
  lightgoldenrodyellow#fafad2250,250,210
  lightgray#d3d3d3211,211,211
  lightgreen#90ee90144,238,144
  lightgrey#d3d3d3211,211,211
  lightpink#ffb6c1255,182,193
  lightsalmon#ffa07a255,160,122
  lightseagreen#20b2aa32,178,170
  lightskyblue#87cefa135,206,250
  lightslategray#778899119,136,153
  lightslategrey#778899119,136,153
  lightsteelblue#b0c4de176,196,222
  lightyellow#ffffe0255,255,224
  lime#00ff000,255,0
  limegreen#32cd3250,205,50
  linen#faf0e6250,240,230
  magenta#ff00ff255,0,255
  maroon#800000128,0,0
  mediumaquamarine#66cdaa102,205,170
  mediumblue#0000cd0,0,205
  mediumorchid#ba55d3186,85,211
  mediumpurple#9370db147,112,219
  mediumseagreen#3cb37160,179,113
  mediumslateblue#7b68ee123,104,238
  mediumspringgreen#00fa9a0,250,154
  mediumturquoise#48d1cc72,209,204
  mediumvioletred#c71585199,21,133
  midnightblue#19197025,25,112
  mintcream#f5fffa245,255,250
  mistyrose#ffe4e1255,228,225
  moccasin#ffe4b5255,228,181
  navajowhite#ffdead255,222,173
  navy#0000800,0,128
  oldlace#fdf5e6253,245,230
  olive#808000128,128,0
  olivedrab#6b8e23107,142,35
  orange#ffa500255,165,0
  orangered#ff4500255,69,0
  orchid#da70d6218,112,214
  palegoldenrod#eee8aa238,232,170
  palegreen#98fb98152,251,152
  paleturquoise#afeeee175,238,238
  palevioletred#db7093219,112,147
  papayawhip#ffefd5255,239,213
  peachpuff#ffdab9255,218,185
  peru#cd853f205,133,63
  pink#ffc0cb255,192,203
  plum#dda0dd221,160,221
  powderblue#b0e0e6176,224,230
  purple#800080128,0,128
  rebeccapurple#663399102,51,153
  red#ff0000255,0,0
  rosybrown#bc8f8f188,143,143
  royalblue#4169e165,105,225
  saddlebrown#8b4513139,69,19
  salmon#fa8072250,128,114
  sandybrown#f4a460244,164,96
  seagreen#2e8b5746,139,87
  seashell#fff5ee255,245,238
  sienna#a0522d160,82,45
  silver#c0c0c0192,192,192
  skyblue#87ceeb135,206,235
  slateblue#6a5acd106,90,205
  slategray#708090112,128,144
  slategrey#708090112,128,144
  snow#fffafa255,250,250
  springgreen#00ff7f0,255,127
  steelblue#4682b470,130,180
  tan#d2b48c210,180,140
  teal#0080800,128,128
  thistle#d8bfd8216,191,216
  tomato#ff6347255,99,71
  turquoise#40e0d064,224,208
  violet#ee82ee238,130,238
  wheat#f5deb3245,222,179
  white#ffffff255,255,255
  whitesmoke#f5f5f5245,245,245
  yellow#ffff00255,255,0
  yellowgreen#9acd32154,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.

The ''transparent'' keyword

The keyword transparent specifies a transparent black color; that is, a color with its red, green, and blue channels all set to the minimum value and its alpha channel set to full transparency, equivalent to ''rgba(0, 0, 0, 0)''. It is a type of <>.

The ''currentcolor'' keyword

The keyword currentcolor takes its value from the value of the 'color' property on the same element. This happens at used-value time, which means that if the value is inherited, it's inherited as ''currentcolor'', not as the value of the 'color' property, so descendants will use their own 'color' property to resolve it. If ''currentcolor'' is used as the value of the 'color' property, it instead takes its value from the inherited value of the 'color' property.
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>
		
In the above example, the emphasis marks would be black over the text "Some" and "emphasized text", but red over the text "really".
Note: Multi-word keywords in CSS usually separate their component words with hyphens. ''currentcolor'' doesn't, because it was originally introduced in SVG as a special attribute value spelled "currentColor", rather than a CSS value. Only later did CSS pick it up, at which point the capitalization stopped mattering, as CSS keywords are case-insensitive.

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( <>, <>, <> )
	hsla() = hsla( <>, <>, <>, <> )
	<hue> = <> | <>
	
The first argument specifies the hue. Hue is represented as an angle of the color circle (the rainbow, twisted around into a circle). The angle ''0deg'' represents red (as does ''360deg'', ''720deg'', etc.), and the rest of the hues are spread around the circle, so ''120deg'' represents green, ''240deg'' represents blue, etc. Because this value is so often given in degrees, the argument can also be given as a number, which is interpreted as a number of degrees. The next two arguments are the saturation and lightness, respectively. For saturation, ''100%'' is a fully-saturated, bright color, and ''0%'' is a fully-unsaturated gray. For lightness, ''50%'' represents the "normal" color, while ''100%'' is white and ''0%'' is black. If the saturation or lightness is less than ''0%'' or the lightness is greater than ''100%'', they are clipped to those values before being converted to an RGB color. Some output devices may support saturations greater than ''100%'', just as they support RGB values greater than ''100%''. If the saturation exceeds the output device's gamut, it must be clipped to that device's gamut before being converted to an RGB color. This clipping should preserve the hue of the color (that is, it's shouldn't be the same thing as clipping an RGB component to the device's gamut), but this specification does not define how to do so. The final argument specifies the alpha channel of the color. It's interpreted identically to the fourth argument of the ''rgba()'' function. If omitted, it defaults to ''100%''.
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(0, 100%, 50%)''.
The advantage of HSL over RGB is that it is far more intuitive: one can guess at the colors they want, and then tweak. It is also easier to create sets of matching colors (by keeping the hue the same and varying the saturation and lightness).
For example, the following colors can all be generated off of the basic "green" hue, just by varying the other two arguments:
		hsl(120, 100%, 50%) lime green
		hsl(120, 100%, 25%) dark green
		hsl(120, 100%, 75%) light green
		hsl(120, 75%, 85%)  pastel green
		

Converting HSL colors to RGB colors

Converting an HSL color to RGB 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;
	}
	

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.
0° Reds
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
30° Red-Yellows (=Oranges)
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
60° Yellows
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
90° Yellow-Greens
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
120° Greens
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
150° Green-Cyans
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
180° Cyans
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
210° Cyan-Blues
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
240° Blues
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
270° Blue-Magentas
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
300° Magentas
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%
330° Magenta-Reds
Saturation
100%75%50%25%0%
100%
88%
75%
63%
50%
38%
25%
13%
0%

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.
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( <>, <>, <> [, <> ]? )
The first argument specifies the hue, and is interpreted identically to ''hsl()''. The second argument specifies the amount of white to mix in, as a percentage from ''0%'' (no whiteness) to ''100%'' (full whiteness). Similarly, the third argument specifies the amount of black to mix in, also from ''0%'' (no blackness) to ''100%'' (full blackness). Values outside of these ranges make the function invalid. If the sum of these two arguments is greater than 100%, then at computed-value time they are normalized to add up to 100%, with the same relative ratio. The fourth argument specifies the alpha channel of the color. It's interpreted identically to the fourth argument of the ''rgba()'' function. If omitted, it defaults to ''100%''. The resulting color can be thought of conceptually as a mixture of paint in the chosen hue, white paint, and black paint, with the relative amounts of each determined by the percentages. If white+black is equal to ''100%'' (after normalization), it defines an achromatic color, or some shade of gray, without any hint of the chosen hue.

Converting HWB colors to RGB colors

Converting an HWB color to RGB is straightforward, and related to how one converts HSL to RGB. The following Javascript implementation of the algorithm assumes that the white and black components have already been normalized, so their sum is no larger than 100%, and have been converted into numbers in the range [0,1].
	function hwbToRgb(hue, white, black) {
		var rgb = hslToRgb(hue, 1, .5);
		for(var i = 0; i < 3; i++) {
			rgb[i] *= (1 - white - black);
			rgb[i] += white;
		}
		return rgb;
	}
	

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%

Specifying Grays: the ''gray()'' functional notation

Grays are a very special set of colors. They're fully desaturated (lacking any actual color at all), which means that specifying a gray with any of the other notations requires specifying some redundant information: if specifying the color with ''rgb()'', all three channels are identical; if specifying the color with ''hsl()'', the hue is irrelevant and the saturation is locked to ''0%''. The ''gray()'' functional notation simplifies specifying this common set of colors, so that only the necessary information is required.
	gray() = gray( [<> | <>] [, <>]? )
	
The first argument specifies the shade of gray, while the second optional argument specifies the alpha channel of the color. ''gray(A)'' represents the same color as ''rgb(A,A,A)''. ''gray(A,B)'' represents the same color as ''rgb(A,A,A,B)''.
gray() should have a keyword arg that opts it into smarter modes. "luminance", for example, instead of being a simple expansion to ''rgb()'', could actually compute the gray with the specified luminance. The sRGB power curve means that grays are much darker than they "should" be; 50% gray has a luminance of .21, for example, rather than .5. Reversing luminance to color is easy for grays: if L < .0774, x * 12.92; otherwise, (x ^ (5/12)) * 1.055 - .055. Alternately, base it on relative contrast ratios somehow? #757575 is the gray that lives exactly between white and black, in contrast-ratio space. Taking 0% to be black, 100% to be white, and 50% to be #757575, you convert between contrast-ratio space and luminance space with L = 21^p, then find the gray with that luminance.

Device-dependent CMYK Colors: the ''device-cmyk()'' function

While screens typically display colors directly with RGB pixels, printers often represent colors in different ways. In particular, one of the most common print-based ways of representing colors is with CMYK: a combination of cyan, magenta, yellow, and black which yields a particular color on that device. The ''device-cmyk()'' function allows authors to specify a color in this way:
		device-cmyk() = device-cmyk( <> [, <>]? [, <> ]? )
		<cmyk-components> = <>, <>, <>, <>
		<cmyk-component> = <> | <>
	
The arguments of the ''device-cmyk()'' function specify the cyan, magenta, yellow, and black components, in order, as a number between 0 and 1 or a percentage between 0% and 100%. These two usages are equivalent, and map to each other linearly. Values less than 0 or 0%, or greater than 1 or 100%, are not invalid; instead, they are clamped to 0/0% or 1/100%. The fifth argument specifies the alpha channel of the color. It's interpreted identically to the fourth argument of the ''rgba()'' 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. Unfortunately, CSS cannot do that; various CSS features require an RGB color, so that compositing/blending/etc. can be done. As such, CMYK colors must be converted to an equivalent RGB color. This is not trivial, like the conversion from HSL or HWB to RGB; the precise conversion depends on the precise characteristics of the output device. If the user agent has information about the output device such that it believes it can accurately convert the CMYK color to a correct RGB color, the computed value of the ''device-cmyk()'' function must be that RGBA color. Otherwise, the computed value must be the fallback color.
For example, the following colors are equivalent (under the default conversion listed above):
			color: device-cmyk(0, 81%, 81%, 30%);
			color: rgb(178, 34, 34);
			color: firebrick;
		
Note: these colors might not match precisely if the browser knows a more precise conversion between CMYK and RGB colors. It's recommended that if authors use any CMYK colors in their document, that they use only CMYK colors in their document to avoid any color-matching difficulties.

Converting Between CMYK and RGB-Based Colors

While most colors defined in this specification are directly compatible with RGBA, and thus can be mechanically and consistently converted back and forth with it, CMYK colors are not necessarily directly compatible; a given CMYK color might map to various RGBA colors depending on the physical characteristics of the output device. Ideally, the user agent will be aware of the output device's color profiles for RGBA and CMYK. If this is true, then the user agent must convert between CMYK and RGBA colors (and vice versa) by first converting the color into an appropriate device-independent color space, such as CIELab, and then converting into the output color space, using the appropriate color profiles for each operation. This is not always possible, however. In that case, the user agent must use the following naive conversion algorithms. To naively convert from CMYK to RGBA: If a fallback color was specified, return that color (converting it to RGB as well, if necessary). Otherwise:
  • red = 1 - min(1, cyan * (1 - black) + black)
  • green = 1 - min(1, magenta * (1 - black) + black)
  • blue = 1 - min(1, yellow * (1 - black) + black)
  • Alpha is same as for input color.
To naively convert from RGBA to CMYK:
  • black = 1 - max(red, green, blue)
  • cyan = (1 - red - black) / (1 - black), or 0 if black is 1
  • magenta = (1 - green - black) / (1 - black), or 0 if black is 1
  • yellow = (1 - blue - black) / (1 - black), or 0 if black is 1
  • alpha is the same as the input color
  • fallback color must be set to the input color

Modifying Colors: the ''color()'' function

When specifying a color scheme for a site, one often wants a color that is close to another color, but slightly different. This becomes more important when CSS Variables are used, where an author may wish to define a "base" color, and then produce an array of slightly modified colors to use elsewhere. The ''color()'' function takes an existing color, and applies zero or more "color adjusters" to it, which specify how to manipulate the color in some way. Several of the color adjusters straightforwardly manipulate the color as an RGB, HSL, or HWB color, as if you'd specified a color in the appropriate syntax with one argument slightly modified. Others perform more complex manipulations of the color, such as blending it or finding contrasting colors. Additionally, the ''color()'' function defines a new, more intuitive syntax for specifying named colors, based on CNS. Issue: Actually add the CNS thing.
		color() = color( [ <> | <> ] <>* )
		<color-adjuster> =
		    [red( | green( | blue( | alpha( | a(] ['+' | '-']? [<> | <>] ) |
		    [red( | green( | blue( | alpha( | a(] '*' <> ) |
		    rgb( ['+' | '-'] [<> | <>]{3} ) |
		    rgb( ['+' | '-'] <> ) |
		    rgb( '*' <> ) |

		    [hue( | h(] ['+' | '-' | '*']? <> ) |
		    [saturation( | s(] ['+' | '-' | '*']? <> ) |
		    [lightness( | l(] ['+' | '-' | '*']? <> ) |
		    [whiteness( | w(] ['+' | '-' | '*']? <> ) |
		    [blackness( | b(] ['+' | '-' | '*']? <> ) |

		    tint( <> ) |
		    shade( <> ) |

		    blend( <> <> [rgb | hsl | hwb]? ) |
		    blenda( <> <> [rgb | hsl | hwb]? ) |

		    contrast( <>? )
	
The first argument specifies the base color. If a <> is given, the base color is the HSL color with the given <>, 100% saturation, and 50% lightness (in other words, the fully-saturated color with the given hue). After the base color, zero or more <>s can be specified. Each <> modifies the color in some way, passing a new base color to the next <> in the list. The same <> can be specified more than once in the list, such as ''color(red s(- 10%) s(- 10%))''; each instance just modifies the color appropriately (in this case, producing ''hsl(0deg, 80%, 50%)''). There are several classes of <>s with various effects, defined in the following sections. The computed value of a ''color()'' function is the color produced by applying all the <>s to the base color. Note: While scaling can be specified without any spaces, like ''lightness(*150%)'', adding/subtracting must be done with spaces after the +/-, or else the +/- will be interpreted as part of the number by the CSS parser. An achromatic color doesn't have a unique hue, so some <>s that would make the color no longer achromatic (such as ''s(50%)'') have special behavior for achromatic colors, as described in each adjuster's description. However, it is possible for, within the space of a single ''color()'' function, for the base color to be chromatic, an adjuster to make it achromatic, and a following adjuster to make it chromatic again, with an author having the reasonable expectation that the hue is maintained. To allow this, during the evaluation of a ''color()'' function's <>s, rather than storing intermediate colors as a 4-tuple of red, green, blue, and alpha, as usual for CSS colors, intermediate colors must be stored as a 5-tuple of red, green, blue, alpha, and hue angle, where the hue angle may be null for some colors and after some operations. Whenever an operation interprets an achromatic color in HSL or HWB space, if the color has a non-null hue angle, that hue must be used for the color's HSL/HWB interpretation. (Individual operations define how to handle null hue angles.) If the base color is achromatic, the hue angle is initially null.
For example, here's a possible ''color()'' function that lightens and brightens the base color:
color(X w(+ 20%) s(+ 20%))
If X is a color like ''blue'', this works in the expected way - after each operation, the color is still chromatic (and the return value is ''#33f''). On the other hand, if X is a greenish gray like ''#787'', which is represented in HWB as ''hwb(120deg, 44%, 50%)'', the first operation will boost the sum of white and black to greater than ''100%'', making it an achromatic gray (''#8f8f8f'', to be specific). However, the ''color()'' function remembers that the hue of the color was originally ''120deg'', so when the second operation saturates the color, it will result in a greenish-gray again (''hsl(120deg, 20%, 56%)'', slightly lighter and brighter than the original color, which is what was intended).
More possibilities:
  • Muted colors. Closure CSS Compiler computes a muted color from a FG and BG color: take the FG's hue, drop the FG saturation some amount, average FG and BG lightness. I want more use-cases and visual examples of this being used.
  • Inverting a color. Use-cases? Is this just done by inverting the r/g/b channels? Is the ''contrast()'' adjuster what people really mean when they ask for invert?
  • Warmer/cooler. How to define? What's warmer, red or yellow? What's cooler, blue or green?

RGBA Adjustment

The most basic set of <>s modify the color's channels directly, altering the amount of red, green, blue, or alpha in the color.
[red( | green( | blue( | alpha( | a(] ['+' | '-']? [<> | <>] )
[red( | green( | blue( | alpha( | a(] '*' <> )
Sets or adjust the red, blue, green, or alpha channels of the base color. If there is no operator, the given channel is set to the given value. If the operator is ''+'' or ''-'', the given channel is interpreted as the matching type (<> or <>) and then incremented or decremented by the given value. If the operator is ''*'', the given channel is multipled by the given value.
rgb( ['+' | '-']? [<> | <>]{3} )
Adjusts the base color in the red, green, and blue channels simultaneously. All three channels are interpreted as the matching type (<> or <>) and then incremented or decremented by the given values, with the first value adjusting the red channel, the second value adjusting the green channel, and the third value adjusting the blue channel.
rgb( ['+' | '-'] <> )
Identical to the previous clause, except that the adjustments to the three channels are specified in hexadecimal format; the <> is interpreted as a hex color, then the red, green, and blue channels of the color are applied as adjustments to the base color.
For example, in ''color(red rgb(+ #004400))'', the base color is ''red'' (''#ff0000''). The red and blue channels aren't adjusted at all (those channels in the given color are both 0), and the green channel is increased by 4416, resulting in a final color of ''#ff4400''.
rgb( '*' <> )
The red, green, and blue channels of the base color are multiplied by the given value.
All <>s in this section, except for ''alpha()'' and ''a()'', set the hue angle to null if the resulting color is achromatic.

HSL/HWB Adjustment

The ''hsl()'' and ''hwb()'' functions provide alternative ways to specify colors numerically, intended to be easier and more intuitive for humans. Similarly, the ''color()'' function allows a color to be adjusted in these "virtual channels".
[hue( | h(] ['+' | '-' | '*']? <> )
Sets or adjusts the hue of the base color, when base color is interpreted as an HSL color. If there is no operator, the hue is set to the given value, regardless of what the hue angle was previously. Otherwise, the hue is incremented or decremented, as appropriate, by the given value. If the hue angle is null, the adjuster instead does nothing.
[saturation( | s(] ['+' | '-' | '*']? <> )
[lightness( | l(] ['+' | '-' | '*']? <> )
[whiteness( | w(] ['+' | '-' | '*']? <> )
[blackness( | b(] ['+' | '-' | '*']? <> )
Sets or adjusts the saturation, lightness, whiteness, or blackness of the base color, when base color is interpreted as an HSL or HWB color, as appropriate. If there is no operator, the given channel is set to the given value. If the operator is ''+'' or ''-'', the given channel is incremented or decremented by the given value. If the operator is ''*'', the given channel is multiplied by the given value. If the hue angle is null, the operation is ''s()'' or ''saturation()'', and the adjuster would make the saturation greater than ''0%'', it instead does nothing. If the hue angle is null, the operation is ''w()'', ''white()'', ''b()'', or ''black()'', and the adjuster would make the sum of whiteness and blackness less than 100%, it additionally adjusts the opposite HWB channel to make the sum equal to 100%. (That is, ''color(white w(- 20%))'' would represent the same color as ''hwb(0, 80%, 20%)''.)

Tints and Shades: the ''tint'' and ''shade'' adjusters

While the ''color()'' function does allow HWB adjustment of colors, the peculiarities of how HWB is defined make it more difficult than it should be to just define a lighter or darker version of a color. The ''tint'' and ''shade'' adjusters fix this, by simply mixing the base color with white or black.
''tint( <> )''
Mixes the base color with pure white to produce a lighter version of the base color. Specifying a <> less than ''0%'' or greater than ''100%'' is a syntax error, and makes the function invalid. Linearly interpolate the red, green, and blue channels of the base color with the red, green, and blue channels of pure white (''rgb(255,255,255)''), where ''0%'' returns the base color and ''100%'' returns pure white. Note: ''tint(X%)'' is identical to ''blend(white X% rgb)''.
''shade( <> )''
Mixes the base color with pure black to produce a darker version of the base color. Identical to the previous clause, except the base color is mixed with pure black (''rgb(0,0,0)'') rather than pure white.

Color Blending: the ''blend'' and ''blenda'' adjusters

The ''tint()'' and ''shade()'' adjusters are common cases of the more general ''blend()'' adjuster, which mixes the base color with an arbitrary color.
''blend( <> <> [rgb | hsl | hwb]? )''
Mixes the base color with the given color to produce an intermediate color. Specifying a <> less than ''0%'' or greater than ''100%'' is a syntax error, and makes the function invalid. The final argument specifies which color space to blend the colors in, defaulting to ''rgb'' if not specified. Both the base color and the given color are interpreted as colors in the given color space, then the components are blended.
For example, ''color(yellow blend(blue 50%))'' blends yellow (''#ffff00'') with blue (''#0000ff'') equally, resulting in ''#808080'', a medium gray. On the other hand, ''color(yellow blend(blue 50% hsl))'' blends the same colors in HSL space, where yellow is ''hsl(60, 100%, 50%)'' and blue is ''hsl(240, 100%, 50%)'', which results in ''hsl(150, 100%, 50%)'', a fully-saturated shade of green.
To determine the resulting color, interpret the base color and the given color in the appropriate color space (RGB, HSL, or HWB). Linearly interpolate each of the channels of the colors according to the given <>, where ''0%'' produces the specified <> and ''100%'' produces the base color. If the color space is ''hsl'' or ''hwb'', interpolate the hue channel either clockwise or counterclockwise, whichever results in a shorter "path" between the two hue angles. If the two hue angles are on opposite sides of the hue circle (separated by 180 degrees), take the clockwise path. If the hue angle is null for one of the colors but not the other, treat the null hue angle as being equal to the non-null hue angle for the purpose of this adjuster. If both hue angles are null, the resulting color's hue angle is null as well. Note: The choice of how to transition when the difference is ''180deg'' is arbitrary, and was chosen merely to provide an unambiguous answer. To achieve counter-clockwise behavior, adjust either color's hue angle by a small amount toward the desired direction.
For example, blending ''yellow'' (hue angle ''60deg'') with 50% ''purple'' (hue angle ''300deg'') results in ''red'' (hue angle ''0deg''), not ''cyan'' (hue angle ''180deg''), even though 60*50% + 300*50% == 180, because the distance between the two colors when moving counter-clockwise is only 120 degrees, as opposed to 240 degrees when going clockwise.
''blenda( <> <> [rgb | hsl | hwb]? )''
Identical to the previous clause, except it pays attention to the alpha channel of the two colors (''blend()'' just preserves the alpha channel of the base color). Let w be the specified <>, rescaled to the range [-1,1], where ''0%'' maps to -1 and ''100%'' maps to 1. Let a be the difference of the alpha channels of the base color and the specified <>, also rescaled to the range [-1,1], where ''-100%'' (''0%'' base color alpha and ''100%'' specified color alpha) maps to -1 and ''100%'' maps to 1. If w * a == -1, let new weight equal w. Otherwise, let new weight equal (w + a) / (1 + w*a). Reinterpret new weight as a percentage in the range [0%, 100%], where -1 maps to ''0%'' and 1 maps to ''100%''. Calculate the result color as if ''blend()'' had been specified, using the new weight percentage instead of the specified <>, and set the alpha channel of the result color to the average of the alpha channels of the base color and the specified <>.

This blends the two colors in a way that pays attention to alpha, similar to how compositing does. Is there a better formula? The current one was determined empirically to give good results, but isn't motivated by any theory.

Should we swap the defaults, so ''blend()'' does the alpha blending, and another name (or maybe another parameter) ignores alpha like ''blend()'' currently does? Check with definitions in CSS compositing and blending module.

Guaranteeing Adequate Contrast: the ''contrast'' adjuster

Guaranteeing that foreground and background colors contrast sufficiently is important, but even if one knows the mathematical definition of "appropriate contrast", it's not trivial to calculate and figure out whether two arbitrary colors are good enough. The ''contrast()'' adjuster makes this easy, automatically computing a color that is sufficiently contrasting with the base color to satisfy accessibility guidelines.
''contrast( <>? )''
Finds a color that contrasts with the base color suffficiently to satisfy accessibility guidelines, using the definition of "contrast" given by WCAG 2.0 Guideline 1.4.3. The <> specifies the desired similarity between the base color and the returned color. ''0%'' will return the minimum-contrast color (the closest color to the base color that still contrasts sufficiently), while ''100%'' will return the maximum-contrast color (white or black, whichever contrasts the base color more) Specifying a value less than ''0%'' or greater than ''100%'' is invalid and a syntax error. If omitted, the <> defaults to ''100%''.
  1. Compute the luminance of the base color. If it's less than .5, the maximum-contrast color is ''hwb(X, 100%, 0%)'', where X is the hue angle of the base color. Otherwise, the maximum-contrast color is ''hwb(X, 0%, 100%)'', where X is the hue angle of the base color. Note: In other words, the maximum-contrast color is either white or black, but with the hue set up correctly for the next step's linear interpolation.
  2. Looking only at colors that are linear interpolations in HWB space (a la the ''blend()'' adjuster) between the base color and the maximum-contrast color, find the color with the smallest contrast ratio with the base color that is greater than 4.5. This is the minimum-contrast color. If there is no color with contrast ratio greather than 4.5, return the maximum-contrast color immediately. Note: 4.5 is the contrast ratio required by WCAG for Level AA contrast. Note: Using this method, the contrast ratio will be monotonically non-increasing as you go from the maximum-contrast color to the base color, so a simple binary search will identify the minimum-contrast color in a small number of iterations.
  3. Blend the minimum-contrast color and maximum-contrast color according to the specified <>, as if ''color(maximum-contrast color blend(minimum-contrast color <> hwb))'' were specified. Return the blended color.

To compute the luminance of a color:

  1. Scale the red, green, and blue channels of the color to the range [0,1].
  2. For each channel, if the channel's value is less than or equal to 0.03928, set the channel's value to channel / 12.92. Otherwise, set the channel's value to ((channel + 0.055) / 1.055) ^ 2.4. Note: This reverses the logarithmic power scaling of the sRGB gamut, so the value of the channel is approximately linear related to the amount of light required to represent it.
  3. The luminance is: 0.2126*R + 0.7152*G + 0.0722*B where R, G, and B are the adjusted red, green, and blue channels from the previous step.
Note: The luminance of a color within the sRGB gamut is contained within the range [0,1], where black is 0 and white is 1.

To compute the contrast ratio of two colors:

  1. Compute the luminance of both colors.
  2. The contrast ratio is: (L1 + 0.05) / (L2 + 0.05) where L1 is the larger of the two luminances, and L2 is the smaller.
Note: The contrast ratio of two colors is contained within the range [1,21], where two identical colors are 1 and the ratio of white and black is 21.

Transparency: the 'opacity' property

Opacity can be thought of as a postprocessing operation. Conceptually, after the element (including its descendants) is rendered into an RGBA offscreen image, the opacity setting specifies how to blend the offscreen rendering into the current composite rendering. See simple alpha compositing for details.
	Name: opacity
	Value: <>
	Initial: 1
	Applies to: all elements
	Inherited: no
	Percentages: N/A
	Media: visual
	Computed value: The specified value, clamped to the range [0,1].
	
<>
The opacity to be applied to the element. It is interpreted identically to its definition in ''rgba()'', except that the resulting opacity is applied to the entire element, rather than a particular color.
The 'opacity' property applies the specified opacity to the element as a whole, including its contents, rather than applying it to each descendant individually. This means that, for example, an opaque child occluding part of the element's background will continue to do so even when 'opacity' is less than 1, but the element and child as a whole will show the underlying page through themselves. If 'opacity' has a value less than 1, the element forms a stacking context for its children. This means that any content outside of it cannot be layered in z-order between pieces of content inside of it, and vice versa. If the element is in a context where the 'z-index' property applies, the ''auto'' value is treated as ''0'' for the element. See section 9.9 and Appendix E of [[!CSS21]] for more information on stacking contexts. The rules in this paragraph do not apply to SVG elements, since SVG has its own rendering model ([[!SVG11]], Chapter 3).

Simple alpha compositing

When drawing, implementations must handle alpha according to the rules in Section 14.2 Simple alpha compositing of [[!SVG11]]. (If the color-interpolation or 'color-rendering' properties mentioned in that section are not implemented or do not apply, implementations must act as though they have their initial values.)

Color Management: the 'color-correction' property

CSS Level 1 ([[CSS1]]), CSS Level 2 ([[!CSS21]]), and CSS Color Level 3 ([[CSS3COLOR]]) define colors specified in CSS to be in the sRGB color space ([[!SRGB]]). However, most or all existing Web browser implementations do not correct colors specified in HTML, CSS, or untagged images even when the proper correction is known. If browsers did so, it would make the colors displayed in Web pages more consistent between different displays and operating systems. However, a more important type of consistency is the consistency of colors in different parts of a page on the same display, such as between colors specified in style sheets and colors in images, or between those colors and colors drawn by plugins. Improving the consistency of colors between different displays, therefore, requires care not to cause the worse problem of inconsistency of colors on the same display. In the long run, we hope that it might be possible for implementations to switch to treating CSS colors and colors in untagged images as being in sRGB by default. (This may depend on additions for color management being made to the plugin API and popular plugins using those additions.) Therefore, this specification provides a way to clearly opt in to that correct behavior, but provides an default behavior (initial value) that may be equivalent to this opt-in.
	Name: color-correction
	Value: auto | sRGB
	Initial: auto
	Applies to: all elements
	Inherited: yes
	Percentages: N/A
	Media: visual
	Computed value: as specified
	
The 'color-correction' property specifies the color space that colors specified in CSS and colors in untagged images are in. An untagged image is an image that is not explicitly assigned a color profile, as defined by the image format. It does not apply to videos, since untagged video should be presumed to be in CCIR 601.

Really? Shouldn't video be consistent with images? Or do implementations really do this differently?

sRGB
Colors specified in CSS and colors in untagged images are in the sRGB color space.
auto
The color space for colors specified in CSS and colors in untagged images is not defined. However, implementations must use a single color space for such colors so that they match each other. When doing so would not cause color mismatches with content such as plugins, implementations should treat ''auto'' the same as ''sRGB''.
Note: The initial value of this property may change in a future level of this specification.

Preserving Colors in Different-Capability Devices: the 'color-adjust' property

On most monitors, the color choices that authors make have no significant difference in terms of how the device performs; displaying a document with a white background or a black background is approximately equally easy. However, some devices have limitations and other qualities that make this assumption untrue. For example, printers tend to print on white paper; a document with a white background thus has to spend no ink on drawing that background, while a document with a black background will have to expend a large amount of ink filling in the background color. This tends to look fairly bad, and sometimes has deleterious physical effects on the paper, not to mention the vastly increased printing cost from expending the extra ink. Even fairly small differences, such as coloring text black versus dark gray, can be quite different when printing, as it switches from using a single black ink to a mixture of cyan, magenta, and yellow ink, resulting in higher ink usage and lower resolution. As a result, in some circumstances user agents will alter the styles an author specifies in some particular context, adjusting them to be more appropriate for the output device and to accommodate what they assume the user would prefer. However, in some cases the document may be using colors in important, well-thought-out ways that the user would appreciate, and so the document would like some way to hint to the user agent that it might want to respect the page's color choices. The 'color-adjust' property controls this.
	Name: color-adjust
	Value: economy | exact
	Initial: economy
	Applies to: all elements
	Inherited: yes
	Media: visual
	Computed value: as specified
	Percentages: N/A
	
The 'color-adjust' property provides a hint to the user-agent about how it should treat color and style choices that might be expensive or generally unwise on a given device, such as using light text on a dark background in a printed document. If user agents allow users to control this aspect of the document's display, the user preference must be respected more strongly than the hint provided by 'color-adjust'. It has the following values:
economy
The user agent should make adjustments to the page's styling as it deems necessary and prudent for the output device. For example, if the document is being printed, a user agent might ignore any backgrounds and adjust text color to be sufficiently dark, to minimize ink usage.
exact
This value indicates that the page is using color and styling on the specified element in a way which is important and significant, and which should not be tweaked or changed except at the user's request. For example, a mapping website offering printed directions might "zebra-stripe" the steps in the directions, alternating between white and light gray backgrounds. Losing this zebra-striping and having a pure-white background would make the directions harder to read with a quick glance when distracted in a car.

Default Style Rules

The following stylesheet is informative, not normative. This style sheet could be used by an implementation as part of its default styling of HTML4, XHTML1, XHTML1.1, XHTML Basic, and other XHTML Family documents.
	html {
		color: black;
	}

	/* traditional desktop user agent colors for hyperlinks */
	:link    { color: blue; }
	:visited { color: purple; }
	
The default background of the root element must be ''transparent''. The default color of the canvas (the surface on which the document is painted) is UA-dependent, but is recommended to be ''white'', especially if the above 'color' rules are used.

APIs for Parsing and Manipulating Colors

In many applications, authors have to use and manipulate colors. CSS allows many different useful syntaxes for colors, but these are only usable in stylesheets, not in script. This section defines a number of APIs for creating and manipulating colors in script:
  • It allows CSS colors to be parsed into JS objects-- use the CSSColor.parse("rebeccapurple") method to get a corresponding color object, or one of the specific color constructors, like new HSLColor("rebeccapurple"), to get back a specific type of color class.
  • It allows colors to be manipulated in any of the syntaxes that CSS exposes: {{RGBColor}} for RGBA colors, with components ranging from 0-1; {{HexColor}} for RGBA colors, with components ranging from 0-255; {{HSLColor}} for HSLA colors, corresponding to the ''hsla()'' syntax; and {{CMYKColor}} for CMYK colors, corresponding to the ''device-cmyk()'' syntax. All of these expose their components as attributes you can manipulate directly, and colors can be easily converted between any of the classes with methods like toRGB() and toHSL().
  • It allows colors to be automatically stringified back into a form that CSS understands, so you can write code like:
    			var c = new HSLColor("red");
    			c.l += .1;
    			el.style.backgroundColor = c;
    			// c is automatically turned into the string "hsla(0deg, 100%, 60%, 100%)"
    			
    If you wish, you can control what type of syntax it stringifies as, like c.toString("hsl").
  • It's explicitly designed to be easy to extend with new methods and new color types, and gives guidance to help you do it correctly (see [[#api-extending]]).

The RGBColor Object

The {{RGBColor}} class is the "primary" color class, because all CSS colors must be RGB-compatible (that is, capable of being converted into a roughly equivalent RGB color). As a result, it's always possible to convert a UA-defined color class (and any properly-designed author-defined color classes, see [[#api-extending]]) into an equivalent {{RGBColor}} instance. This fact is used to aid in automatic handling of several methods common to all color classes.
	[Constructor(double r, double g, double b, optional double a=1),
	 Constructor(RGBColor rgb),
	 Constructor(CSSColor css),
	 Constructor(optional RGBColorInit color),
	 Constructor(DOMString cssstring)]
	interface RGBColor : CSSColor {
		attribute double r;
		attribute double g;
		attribute double b;
		attribute double a;

		RGBColor   toRGB();
		HSLColor   toHSL();
		HexColor   toHex();
		CMYKColor  toCMYK();
		DOMString? toName();

		static attribute ColorStringifiers stringifiers;
		attribute ColorStringifierType defaultStringifier;

	};

	dictionary RGBColorInit {
		double r = 0;
		double g = 0;
		double b = 0;
		double a = 1;
	};

	enum ColorStringifierType {
		"rgb",
		"hsl",
		"hex",
		"cmyk"
	};

	dictionary ColorStringifiers {
		ColorStringifierCallback rgb;
		ColorStringifierCallback hsl;
		ColorStringifierCallback hex;
		ColorStringifierCallback cmyk;
	};

	callback ColorStringifierCallback = DOMString (CSSColor color, any ...args);
	
RGBColor(double r, double g, double b, optional double a=1)
Defined as follows in ECMAScript:
			function(r, g, b, a) {
				this.r = r;
				this.g = g;
				this.b = b;
				this.a = a === undefined ? 1 : a;
				return this;
			}
			
Note: The RGBColor class's attributes have a normal range of 0 to 1, not 0 to 255. See the HexColor class if you prefer working in the 0-255 range.
RGBColor({{RGBColor}} rgb)
Defined as follows in ECMAScript:
			function(rgb) {
				this.r = rgb.r;
				this.g = rgb.g;
				this.b = rgb.b;
				this.a = rgb.a;
				return this;
			}
			
RGBColor({{CSSColor}} css)
Defined as follows in ECMAScript:
			function(css) {
				const color = css.toRGB();
				this.r = color.r;
				this.g = color.g;
				this.b = color.b;
				this.a = color.a;
				return this;
			}
			
RGBColor(optional {{RGBColorInit}} color)
Defined as follows in ECMAScript:
			function(color) {
				if(color === undefined) color = {r:0, g:0, b:0, a:1};
				this.r = color.r === undefined ? 0 : color.r;
				this.g = color.g === undefined ? 0 : color.g;
				this.b = color.b === undefined ? 0 : color.b;
				this.a = color.a === undefined ? 1 : color.a;
				return this;
			}
			
RGBColor(DOMString cssstring)
Parse a component value from the passed string. If this operation succeeds and the result is a valid CSS color, construct an instance of the {{RGBColor}} class with attributes initialized to represent that color. Otherwise, throw an XXXError.
toRGB()
Defined as follows in ECMAScript:
			function() {
				return new RGBColor(this);
			}
			
toHSL()
Defined as follows in ECMAScript:
			function() {
				return new HSLColor(this);
			}
			
toHex()
Defined as follows in ECMAScript:
			function() {
				return new HexColor(this);
			}
			
toCMYK()
Defined as follows in ECMAScript:
			function() {
				return new CMYKColor(this);
			}
			
toName()
Let thisObject be the object this method was called on. If the object this method was called on’s r, g, b, and a attributes, when multiplied by 255 and rounded to the nearest integer, are equal to the r, g, b, and a values assigned to a named color, return that color's name as a DOMString. Otherwise, return null.
r
g
b
a
The {{r}}, {{g}}, {{b}}, and {{a}} attributes represent the red, green, blue, and alpha channels of the RGB color that the RGBColor instance represents. Note: The RGBColor class's attributes have a normal range of 0 to 1, not 0 to 255. See the HexColor class if you prefer working in the 0-255 range.
stringifiers
Must be initially set to the following ECMAScript object:
			{
				"rgb": function(color) {
					const r = color.r*100 + "%";
					const g = color.g*100 + "%";
					const b = color.b*100 + "%";
					const a = color.a*100 + "%";
					return "rgba(" + [r,g,b,a].join(", ") + ")";
				},
				"hsl": function(color) {
					return "" + color.toHSL();
				},
				"hex": function(color) {
					return "" + color.toHex();
				},
				"cmyk": function(color) {
					return "" + color.toCMYK();
				}
			}
			
defaultStringifier
The defaultStringifier attribute must be initially set to "rgb".

The HSLColor Class

	[Constructor(double h, double s, double l, optional double a=1),
	 Constructor(RGBColor rgb),
	 Constructor(HSLColor hsl),
	 Constructor(CSSColor css),
	 Constructor(optional HSLColorInit color),
	 Constructor(DOMString cssstring)]
	interface HSLColor : CSSColor {
		attribute double h;
		attribute double s;
		attribute double l;
		attribute double a;

		RGBColor toRGB();
		HSLColor toHSL();

		static attribute ColorStringifiers stringifiers;
		attribute ColorStringifierType defaultStringifier;
	};

	dictionary HSLColorInit {
		double h = 0;
		double s = 0;
		double l = 0;
		double a = 1;
	};
	
HSLColor(double h, double s, double l, optional double a=1)
Defined as follows in ECMAScript:
			function(h, s, l, a) {
				this.h = h;
				this.s = s;
				this.l = l;
				this.a = a === undefined ? 1 : a;
				return this;
			}
			
HSLColor({{RGBColor}} rgb)
Defined as follows in ECMAScript:
			function(rgb) {
				const {r,g,b,a} = rgb;
				this.a = a;

				const max = Math.max(r, g, b);
				const min = Math.min(r, g, b);
				const chroma = max - min;
				if(chroma == 0) {
					this.h = 0;
				} else if(r == max) {
					this.h = (((g - b) / chroma) % 6) * 360;
				} else if(g == max) {
					this.h = ((((b - r) / chroma) + 2) % 6) * 360;
				} else {
					this.h = ((((r - g) / chroma) + 4) % 6) * 360;
				}

				this.l = (min + max)/2;

				if(this.l == 0 || this.l == 1) {
					this.s = 0;
				} else {
					this.s = chroma / (1 - Math.abs(2 * this.l - 1));
				}

				return this;
			}
			
HSLColor({{HSLColor}} hsl)
Defined as follows in ECMAScript:
			function(hsl) {
				this.h = hsl.h;
				this.s = hsl.s;
				this.l = hsl.l;
				this.a = hsl.a;
				return this;
			}
			
HSLColor({{CSSColor}} css)
Defined as follows in ECMAScript:
			function(css) {
				const color = css.toHSL();
				this.h = color.h;
				this.s = color.s;
				this.l = color.l;
				this.a = color.a;
				return this;
			}
			
HSLColor(optional {{HSLColorInit}} color)
Defined as follows in ECMAScript:
			function(color) {
				if(color === undefined) color = {h:0, s:0, l:0, a:1};
				this.h = color.h === undefined ? 0 : color.h;
				this.s = color.s === undefined ? 0 : color.s;
				this.l = color.l === undefined ? 0 : color.l;
				this.a = color.a === undefined ? 1 : color.a;
				return this;
			}
			
HSLColor(DOMString cssstring)
Parse a component value from the passed string. If this operation succeeds and the result is a valid CSS color, construct an instance of the {{HSLColor}} class with attributes initialized to represent that color. Otherwise, throw an XXXError.
toRGB()
Defined as follows in ECMAScript:
			function() {
				const max = (1 - Math.abs(2 * this.l - 1)) * this.s;
				const h_ = ((this.h / 60 % 6) + 6) % 6;
				const mid = max * (1 - Math.abs((h_ % 2) - 1));

				if(h_ >= 0 && h_ < 1) {
					return new RGBColor(max, mid, 0);
				} else if(h_ >= 1 && h_ < 2) {
					return new RGBColor(mid, max, 0);
				} else if(h_ >= 2 && h_ < 3) {
					return new RGBColor(0, max, mid);
				} else if(h_ >= 3 && h_ < 4) {
					return new RGBColor(0, mid, max);
				} else if(h_ >= 4 && h_ < 5) {
					return new RGBColor(mid, 0, max);
				} else {
					return new RGBColor(max, 0, mid);
				}
			}
			
toHSL()
Defined as follows in ECMAScript:
			function() {
				return new HSLColor(this);
			}
			
h
s
l
a
The {{h}}, {{s}}, {{l}}, and {{a}} attributes represent the hue, saturation, lightness, and alpha channels of the HSL color that the HSLColor instance represents. Note: The HSLColor class's hue attribute has a normal range of 0 to 360 (denoted in degrees), and its other attributes have a normal range of 0 to 1.
stringifiers
Must be initially set to the following ECMAScript object:
			{
				"hsl": function(color) {
					const h = color.h + "deg";
					const s = color.s*100 + "%";
					const l = color.l*100 + "%";
					const a = color.a*100 + "%";
					return "hsla(" + [h,s,l,a].join(", ") + ")";
				}
			}
			
defaultStringifier
The defaultStringifier attribute must be initially set to "hsl".

The HWBColor Class

	[Constructor(double h, double w, double b, optional double a=1),
	 Constructor(RGBColor rgb),
	 Constructor(HSLColor hsl),
	 Constructor(HWBColor hwb),
	 Constructor(CSSColor css),
	 Constructor(optional HWBColorInit color),
	 Constructor(DOMString cssstring)]
	interface HWBColor : CSSColor {
		attribute double h;
		attribute double w;
		attribute double b;
		attribute double a;

		RGBColor toRGB();
		HSLColor toHSL();
		HWBColor toHWB();

		static attribute ColorStringifiers stringifiers;
		attribute ColorStringifierType defaultStringifier;
	};

	dictionary HWBColorInit {
		double h = 0;
		double w = 0;
		double b = 0;
		double a = 1;
	};
	
HWBColor(double h, double w, double b, optional double a=1)
Defined as follows in ECMAScript:
			function(h, w, b, a) {
				this.h = h;
				this.w = w;
				this.b = b;
				this.a = a === undefined ? 1 : a;
				return this;
			}
			
HWBColor({{RGBColor}} rgb)
Defined as follows in ECMAScript:
			function(rgb) {
				const {r,g,b,a} = rgb;
				this.a = a;

				const max = Math.max(r, g, b);
				const min = Math.min(r, g, b);
				const chroma = max - min;
				if(chroma == 0) {
					this.h = 0;
				} else if(r == max) {
					this.h = (((g - b) / chroma) % 6) * 360;
				} else if(g == max) {
					this.h = ((((b - r) / chroma) + 2) % 6) * 360;
				} else {
					this.h = ((((r - g) / chroma) + 4) % 6) * 360;
				}
				this.w = min;
				this.b = 1 - max;
				return this;
			}
			
HWBColor({{HSLColor}} hsl)
Defined as follows in ECMAScript:
			function(hsl) {
				this.h = hsl.h;
				this.a = hsl.a;
				...

				return this;
			}
			
HWBColor({{HWBColor}} hwb)
Defined as follows in ECMAScript:
			function(hsl) {
				this.h = hwb.h;
				this.w = hwb.w;
				this.b = hwb.b;
				this.a = hwb.a;
				return this;
			}
			
HWBColor({{CSSColor}} css)
Defined as follows in ECMAScript:
			function(css) {
				const color = css.toHWB();
				this.h = color.h;
				this.w = color.w;
				this.b = color.b;
				this.a = color.a;
				return this;
			}
			
HWBColor(optional {{HWBColorInit}} color)
Defined as follows in ECMAScript:
			function(color) {
				if(color === undefined) color = {h:0, w:0, b:0, a:1};
				this.h = color.h === undefined ? 0 : color.h;
				this.w = color.w === undefined ? 0 : color.w;
				this.b = color.b === undefined ? 0 : color.b;
				this.a = color.a === undefined ? 1 : color.a;
				return this;
			}
			
HWBColor(DOMString cssstring)
Parse a component value from the passed string. If this operation succeeds and the result is a valid CSS color, construct an instance of the {{HWBColor}} class with attributes initialized to represent that color. Otherwise, throw an XXXError.
toRGB()
Defined as follows in ECMAScript:
			function() {
				let w = this.w;
				let b = this.b;
				if(w+b > 1) {
					const sum = w+b;
					w /= sum;
					b /= sum;
				}

				const h = ((this.h / 60 % 6) + 6) % 6;
				const max = 1 - b;
				const mid = 1 - Math.abs((h % 2) - 1);
				const min = w;

				if(h >= 0 && h_ < 1) {
					return new RGBColor(max, mid, min);
				} else if(h >= 1 && h < 2) {
					return new RGBColor(mid, max, min);
				} else if(h >= 2 && h < 3) {
					return new RGBColor(min, max, mid);
				} else if(h >= 3 && h < 4) {
					return new RGBColor(min, mid, max);
				} else if(h >= 4 && h < 5) {
					return new RGBColor(mid, min, max);
				} else {
					return new RGBColor(max, min, mid);
				}
			
toHSL()
Defined as follows in ECMAScript:
			...
			
toHWB()
Defined as follows in ECMAScript:
			function() {
				return new HWBColor(this);
			}
			
h
w
b
a
The {{h}}, {{w}}, {{b}}, and {{a}} attributes represent the hue, whiteness, blackness, and alpha channels of the HWB color that the HWBColor instance represents. Note: The HWBColor class's hue attribute has a normal range of 0 to 360 (denoted in degrees), and its other attributes have a normal range of 0 to 1.
stringifiers
Must be initially set to the following ECMAScript object:
			{
				"hwb": function(color) {
					const h = color.h + "deg";
					const w = color.w*100 + "%";
					const b = color.b*100 + "%";
					const a = color.a*100 + "%";
					return "hwb(" + [h,w,b,a].join(", ") + ")";
				}
			}
			
defaultStringifier
The defaultStringifier attribute must be initially set to "hwb".

The HexColor Class

	[Constructor([Clamp] octet r, [Clamp] octet g, [Clamp] octet b, optional [Clamp] octet a = 255),
	 Constructor(RGBColor rgb),
	 Constructor(HexColor hex),
	 Constructor(CSSColor css),
	 Constructor(optional HexColorInit color),
	 Constructor(DOMString cssstring)]
	interface HexColor : CSSColor {
		[Clamp] attribute octet r;
		[Clamp] attribute octet g;
		[Clamp] attribute octet b;
		[Clamp] attribute octet a;

		RGBColor toRGB();
		HexColor toHex();

		static attribute ColorStringifiers stringifiers;
		attribute ColorStringifierType defaultStringifier;
	};

	dictionary HexColorInit {
		[Clamp] octet r = 0;
		[Clamp] octet g = 0;
		[Clamp] octet b = 0;
		[Clamp] octet a = 255;
	};
	
HexColor([Clamp] octet r, [Clamp] octet g, [Clamp] octet b, optional [Clamp] octet a=255)
Defined as follows in ECMAScript:
			function(r, g, b, a) {
				this.r = r;
				this.g = g;
				this.b = b;
				this.a = a === undefined ? 255 : a;
				return this;
			}
			
HexColor({{RGBColor}} rgb)
Defined as follows in ECMAScript:
			function(rgb) {
				var normalize = function(num) {
					return Math.max(0, Math.min(255, Math.round(num)));
				}
				this.r = normalize(rgb.r * 255);
				this.g = normalize(rgb.g * 255);
				this.b = normalize(rgb.b * 255);
				this.a = normalize(rgb.a * 255);
				return this;
			}
			
HexColor({{HexColor}} hex)
Defined as follows in ECMAScript:
			function(hex) {
				this.r = hex.r;
				this.g = hex.g;
				this.b = hex.b;
				this.a = hex.a;
				return this;
			}
			
HexColor({{CSSColor}} css)
Defined as follows in ECMAScript:
			function(css) {
				const color = css.toHex();
				this.r = color.r;
				this.g = color.g;
				this.b = color.b;
				this.a = color.a;
				return this;
			}
			
HSLColor(optional {{HexColorInit}} color)
Defined as follows in ECMAScript:
			function(color) {
				if(color === undefined) color = {r:0, g:0, b:0, a:255};
				this.r = color.r === undefined ? 0 : color.r;
				this.g = color.g === undefined ? 0 : color.g;
				this.b = color.b === undefined ? 0 : color.b;
				this.a = color.a === undefined ? 255 : color.a;
				return this;
			}
			
HexColor(DOMString cssstring)
Parse a component value from the passed string. If this operation succeeds and the result is a valid CSS color, construct an instance of the {{HexColor}} class with attributes initialized to represent that color. Otherwise, throw an XXXError.
toRGB()
Defined as follows in ECMAScript:
			function() {
				return new RGBColor(this.r/255, this.g/255, this.b/255, this.a/255);
			}
			
r
g
b
a
The {{r}}, {{g}}, {{b}}, and {{a}} attributes represent the red, blue, green, and alpha channels of the RGB color that the HexColor instance represents. Note: The HexColor class's attributes are all restricted to integers between 0 and 255. Setting them to a non-integer will round them to the nearest integer, and setting them to a number less than 0 or greater than 255 will set them to 0 or 255, respectively.
stringifiers
Must be initially set to the following ECMAScript object:
			{
				"hex": function(color) {
					const r = (color.r < 16 ? "0" : "") + color.r.toString(16);
					const g = (color.g < 16 ? "0" : "") + color.g.toString(16);
					const b = (color.b < 16 ? "0" : "") + color.b.toString(16);
					const a = (color.a < 16 ? "0" : "") + color.a.toString(16);
					return "#" + r + g + b + a;
				}
			}
			
defaultStringifier
The defaultStringifier attribute must be initially set to "hex".

The CMYKColor Class

	[Constructor(double c, double m, double y, double k, optional double a=1, optional fallback RGBColor?=null),
	 Constructor(RGBColor rgb),
	 Constructor(CMYKColor cmyk),
	 Constructor(CSSColor css),
	 Constructor(optional CMYKColorInit color),
	 Constructor(DOMString cssstring)]
	interface CMYKColor : CSSColor {
		attribute double c;
		attribute double m;
		attribute double y;
		attribute double k;
		attribute double a;
		attribute RGBColor? fallback;

		RGBColor toRGB();

		static attribute ColorStringifiers stringifiers;
		attribute ColorStringifierType defaultStringifier;
	};

	dictionary CMYKColorInit {
		double c = 0;
		double m = 0;
		double y = 0;
		double k = 0;
		double a = 1;
		RGBColor? fallback = null;
	};
	
CMYKColor(double c, double m, double y, double k, optional double a=1, optional {{RGBColor}}? fallback=null)
Defined as follows in ECMAScript:
			function(c, m, y, k, a, fallback) {
				this.c = c;
				this.m = m;
				this.y = y;
				this.k = k;
				this.a = a === undefined ? 1 : a;
				this.fallback = fallback === undefined ? null : fallback;
			}
			
CMYKColor({{RGBColor}} rgb)
Defined as follows in ECMAScript:
			function(rgb) {
				const {r,g,b,a} = rgb;
				this.a = a;

				const k = 1 - Math.max(r, g, b);
				this.k = k;

				if(k == 1) {
					this.c = 0;
					this.m = 0;
					this.y = 0;
				} else {
					this.c = (1 - r - k) / (1 - k);
					this.m = (1 - g - k) / (1 - k);
					this.y = (1 - b - k) / (1 - k);
				}

				this.fallback = new RGBColor(rgb);

				return this;
			}
			
CMYKColor({{CMYKColor}} cmyk)
Defined as follows in ECMAScript:
			function(cmyk) {
				this.c = cmyk.c;
				this.m = cmyk.m;
				this.y = cmyk.y;
				this.k = cmyk.k;
				this.a = cmyk.a;
				this.fallback = cmyk.fallback;
				return this;
			}
			
CMYKColor({{CSSColor}} css)
Defined as follows in ECMAScript:
			function(css) {
				const color = css.toCMYK();
				this.c = color.c;
				this.m = color.m;
				this.y = color.y;
				this.k = color.k;
				this.a = color.a;
				this.fallback = color.fallback;
				return this;
			}
			
CMYKColor(optional {{CMYKColorInit}} color)
Defined as follows in ECMAScript:
			function(color) {
				if(color === undefined) color = {c:0, m:0, y:0, k:0, a:1, fallback:null};
				this.c = color.c === undefined ? 0 : color.c;
				this.m = color.m === undefined ? 0 : color.m;
				this.y = color.y === undefined ? 0 : color.y;
				this.k = color.k === undefined ? 0 : color.k;
				this.a = color.a === undefined ? 1 : color.a;
				this.fallback = color.fallback === undefined ? null : color.fallback;
			}
			
CMYKColor(DOMString cssstring)
Parse a component value from the passed string. If this operation succeeds and the result is a valid ''device-cmyk()'' function, construct an instance of the {{CMYKColor}} class with attributes initialized to represent that color. Otherwise, if the operation succeeds and the result is any other type of valid CSS color, construct an instance of the {{CMYKColor}} class with attributes initialized to represent that color converted to CMYK, per [[#cmyk-rgb]]. Otherwise, throw an XXXError.
toRGB()
Defined as follows in ECMAScript:
			function() {
				const k_ = 1 - this.k;
				const r = 1 - Math.min(1, this.c * k_ + this.k);
				const g = 1 - Math.min(1, this.m * k_ + this.k);
				const b = 1 - Math.min(1, this.y * k_ + this.k);
				return new RGBColor(r, g, b, this.a);
			}
			
c
m
y
k
a
fallback
The {{c}}, {{m}}, {{y}}, {{k}}, {{a}}, and {{fallback}} attributes represent the red, blue, green, and alpha channels of the CMYK color that the CMYKColor instance represents. Note: The CMYKColor class's c, m, y, k, and a attributes have a normal range of 0 to 1. Its fallback attribute is always an RGBColor, or null (indicating it should convert itself to an RGBColor per [[#cmyk-rgb]]).
stringifiers
Must be initially set to the following ECMAScript object:
			{
				"cmyk": function(color) {
					const c = color.c;
					const m = color.m;
					const y = color.y;
					const k = color.k;
					const a = color.a;
					if(/* UA knows the output device's color profile */) {
						fallback = "" + (/* Equivalent RGBColor object */)
					} else {
						fallback = "" color.toRGB();
					}
					return "cmyk(" + [c,m,y,k,a,fallback].join(", ") + ")";
				}
			}
			
defaultStringifier
The defaultStringifier attribute must be initially set to "cmyk".

Extending the Color Classes

The color classes defined in this spec are specifically designed to be extensible by authors; their design makes it easy to both add new methods to colors, and to create new color classes entirely. The following sections give guidance on how to correctly extend the color classes, so that they behave correctly and in an unsurprising manner.

Adding New Color-Manipulating Methods

If a new method is truly unique to a particular color class, add it to the class's prototype, as normal. However, most color-manipulation methods are applicable to colors in general, regardless of the particular format the color may be expressed in. To add a method of this type, follow these steps:
  1. Define the method for {{RGBColor}}s, by adding it to {{RGBColor}}.prototype.
  2. Define a forwarding method for all other color classes, by calling CSSColor.forward("myMethodName") or CSSColor.forwardToSameClass("myMethodName"). Use forward() if the method returns a non-color value, such as a number, or if it purposely returns a color value of a particular type, like a conversion function. Use forwardToSameClass() if the method returns a color, and the precise type of color returned doesn't matter, as this will ensure it returns the same type of color as it was called on. (These default implementations are not very efficient, but they automatically work for all color classes, including ones you define or that a UA adds in the future.)
  3. Optionally, define a more efficient version of the method directly on the prototype of the color classes you're interested in.
For example, here's how you would add a lightness() method to all color classes, which computes the approximate perceptual lightness of a color:
		RGBColor.prototype.lightness = function() {
			return .2126 * Math.pow(this.r, 2.2) +
				.7152 * Math.pow(this.g, 2.2) +
				.0722 * Math.pow(this.b, 2.2);
		};

		CSSColor.forward("lightness");
		
Without any further effort, the lightness() method is now available for other color classes, like instances of {{CMYKColor}}.

Adding New Color Classes

Each color class is specialized to a particular representation of a color, to allow easy manipulationg of the color in the desired color space. For example, even though HSL colors are conceptually equivalent to RGB colors, it is still sometimes useful to be able to directly alter the hue angle of the color, which the {{HSLColor}} class allows. If, as an author, you wish to work with colors in your application in a color space that CSS does not directly support, you can easily add your own color class. Doing this correctly, however, requires some care. To work correctly with the rest of the color classes, a new color class must:
  1. Subclass {{CSSColor}}, such as via:
    MyNewColor.prototype = Object.create(CSSColor.prototype);
  2. Implement a toRGB() method, which can be called without any arguments and which returns an equivalent {{RGBColor}} instance. (Remember that the normal range of {{RGBColor}} components is between 0 and 1, not 0 and 255.)
  3. Implement a constructor that, when passed an {{RGBColor}} instance as its sole argument, will produce a color equivalent to the {{RGBColor}}. (Same disclaimer as previous item.)
For example, here is the bare minimum of code required to correctly implement an HSV color class:
		function HSVColor(...args) {
			if(args.length == 1 && args[0] instanceof RGBColor) {
				let {r, g, b, a} = args[0];
				r = ((r % 1) + 1) % 1;
				g = ((g % 1) + 1) % 1;
				b = ((b % 1) + 1) % 1;
				a = Math.min(1, Math.max(0, a));

				this.a = a;

				const max = Math.max(r, g, b);
				const min = Math.min(r, g, b);
				const chroma = max - min;
				if(chroma == 0) {
					this.h = 0;
				} else if(r == max) {
					this.h = (((g - b) / chroma) % 6) * 360;
				} else if(g == max) {
					this.h = ((((b - r) / chroma) + 2) % 6) * 360;
				} else {
					this.h = ((((r - g) / chroma) + 4) % 6) * 360;
				}

				this.v = max;

				if(chroma == 0) {
					this.s = 0;
				} else {
					this.s = chroma/this.v;
				}

				return this;
			}
			throw XXXError();
		}

		HSVColor.prototype = Object.create(CSSColor.prototype);

		HSVColor.prototype.toRGB = function() {
			const max = this.v * this.s;
			const h_ = ((this.h / 60 % 6) + 6) % 6;
			const mid = max * (1 - Math.abs((h_ % 2) - 1));

			if(h_ >= 0 && h_ < 1) {
				return new RGBColor(max, mid, 0);
			} else if(h_ >= 1 && h_ < 2) {
				return new RGBColor(mid, max, 0);
			} else if(h_ >= 2 && h_ < 3) {
				return new RGBColor(0, max, mid);
			} else if(h_ >= 3 && h_ < 4) {
				return new RGBColor(0, mid, max);
			} else if(h_ >= 4 && h_ < 5) {
				return new RGBColor(mid, 0, max);
			} else {
				return new RGBColor(max, 0, mid);
			}
		}
		
This allows you to create an HSVColor instance (indirectly, by first creating an RGBColor), convert it into any other color class (by automatically indirecting to RGBColor), pick up any methods defined for all color types (same), and even assign it directly to a CSS property (by stringifying into the ''rgba()'' syntax).
There are several additional methods you should define, however, to get maximum value and consistency with the CSS-defined color classes:
  • A constructor that builds the color with multiple arguments (typically the color's properties, in some order, with alpha optional and last).
  • A constructor that can take an object with the same properties as the class, or an instance of the class.
  • A constructor that can take *any* subclass of CSSColor and return an equivalent color in your class (probably by just calling toRGB() on it, which all built-in classes define and all custom classes should define in order to work correctly, and then converting it into your class).
  • A constructor that can take a string containing a CSS color value, and turn it into an instance of your class.
  • A toMyColor() method on {{RGBColor}}.prototype, that transforms any type of color into your color class.
  • If your class is very similar to an existing class, such that you can do a better/faster conversion between the existing class and yours directly rather than doing through {{RGBColor}} first, a toMyColor() method on TheOtherColor.prototype, and a toTheOtherColor() method on MyNewColor.prototype. Add cases to your constructor to handle these classes as well.
  • Custom stringifiers on the stringifiers object for one of the UA-defined color classes, if it's more accurate/efficient to convert your color class into one of the UA-defined types directly than it is to first convert it into an {{RGBColor}}. Note: It is not generally useful to define a special stringifier for your class itself; the main purpose of the stringifier is to allow the object to be used in a CSS property, and only the UA-defined color classes correspond to valid CSS syntaxes.
All of these apply to our example HSV color class, so let's see what it looks like with all of these additions:
		function HSVColor(...args) {
			const undefined = void 0;

			if(args.length == 1 && args[0] instanceof RGBColor) {
				/* RGBColor constructor */
				let {r, g, b, a} = rgbcolor;
				r = ((+r % 1) + 1) % 1;
				g = ((+g % 1) + 1) % 1;
				b = ((+b % 1) + 1) % 1;
				a = Math.min(1, Math.max(0, +a));

				this.a = a;

				const max = Math.max(r, g, b);
				const min = Math.min(r, g, b);
				const chroma = max - min;
				if(chroma == 0) {
					this.h = 0;
				} else if(r == max) {
					this.h = (((g - b) / chroma) % 6) * 360;
				} else if(g == max) {
					this.h = ((((b - r) / chroma) + 2) % 6) * 360;
				} else {
					this.h = ((((r - g) / chroma) + 4) % 6) * 360;
				}

				this.v = max;

				if(chroma == 0) {
					this.s = 0;
				} else {
					this.s = chroma/this.v;
				}
				return this;
			} else if(args.length == 1 && typeof args[0] == "string") {
				/* String-parsing constructor */
				return RGBColor.parse(args[0]).toHSV();
			} else if(args.length <= 1) {
				/* HSVA-like object constructor */
				const {h, s, v, a} = args[0];
				this.h = h === undefined ? 0 : +h;
				this.s = s === undefined ? 0 : +s;
				this.v = v === undefined ? 0 : +v;
				this.a = a === undefined ? 1 : +a;
				return this;
			} else if(args.length == 3 || args.length == 4) {
				/* Direct (h, s, v, a) arguments constructor */
				this.h = +args[0];
				this.s = +args[1];
				this.v = +args[2];
				this.a = args[3] === undefined ? 1 : +args[3];
				return this;
			}
			throw XXXError();
		}

		HSVColor.prototype = Object.create(CSSColor.prototype);

		HSVColor.prototype.asRGB = function() {
			const max = this.v * this.s;
			const h_ = ((this.h / 60 % 6) + 6) % 6;
			const mid = max * (1 - Math.abs((h_ % 2) - 1));

			if(h_ >= 0 && h_ < 1) {
				return new RGBColor(max, mid, 0);
			} else if(h_ >= 1 && h_ < 2) {
				return new RGBColor(mid, max, 0);
			} else if(h_ >= 2 && h_ < 3) {
				return new RGBColor(0, max, mid);
			} else if(h_ >= 3 && h_ < 4) {
				return new RGBColor(0, mid, max);
			} else if(h_ >= 4 && h_ < 5) {
				return new RGBColor(mid, 0, max);
			} else {
				return new RGBColor(max, 0, mid);
			}
		}

		RGBColor.prototype.asHSV = function(rgbcolor) {
			return new HSVColor(rgbcolor);
		}
		CSSColor.forward("asHSV");

		HSVColor.prototype.asHSL = function() {
			/* The default converter will first go to RGB,
			   then to HSL, which will lose precision in
			   the hue angle, making an ugly decimal. */
			let h = this.h;
			let a = this.a;

			let max = this.v;
			let chroma = this.v * this.s;
			let min = max - chroma;
			let l = (max + min) / 2;

			let s;
			if(chroma == 0) {
				s = 0;
			} else {
				s = chroma / (1 - Math.abs(2 * l - 1));
			}

			return new HSLColor(h, s, l, a);
		}

		HSLColor.prototype.asHSV = function() {
			/* Same as before; going HSL -> RGB -> HSV
			   would lose precision in the hue. */
				let h = this.h;
				let a = this.a;

				const templ = this.l * 2;
				const temps = this.s * (templ <= 1) ? templ : 2 - templ;
				const v = (templ + temps) / 2;

				let s;
				if(templ + temps == 0) {
					s = 0;
				} else {
					s = (2 * temps) / (templ + temps);
				}

				return new HSVColor(h, s, v, a);
		}

		HSVColor.stringifiers = {
			"hsl": function(hsv) {
				return hsv.toHSL().toString("hsl");
			}
			/* Don't define an "hsv" stringifier type,
			   because CSS won't understand it. */
		}
		
You now have a complete and functional HSV color class!

The CSSColor Object

The CSSColor object exists to provide a super-class for all color objects, to aid in type-testing and to provide default implementations for various methods. It is impossible to construct an instance of it. In addition, it provides two static methods: {{parse()}}, which takes a string containing a CSS color value and returns an appropriate color class representing that value; and {{forward()}}, which is a helper function for adding new methods to all color classes (see [[#api-adding-methods]] for more detail on using this function).
The most common use of this interface is to parse color strings into an appropriate color object. The following code sets color to a {{HexColor}} object:
var color = CSSColor.parse("#96c");
While this code sets color to an {{HSLColor}} object:
var color = CSSColor.parse("hsl(120deg, 100%, 50%)");
Note that this returns a color class matching the type of input provided to it. If a particular color class is desired, the class's constructor can be passed a string instead. For example, the following will set color to an {{RGBColor}} object, even though it uses the ''hsl()'' function:
var color = new RGBColor("hsl(120deg, 100%, 50%)")
	[Constructor()]
	interface CSSColor {
		RGBColor toRGB();

		static CSSColor parse(DOMString css, optional Element el);

		DOMString toString(optional ColorStringifierType type, any... args);

		static void forward(DOMString methodName);
		static void forwardToSameClass(DOMString methodName);

		/* Default, forwarded converters */
		HSLColor  toHSL();
		HexColor   toHex();
		CMYKColor  toCMYK();
		DOMString? toName();
	};
	
CSSColor()
Throw an XXXError. Note: CSSColor is not designed to be constructable; see the note at the top of this section.
toRGB()
Throw an XXXError. Note: All {{CSSColor}} subclasses have to implement this method themselves.
parse(DOMString css, Element el)
Issue: Should parse the string, returning the appropriate CSSColor subclass depending on how the color was written. E.g., return an HSLColor if you call CSSColor.parse("hsl(120deg, 100%, 50%");. The el argument is used to resolve "currentcolor" and any future colors we introduce that need to be resolved against an element. Throws if it can't parse a color out of the string, or if you give it "currentcolor" but no element to resolve against.
toString(optional {{ColorStringifierType}} type, any... args)
Defined as follows in ECMAScript:
			function(type, ...args) {
				var undefined = void 0;

				if(type !== undefined) {
					type = "" + type;
				} else if(this.constructor.defaultStringifier !== undefined) {
					type = "" + this.constructor.defaultStringifier;
				} else {
					type = "rgb";
				}

				if(/* type is not a {{ColorStringifierType}} value */) {
					throw XXXError("Invalid stringifier type: " + type);
				}

				if(this.constructor.stringifiers
					&& this.constructor.stringifiers[type]) {
					return this.constructor.stringifiers[type](this, ...args);
				} else {
					return RGBColor.stringifiers[type](this.toRGB(), ...args);
				}
			}
			
forward(DOMString methodName)
This function automates "forwarding" methods to a canonical definition in RGBColor, so that authors can add new methods to color objects without having to explicitly add variants of the method to every single color class. It adds a default version of a method to {{CSSColor}}.prototype (which will get picked up by all subclasses, unless explicitly overridden) that converts the color into an {{RGBColor}} and calls the method there. Note: For this to work correctly, author-defined color classes must have certain methods and behavior, as defined in [[#api-extending]]. If an author-defined class violates these expectations, defaulted methods might work incorrectly or throw errors. Defined as follows in ECMAScript:
			function(methodName) {
				methodName = "" + methodName;
				this.prototype[methodName] = function(...args) {
					return this.toRGB()[methodName](...args);
				}
			}
			
forwardToSameClass(DOMString methodName)
Similar to the {{forward()}} method, but used when the method returns a color object, and you'd like it to return the same type of color object as you called the method on.
It is always safe to use {{forward()}} on a method; if it returns a color, it will just be an RGBColor. For example, if you use CSSColor.forward("lighten"); to forward a lighten(%) method to all color types, when you call c.lighten(.2) on an {{HSLColor}} object, you'll get back the lightened color as an {{RGBColor}} object. However, you probably want lighten() called on an {{HSLColor}} object to return another {{HSLColor}} object. Using CSSColor.forwardToSameClass("lighten"); will make this happen automatically. Obviously, if a method does not return a color you should not use {{forwardToSameClass()}}; the results will probably not be what you want, as it will attempt to convert whatever the return value is into an instance of the color class.
Defined as follows in ECMAScript:
			function(methodName) {
				methodName = "" + methodName;
				this.prototype[methodName] = function(...args) {
					let result = this.toRGB()[methodName](...args);
					return this.constructor(result);
				}
			}
			
toHSL()
toHex()
toCMYK()
toName()
All of these methods delegate to the appropriate methods on {{RGBColor}}, exactly as if created by calling CSSColor.forward("asHSL"), etc.

Appendix A: Deprecated CSS System Colors

Earlier versions of CSS defined several additional named color keywords, the <deprecated-system-color>s, which were meant to take their value from operating system themes. These color names have been deprecated, however, as they are insufficient for their original purpose (making website elements look like their native OS counterparts), and represent a security risk, as it makes it easier for a webpage to "spoof" a native OS dialog. User agents must support these keywords, but should map them to "default" values, not based on the user's OS settings (for example, mapping all the "background" colors to white and "foreground" colors to black). Authors must not use these keywords.
ActiveBorder
Active window border.
ActiveCaption
Active window caption.
AppWorkspace
Background color of multiple document interface.
Background
Desktop background.
ButtonFace
The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.
ButtonHighlight
The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.
ButtonShadow
The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.
ButtonText
Text on push buttons.
CaptionText
Text in caption, size box, and scrollbar arrow box.
GrayText
Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.
Highlight
Item(s) selected in a control.
HighlightText
Text of item(s) selected in a control.
InactiveBorder
Inactive window border.
InactiveCaption
Inactive window caption.
InactiveCaptionText
Color of text in an inactive caption.
InfoBackground
Background color for tooltip controls.
InfoText
Text color for tooltip controls.
Menu
Menu background.
MenuText
Text in menus.
Scrollbar
Scroll bar gray area.
ThreeDDarkShadow
The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
ThreeDFace
The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
ThreeDHighlight
The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
ThreeDLightShadow
The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
ThreeDShadow
The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.
Window
Window background.
WindowFrame
Window frame.
WindowText
Text in windows.

Acknowledgments

Thanks to Brad Pettit both for writing up color-profiles, and for implementing it. Thanks to Steven Pemberton for his write up on HSL colors. Thanks especially to the feedback from Marc Attinasi, Bert Bos, Joe Clark, fantasai, Patrick Garies, Tony Graham, Ian Hickson, Susan Lesch, Alex LeDonne, Cameron McCormack, Krzysztof Maczyński, Chris Moschini, Chris Murphy, Christoph Päper, David Perrell, Jacob Refstrup, Dave Singer, Jonathan Stanley, Andrew Thompson, Russ Weakley, Etan Wexler, David Woolley, Boris Zbarsky, Steve Zilles, the XSL FO subgroup of the XSL working group, and all the rest of the www-style community. And thanks to Chris Lilley for being the resident CSS Color expert.

Changes

Changes from Colors 3

  1. ''rgb()'' and ''rgba()'' functions now accept <> rather than <>.
  2. ''hsl()'' and ''hsla()'' functions now accept <> as well as <> for hues.
  3. All uses of <> now accept <> as well as <>.
  4. 4 and 8-digit hex colors have been added, to specify transparency.
  5. The 'color-correction' property has been pulled in from the unpublished Color Correction proposal.
Several brand new features have been added:
  1. ''gray()'' function, for specifying grays compactly. (And maybe allowing specification via luminance.)
  2. ''hwb()'' function, for specifying colors in the HWB notation.
  3. ''color()'' function, for manipulating colors.
  4. ''device-cmyk()'' function, for specifying colors in an output-device-specific CMYK colorspace.
  5. Addition of named color ''rebeccapurple''.