Important

This slide deck was created for use in a controlled environment, during a talk. It works best with Firefox 28+ at 1280x720 or 1024x768 with enabled blending modes in about:config. It WILL NOT work properly in other browsers. The demos were live coded, so these slides are a bit pointless if you never watched the talk.

The Chroma Zone

Engineering color on the Web

By Lea Verou (@LeaVerou)

Picture of me
about:lea

I make stuff

More: lea.verou.me/projects

CSS WG Invited Expert

O’Reilly author (2014)

MIT

How color works on screen

RGB: Additive color

Apple iPad 2, 1024×768, 9.7″
Apple iPad Retina Display (2012), 2048×1536, 9.7″
Apple iPhone 4S, 960×640, 3.5″
ASUS Eee Pad Transformer Prime, 1280×800, 10.1″
8 bits
8 bits
8 bits

= 24 bits per pixel

= 3 bytes per pixel
(in uncompressed images)

256³ possible colors

Anti-aliasing

Colour & Code

Colors in HTML 3.2

#ff005a

= Red: 255
Green: 0
Blue: 90

So, 255 —> FF and 0 —> 00. What about 90? Hmmm… 90 = 5×16+10 —> 5A, so #FF005A!

Decimal to hex:

hex = decimal.toString(16);

Hex to decimal:

decimal = parseInt(hex, 16); /* faster */
decimal = +("0x" + hex);
red lime blue aqua fuchsia yellow green maroon navy olive purple teal white silver gray black

HTML 3.2 colors

  • #rrggbb Confusing and cryptic >:(
  • Color names Mostly useless :'(

Colors in CSS 1 - 2.1

orange

CSS 1 - 2.1 colors

  • rgb() Better, but still confusing :/
  • #rgb Shorter, but still confusing :/
  • orange named color Not very useful :/

Colors in CSS Color Level 3

A simple JS “class” for RGB colors

function Color(red, green, blue, alpha) {
	if (Array.isArray(arguments[0])) {
		var arr = arguments[0];
		this.rgb = arr.slice(0,3);
		alpha = arr[3];
	}
	else {
		this.red = red || 0;
		this.green = green || 0;
		this.blue = blue || 0;
	}
	
	this.alpha = alpha >= 0? alpha : 1;
};

this.rgb getter/setter

Color.prototype = {
	get rgb () {
		return [this.red, this.green, this.blue];
	},
	
	set rgb(arr) {
		this.red = arr[0];
		this.green = arr[1];
		this.blue = arr[2];
	},
	/* ... */
}
var magenta = new Color(255, 0, 127);
var magenta = new Color(255, 0, 127, 1);
var magenta = new Color([255, 0, 127, 1]);
var magenta = new Color();
magenta.rgb = [255, 0, 127];
var magenta = new Color();
magenta.red = 255;
magenta.blue = 127;

Example method: Invert

Color.prototype.invert = function () {
	this.rgb = this.rgb.map(function(c) {
		return 255 - c;
	});
};
hsl(60, 100%, 50%) hsl(240, 100%, 50%)
hsl(0, 0%, 0%) hsl(0, 0%, 50%) hsl(0, 0%, 100%)

A color space has

Perceptual uniformity

when the perceptual similarity of two colors
is measured by the distance between them.

RGB is not perceptually uniform.

HSL isn’t either, as it’s just a transformation of RGB.

rgb(255, 128, 255) rgb(255, 0, 255) rgb(128, 0, 255)

Lightness != luminance

el.style.background = color;
el.style.color = color.lightness > 50?
                   'black' : 'white';
get lightness () {
	var max = Math.max.apply(Math, this.rgb),
	    min = Math.min.apply(Math, this.rgb);
		
	return Math.round((min + max)/2.55/2);
}

Relative luminance

get luminance () { // Formula from WCAG 2.0
	var rgb = this.rgb.map(function(c){
		c /= 255; // to 0-1 range
		return c < .03928 ? 
		         c / 12.92 : 
		         Math.pow((c+.055)/1.055, 2.4);
	});
	
	return 21.26 * rgb[0] + // red
	       71.52 * rgb[1] + // green
	        7.22 * rgb[2];  // blue
}
0%
vs
0%

Color contrast

Color.prototype.contrast = function(color) {
	var l1 = this.luminance;
	var l2 = color.luminance;
	var ret = (l1 + .05) / (l2 + .05);
	
	return ret < 1? 1 / ret : ret;
};

Alpha blending

Alpha blending

Color.prototype.over = function(dest) {
	var α = dest.alpha * (1-this.alpha);
	
	this.rgb = this.rgb.map(function(c, i){
		return this.rgb[i] * this.alpha +
		       dest.rgb[i] * α;
	}, this);
	
	this.alpha += α;
};

Color interpolation

Color.prototype.mix = function (color, weight) {
	this.rgba = this.rgba.map(function(c,i){
		return  this.rgba[i] * weight +
		       color.rgba[i] * (1-weight);
	});
};

In SASS:

background: mix($color1, $color2, 50%);

Color interpolation

Premultiplied

rgb(255, 255, 255)
rgba(255, 255, 255, .75)
rgba(255, 255, 255, .5)
rgba(255, 255, 255, .25)
rgba(255, 255, 255, 0)

Non-premultiplied

rgb(255, 255, 255)
rgba(192, 192, 192, .75)
rgba(128, 128, 128, .5)
rgba(64, 64, 64, .25)
rgba(0, 0, 0, 0)

Named colors distribution

blanchedalmond cornflowerblue darkgoldenrod darkolivegreen lavenderblush lightgoldenrodyellow lightseagreen lightslategray lightsteelblue mediumaquamarine mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred palegoldenrod paleturquoise palevioletred
blanchedalmond chartreuse cornflowerblue goldenrod dodgerblue gainsboro lemonchiffon linen moccasin oldlace olivedrab orchid
indianred navajowhite peru
gray darkgray
Some text

CSS Color Level 3 additions

  • hsl() Better, but not great :/
  • rgba() and hsla() Badly needed :)
  • MOAR Color names Mostly useless :'(
  • currentColor Cool, but limited :)

Colors in CSS Color Level 4

gray() with SASS

@function gray($l, $alpha: 1) {
  @return rgba($l, $l, $l, $alpha);
}

background: gray(50%); /* → #7f7f7f */
background: gray(255, .2); 
       /* → rgba(255, 255, 255, 0.2); */

color() adjusters

red green blue alpha rgb
hue h
saturation s
lightness l
whiteness w blackness b
tint shade blend blenda contrast

tint & shade in SASS

@function tint($color, $amount) {
	@return mix(white, $color, $amount);
}
@function shade($color, $amount) {
	@return mix(black, $color, $amount);
}

background: tint(red, 10%); /* → #ff1919 */
background: shade(red, 10%); /* → #e50000 */

Note: tint() & shade() are already included in Bourbon

hwb(335, 90%, 90%)

CSS Color Level 4 (Tentative)

  • gray()
  • #rgba and #rrggbbaa
  • color()
  • hwb()
  • named hues and <angle> in hsl()
  • rgb() and hsl() will accept alpha values

Parse any (supported) CSS color

Color.fromString = function(str) {
	var dummy = document.createElement('div'), rgba;
	dummy.style.color = str;
	
	if (dummy.style.color) {
		document.head.appendChild(dummy);
		rgba = getComputedStyle(dummy).color;
		document.head.removeChild(dummy);
		
		rgba = rgba.match(/[\d.]+(?=,|\))/g) || [];
		rgba = rgba.map(function(c){ return +c });
		return new Color(rgba);
	}
	return null; // or throw TypeError()
};

Other color spaces

CMY: Subtractive color

CMY: Theory and reality

Adding the Key in CMYK

Color gamut

ICC color profiles