Given a background color, how to get a foreground color that makes it readable on that background color?
The safest bet is to conform with the World Wide Web Consortium’s (W3C) Web Content Accessibility Guidelines 2.0, which specify a brightness contrast ratio of 4.5:1 for regular text (12 pt or smaller), and 3.0:1 for large text. Contrast ratio is defined as:
[Y(b) + 0.05] / [Y(d) + 0.05]
Where Y(b) is the brightness (luminance) of the brighter color and Y(d) is the brightness of the darker color.
You calculate luminance Y by first converting each of the color’s RGB values to gamma adjusted normalize rgb values:
- r = (R/255)^2.2
- b = (B/255)^2.2
- g = (G/255)^2.2
Then combine them using sRGB constants (rounded to 4 places):
Y = 0.2126*r + 0.7151*g + 0.0721*b
This gives white a Y of 1 and black a Y of 0, so the maximum possible contrast is (1.05/ 0.05) = 21 (within rounding error).
Or let JuicyStudio do the math for you.
This calculation assumes a standard-performing monitor in a relatively dimly lit room (or a room that the user can make dim if she or he has to). That makes it adequate for home or office use, but I don’t know if it’s adequate for mobile apps or other devices that are used outdoors.
Here's one I did in both Java and Javascript. It's loosely based off this one in javascript. I took the Luminance formula from here. The sweet-spot of the threshold from my eye was about 140.
Java version:
public class Color {
private float CalculateLuminance(ArrayList<Integer> rgb){
return (float) (0.2126*rgb.get(0) + 0.7152*rgb.get(1) + 0.0722*rgb.get(2));
}
private ArrayList<Integer> HexToRBG(String colorStr) {
ArrayList<Integer> rbg = new ArrayList<Integer>();
rbg.add(Integer.valueOf( colorStr.substring( 1, 3 ), 16 ));
rbg.add(Integer.valueOf( colorStr.substring( 3, 5 ), 16 ));
rbg.add(Integer.valueOf( colorStr.substring( 5, 7 ), 16 ));
return rbg;
}
public String getInverseBW(String hex_color) {
float luminance = this.CalculateLuminance(this.HexToRBG(hex_color));
String inverse = (luminance < 140) ? "#fff" : "#000";
return inverse;
}
}
Javascript version:
Here's the same thing in javascript for your front-end things. RGB conversion taken from here:
hex_to_rgb: function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
},
hex_inverse_bw: function(hex) {
rgb = this.hex_to_rgb(hex);
luminance = (0.2126*rgb["r"] + 0.7152*rgb["g"] + 0.0722*rgb["b"]);
return (luminance < 140) ? "#ffffff": "#000000";
}
- Calculate the lightness (see HSL)
- If the lightness is less than 50%, use white. Otherwise, use black.
Using colors as foreground color is difficult, because you have to take contrast and color blindness into account.