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;
    }

}

enter image description here

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.