Calculating Color Contrast
27 Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
Robin Sonefors
Eric Wieser
Don’t be silly, this doesn’t work:
function getContrast50(hexcolor){ return (parseInt(hexcolor, 16) > 0xffffff/2) ? ‘black’:‘white’; }That’ll say a background color of #010000 should have black text, and a background color of #7FFFFF should have
white text. That’s ludicrous!
Michael Kaply
Brian,
What is your opinion of using luminance for this computation?
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
if luminance < 110 light, else dark (might be backwards)
This is how Firefox determines whether a given textcolor in a Persona should have a light or dark text shadow.
Gary
This is awesome! Thanks Brian. I’ll definitely be playing with the PHP/Javascript in order to better select when to use black or white text.
Drew McLellan
Eric – have you tried it?
Eric Wieser
Ok, it looks like I made a mistake reading that. #010000 does give the sensible choice of white.
However, I still feel that treating the entire color as one number is inherently wrong. Provided I keep my red component below 0×80, the text color will ALWAYS be white. That seems like a bad choice.
What I assume the author meant to do was to take the average of the components, and see if they were more than 50%. So:
return (r + g + b) / 3 > 0.5 ? ‘black’ : ‘white’;
J. Ryan Stinnett
Definitely an interesting idea! I like the results the YIQ algorithm provides.
Also, this is a bit of a nitpick, but at least according to Wikipedia, the blue value should be multiplied by 114, not 144.
Danny Cohen
Very much liked this article. I posted some jQuery code for using it to style tag links: http://dco1.tumblr.com/post/2501743028/24-ways-calculating-color-contrast
Adrian
useful article, thank you Brian
Rory Parle
The second method looks very useful, but I have reservations about the 50% method. Because the red portion of the color is the most significant two digits when you treat it as a number, that is effectively the only component that has any impact on the decision. It’s equivalent to using the other method but weighting red by a factor of 256 relative to green and 65,000 relative to blue.
Drew McLellan
Eric – that sounds like a sensible way to improve that algorithm. That doesn’t make the original ludicrous, as you put it. It’s basic and has room for improvement – the article already explores that.
Brian LePore
This is brilliant.
As a reminder to all (though I doubt 24ways readers need this reminder), always explicitly define your background color and don’t assume everyone uses white as a default. I can’t tell you the number of Company’s that I’ve seen this from. Sites like ebay, chase, Google, etc. are usually good about their home pages and certain other pages, but there are many other pages where this is forgotten about. Techniques like this in the article would fall apart if the user didn’t have the white background you assumed they’d just have.
Christoph Freyer
It’s really a great and useful article. Thank you Brian.
Kemal Delali?
Great article Brian!
I needed the knowledge from this article few years ago as there was a lot to explore and learn about colours before wrapping up the yiq technique.
@Drew thanks for the demo page
Henk
Great article Brian. Too bad this was the last post of 2010.
Thanks to all for the great tips this year.
Nicolas Chevallier
Clever and simple way to resolve a common color problem, Thanks !
Dany Stastka
Hy
I found the article very interesting and so I create spontaneously a C# function. It may be any value between
#000000 and #ffffff calculate.
Testing: http://stastka.ch/tools/color/
Brian Suda
J. RYAN STINNETT, you are right. It seems to be a typo that made it pretty far. I have updated the text so the function is 114 not 144. Thanks for that catch, with enough eyeballs all bugs are shallow.
Mattijs Bliek
This could come in handy sometime, nice article.
I do however disagree with the ‘…neither of which is unreadable.’ remark regarding choosing between the 50% or YIQ method.
As Drew’s test page clearly shows, white text on—for instance—#00FFFF hurts your eyes and isn’t readable at all. The YIQ method seems like the way to go in my opinion, it produces far more readable results in all the tests appearing in this article.
Brian Suda
Mike Kaply, there are a few equations for contrast with luminance, this is the W3C definition, which is very similar
http://www.w3.org/TR/2006/WD-WCAG20-20060427/appendixA.html#luminosity-contrastdef
They also have a more complex equation which I wanted to avoid in this quick article.
http://www.w3.org/WAI/GL/WCAG20/WD-WCAG20-TECHS/G145.html
Testing the contrast by luminance is yet another tool to have in your tool box. I don’t think it really matters which equation you choose, there will always be issues with edge-cases, it is more important that you are using something.
Alan Hogan
The 50% algorithm is incorrect and, as other commentors note, basically only considers red.
The test colors are flawed, too; they are all completely saturated (or exactly grey). This means that even an incredibly naïve algorithm like the 50% one will always have contrast simply because both white and black always contrast against saturated colors — and because when r = g = b (grey) then it doesn’t matter if red is over-represented.
I really feel the article needs updated to reflect this. It’s that big a deal, and I think 24 Ways commands enough respect that readers expect/deserve a correction.
Aaron Russell
I’ve just read this technique by Brian (have a bit of catching up to do on the old RSS feeds) and decided to implement it in Sass/Compass:
https://gist.github.com/781017
The end result:
http://cl.ly/42Kz
For a quick 5 minute hack, this may come in very handy indeed. Thanks.
Paw
Hey Brian, thanks for sharing this.. just what i wanted, and wupti.. i got it :)
Curt Morehouse
Very cool and useful! I’ve implemented it on my mobile app for ad serving. Now if we could get contrasting colors instead of just black or white….
Mark Pedersen
Thanks, much easier to determine than having to load up Photoshop or similar tool.
Ryan Whittaker
I was thinking about this, but didn’t know exactly how to do the search query on google, calculating color contrast was a bit off-piste, but still, got me what I wanted. Still a relevant post even so long after submission!
Matt L.
Very helpful! Well done.
Wanted to share that the parameters in the substring methods inside the getContrastYIQ function are incorrect.
The indexes should be (0,2) (2,4) (4,6) not (0,2) (2,2) (4,2)
As someone who’s primarily a programmer, and not really a designer, I, like Eric, at first considered the first algorithm revolting. It is indeed flawed, and figuring out which colors would be pathological to the algorithm is not a particularly challenging endeavor.
However, I realized it will probably work alright, and in case there are more readers like myself and Eric, I will provide an explanation that at least I find more satisfying than the one given in the article:
Most video formats are still encoded in black and white, with added color channels. While this was originally done for backwards compatibility reasons, this also turns out to compress really well, since it turns out that the red, green and blue channels are very often quite similar: by separating the channels in a YIQ or a YUV picture, the luma channel will likely be the only one with a lot of information. By separating the channels in a RGB picture, all of the channels will appear to contain pretty much the same shapes. This must mean that most things are “somewhat greyish”: the channels in RGB are rarely that far apart.
Exploiting this, as the 50% method appears to do, by assuming that the green and blue channel will be similar to the red will thus often give you somewhat sane results.
This assumption does break sometimes, particularly for highly saturated colors such as #ff0000, but for those, either black or white text works.
Sure, #7fffff and #800000 will give you really bad results, but other than those, it should work alright.