On a black background, the high-chroma colors yellow (#FFFF00), green (#00FF00), cyan (#00FFFF), and magenta (#FF00FF) provide the best contrast. While white text on a black background provides very high value contrast, it is less readable and causes greater eye fatigue than black text on a white background.
White backgrounds: Simple and classic, black text on a white background provides the highest readability ratio. Blues and grays also provide the right contrast and thus work well on white backgrounds. However, avoid using white backgrounds for an entire webpage, which can strain the eyes.
To make your content accessible, you should have high contrast between all text and its background color. If your text is dark, your background should be light, and vice versa. Black text on a white background is recommended for paragraphs of text.
Building on my answer to a similar question.
You need to break the hex code into 3 pieces to get the individual red, green, and blue intensities. Each 2 digits of the code represent a value in hexadecimal (base-16) notation. I won't get into the details of the conversion here, they're easy to look up.
Once you have the intensities for the individual colors, you can determine the overall intensity of the color and choose the corresponding text.
if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
The threshold of 186 is based on theory, but can be adjusted to taste. Based on the comments below a threshold of 150 may work better for you.
The formula given for contrast in the W3C Recommendations is (L1 + 0.05) / (L2 + 0.05)
, where L1
is the luminance of the lightest color and L2
is the luminance of the darkest on a scale of 0.0-1.0. The luminance of black is 0.0 and white is 1.0, so substituting those values lets you determine the one with the highest contrast. If the contrast for black is greater than the contrast for white, use black, otherwise use white. Given the luminance of the color you're testing as L
the test becomes:
if (L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05) use #000000 else use #ffffff
This simplifies down algebraically to:
if L > sqrt(1.05 * 0.05) - 0.05
Or approximately:
if L > 0.179 use #000000 else use #ffffff
The only thing left is to compute L
. That formula is also given in the guidelines and it looks like the conversion from sRGB to linear RGB followed by the ITU-R recommendation BT.709 for luminance.
for each c in r,g,b:
c = c / 255.0
if c <= 0.03928 then c = c/12.92 else c = ((c+0.055)/1.055) ^ 2.4
L = 0.2126 * r + 0.7152 * g + 0.0722 * b
The threshold of 0.179 should not be changed since it is tied to the W3C guidelines. If you find the results not to your liking, try the simpler formula above.
I take no credit for this code as it's not mine, but I leave it here for others to find quickly in the future:
Based on Mark Ransoms answer, here's a code snippet for the simple version:
function pickTextColorBasedOnBgColorSimple(bgColor, lightColor, darkColor) {
var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor;
var r = parseInt(color.substring(0, 2), 16); // hexToR
var g = parseInt(color.substring(2, 4), 16); // hexToG
var b = parseInt(color.substring(4, 6), 16); // hexToB
return (((r * 0.299) + (g * 0.587) + (b * 0.114)) > 186) ?
darkColor : lightColor;
}
and here's the code snippet for the advanced version:
function pickTextColorBasedOnBgColorAdvanced(bgColor, lightColor, darkColor) {
var color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor;
var r = parseInt(color.substring(0, 2), 16); // hexToR
var g = parseInt(color.substring(2, 4), 16); // hexToG
var b = parseInt(color.substring(4, 6), 16); // hexToB
var uicolors = [r / 255, g / 255, b / 255];
var c = uicolors.map((col) => {
if (col <= 0.03928) {
return col / 12.92;
}
return Math.pow((col + 0.055) / 1.055, 2.4);
});
var L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
return (L > 0.179) ? darkColor : lightColor;
}
To use them just call:
var color = '#EEACAE' // this can be any color
pickTextColorBasedOnBgColorSimple(color, '#FFFFFF', '#000000');
Also, thanks Alx
and chetstone
.
How about this (JavaScript code)?
/**
* Get color (black/white) depending on bgColor so it would be clearly seen.
* @param bgColor
* @returns {string}
*/
getColorByBgColor(bgColor) {
if (!bgColor) { return ''; }
return (parseInt(bgColor.replace('#', ''), 16) > 0xffffff / 2) ? '#000' : '#fff';
}
Additionally to the arithmetic solutions, it's also possible to use an AI neural network. The advantage is that you can tailor it to your own taste and needs (ie. off-white text on bright saturated reds looks good and is just as readable as black).
Here's a neat Javascript demo that illustrates the concept. You can also generate your own JS formula right in the demo.
https://harthur.github.io/brain/
Below are some charts that helped me get my mind around the problem. In the first chart, lightness is a constant 128, while hue and saturation vary. In the second chart, saturation is a constant 255, while hue and lightness vary.
Here is my solution in Java for Android:
// Put this method in whichever class you deem appropriate
// static or non-static, up to you.
public static int getContrastColor(int colorIntValue) {
int red = Color.red(colorIntValue);
int green = Color.green(colorIntValue);
int blue = Color.blue(colorIntValue);
double lum = (((0.299 * red) + ((0.587 * green) + (0.114 * blue))));
return lum > 186 ? 0xFF000000 : 0xFFFFFFFF;
}
// Usage
// If Color is represented as HEX code:
String colorHex = "#484588";
int color = Color.parseColor(colorHex);
// Or if color is Integer:
int color = 0xFF484588;
// Get White (0xFFFFFFFF) or Black (0xFF000000)
int contrastColor = WhateverClass.getContrastColor(color);
Based on the answer of @MarkRansom, I created a PHP script you can find here:
function calcC($c) {
if ($c <= 0.03928) {
return $c / 12.92;
}
else {
return pow(($c + 0.055) / 1.055, 2.4);
}
}
function cutHex($h) {
return ($h[0] == "#") ? substr($h, 1, 7) : $h;
}
function hexToR($h) {
return hexdec(substr(cutHex($h), 0, 2));
}
function hexToG($h) {
return hexdec(substr(cutHex($h), 2, 2)); // Edited
}
function hexToB($h) {
return hexdec(substr(cutHex($h), 4, 2)); // Edited
}
function computeTextColor($color) {
$r = hexToR($color);
$g = hexToG($color);
$b = hexToB($color);
$uicolors = [$r / 255, $g / 255, $b / 255];
$c = array_map("calcC", $uicolors);
$l = 0.2126 * $c[0] + 0.7152 * $c[1] + 0.0722 * $c[2];
return ($l > 0.179) ? '#000000' : '#ffffff';
}
This is a swift version of Mark Ransom's answer as an extension of UIColor
extension UIColor {
// Get the rgba components in CGFloat
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return (red, green, blue, alpha)
}
/// Return the better contrasting color, white or black
func contrastColor() -> UIColor {
let rgbArray = [rgba.red, rgba.green, rgba.blue]
let luminanceArray = rgbArray.map({ value -> (CGFloat) in
if value < 0.03928 {
return (value / 12.92)
} else {
return (pow( (value + 0.55) / 1.055, 2.4) )
}
})
let luminance = 0.2126 * luminanceArray[0] +
0.7152 * luminanceArray[1] +
0.0722 * luminanceArray[2]
return luminance > 0.179 ? UIColor.black : UIColor.white
} }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With