1 of 48

Color Suggestions

in Accessibility Developer Tools

Alice Boxhall / @sundress

2 of 48

Color

3 of 48

Color contrast

contrast ratio

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter of the colors, and
  • L2 is the relative luminance of the darker of the colors.

4 of 48

Color contrast

axs.color.calculateContrastRatio = function(fgColor, bgColor) {var fgLuminance = axs.color.calculateLuminance(fgColor);var bgLuminance = axs.color.calculateLuminance(bgColor);var contrastRatio =

(Math.max(fgLuminance, bgLuminance) + 0.05) /(Math.min(fgLuminance, bgLuminance) + 0.05);return contrastRatio;}

contrast ratio

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter of the colors, and
  • L2 is the relative luminance of the darker of the colors.

5 of 48

Color contrast

axs.color.calculateContrastRatio = function(fgColor, bgColor) {var fgLuminance = axs.color.calculateLuminance(fgColor);var bgLuminance = axs.color.calculateLuminance(bgColor);var contrastRatio =

(Math.max(fgLuminance, bgLuminance) + 0.05) /(Math.min(fgLuminance, bgLuminance) + 0.05);return contrastRatio;}

contrast ratio

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter of the colors, and
  • L2 is the relative luminance of the darker of the colors.

6 of 48

Luminance

relative luminance

the relative brightness of any point in a colorspace, normalized to 0 for darkest black and 1 for lightest white

Note 1: For the sRGB colorspace, the relative luminance of a color is defined as

L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

  • if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
  • if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
  • if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4

and RsRGB, GsRGB, and BsRGB are defined as:

  • RsRGB = R8bit/255
  • GsRGB = G8bit/255
  • BsRGB = B8bit/255

7 of 48

Color representation: RGB

Green

Blue

Red

rgb(255 , 0, 0)

rgb(0, 255, 0)

rgb(0, 0, 255)

8 of 48

Luminance

relative luminance

the relative brightness of any point in a colorspace, normalized to 0 for darkest black and 1 for lightest white

Note 1: For the sRGB colorspace, the relative luminance of a color is defined as

L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

  • if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
  • if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
  • if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4

and RsRGB, GsRGB, and BsRGB are defined as:

  • RsRGB = R8bit/255
  • GsRGB = G8bit/255
  • BsRGB = B8bit/255

9 of 48

Luminance

relative luminance

the relative brightness of any point in a colorspace, normalized to 0 for darkest black and 1 for lightest white

Note 1: For the sRGB colorspace, the relative luminance of a color is defined as

L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

  • if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
  • if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
  • if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4

and RsRGB, GsRGB, and BsRGB are defined as:

  • RsRGB = R8bit/255
  • GsRGB = G8bit/255
  • BsRGB = B8bit/255

Gamma compression

10 of 48

Luminance

axs.color.calculateLuminance = function(color) {var rSRGB = color.red / 255;var gSRGB = color.green / 255;var bSRGB = color.blue / 255;var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);return 0.2126 * r + 0.7152 * g + 0.0722 * b;};

11 of 48

12 of 48

Color contrast

axs.color.calculateContrastRatio = function(fgColor, bgColor) {var fgLuminance = axs.color.calculateLuminance(fgColor);var bgLuminance = axs.color.calculateLuminance(bgColor);var contrastRatio =

(Math.max(fgLuminance, bgLuminance) + 0.05) /(Math.min(fgLuminance, bgLuminance) + 0.05);return contrastRatio;}

contrast ratio

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter of the colors, and
  • L2 is the relative luminance of the darker of the colors.

13 of 48

14 of 48

Those color suggestions...

15 of 48

Color representation: RGB

Green

Blue

Red

rgb(255 , 0, 0)

rgb(0, 255, 0)

rgb(0, 0, 255)

16 of 48

Color representation: RGB

Green

Blue

Red

rgb(255 , 0, 0)

rgb(0, 255, 0)

rgb(0, 0, 255)

rgb(0, 0, 0)

Black

luminance = 0

rgb(255, 255, 255)

White

luminance = 1

17 of 48

Color representation: HSV

18 of 48

I ask for help

"My best guess so far is to convert C2 to HSV, change S by 1 (greater or lesser depending on whether the foreground is lighter or darker than the background), convert back, check contrast ratio, repeat until contrast ratio >= 4.5."

19 of 48

I get the best response ever

"I'd say the algorithm is pretty sound, but think of the adjustment _vector_ in 3-space (i.e. if Saturation is the desirable increment, consider you may be at a saturation limit so try adding a saturation "vector" but be prepared for it to clip or project onto the manifold of your 3-space .... also, if you're maxing out the desired vector, you may have to subtract the opposing vector from the 2nd color).

Also, L*ab is a fairly low-cost 3-space with (coarse) perceptual uniformity (IPT would be a better choice ... but they you're down in the weeds & really, consider 8bit RGB triples, it's of negligible benefit to extend that far).

In short, any invertible space is OK for color transforms ... just choose the space to make your calculations simpler/flexible. You can get away with affine transforms most of the time if your space is well chosen for the task."

20 of 48

Uhhhh...

By Ruby Wang (Own work) [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons

21 of 48

We Can Do It!

22 of 48

Desired luminance

C = (L1 + 0.05) / (L2 + 0.05)

L = C(L2 + 0.05) - 0.05

23 of 48

Color representation: RGB

Green

Blue

Red

rgb(255 , 0, 0)

rgb(0, 255, 0)

rgb(0, 0, 255)

rgb(0, 0, 0)

Black

luminance = 0

rgb(255, 255, 255)

White

luminance = 1

24 of 48

Luminance...

L = 0.2126 * R + 0.7152 * G + 0.0722 * B

25 of 48

Luminance...

L = 0.2126 * R + 0.7152 * G + 0.0722 * B

(Formula taken from [sRGB] and [IEC-4WD]).

26 of 48

Luminance...

L = 0.2126 * R + 0.7152 * G + 0.0722 * B

(Formula taken from [sRGB] and [IEC-4WD]).

IEC-4WD

IEC/4WD 61966-2-1: Colour Measurement and Management in Multimedia Systems and Equipment - Part 2.1: Default Colour Space - sRGB. May 5, 1998.

sRGB

"A Standard Default Color Space for the Internet - sRGB," M. Stokes, M. Anderson, S. Chandrasekar, R. Motta, eds., Version 1.10, November 5, 1996. A copy of this paper is available at http://www.w3.org/Graphics/Color/sRGB.html.

27 of 48

28 of 48

By Ruby Wang (Own work) [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons

29 of 48

Luminance...

L = 0.2126 * R + 0.7152 * G + 0.0722 * B

30 of 48

Luminance...

31 of 48

Luminance...

32 of 48

Luminance...

33 of 48

Luminance...

34 of 48

Luminance...

35 of 48

YCbCr

36 of 48

Color representations: a chart

37 of 48

RGB → YCbCr

38 of 48

RGB → YCbCr

39 of 48

RGB → YCbCr

axs.color.toYCbCr = function(color) {

var rSRGB = color.red / 255;

var gSRGB = color.green / 255;

var bSRGB = color.blue / 255;

var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);

var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);

var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);

return new axs.color.YCbCr(axs.color.multiplyMatrixVector(axs.color.YCC_MATRIX,

[r, g, b]));

};

40 of 48

Using YCbCr to pick a new color

Background color

Foreground color

41 of 48

Using YCbCr to pick a new color

Background color

Foreground color

Better foreground color

42 of 48

Using YCbCr to pick a new color

Background color

Foreground color

Better foreground color

var translatedColor = [luma,

ycc.Cb,

ycc.Cr];

return axs.color.fromYCbCrArray(

translatedColor);

43 of 48

Using YCbCr to pick a new color

Background color

Foreground color

???

44 of 48

Using YCbCr to pick a new color

Background color

Foreground color

Better foreground color

45 of 48

Using YCbCr to pick a new color

Foreground color

Background color

46 of 48

Using YCbCr to pick a new color

Foreground color

Background color

Better background color

47 of 48

So now you know.

48 of 48

Thank you!

Alice Boxhall

@sundress