What should you do when you get a complaint about the color contrast in your web design? It might seem perfectly fine to you because you’re able to read content throughout the site, but to someone else, it might be a totally different experience. How can put yourself in that person’s shoes to improve their experience?
There are some relatively easy ways to test contrast. For example, you can check the site on your phone or tablet in bright sunlight (not reliable*), or add a CSS filter to mimic a grayscale view). But… you don’t have to trust your eyes. Not everyone has your exact eyes anyway, so your subjective opinion can possibly be a faulty measurement.
You can mathematically know if two colors have enough contrast between them.
The W3C has a document called Web Content Accessibility Guidelines (WCAG) 2.1 that covers successful contrast guidelines. Before we get to the math, we need to know what contrast ratio scores we are aiming to meet or exceed. To get a passing grade (AA), the contrast ratio is 4.5:1 for most body text and 3:1 for larger text.
How did the W3C arrive at these ratios?
The guidelines were created for anyone using a standard browser, with no additional assistive technology. The contrast ratios that the WCAG suggests were based initially on earlier contrast standards and adjusted to accommodate newer display technologies, like antialiased text, so content would be readable by people with a variety of visual or cognitive difficulties, whether it be due to age, sickness, or other losses of visual acuity.
We’re basically aiming to make text readable for someone with 20/40 vision, which is equivilent to the vision of someone 80 years old. Visual acuity of 20/40 means you can only read something at 20 feet away that someone with perfect 20/20 vision could read if it was 40 feet away.
So, say your design calls for antialiased text because it looks much smoother on a screen. It actually sacrifices a bit of contrast and ding your ratio. The WCAG goes into more detail on how scoring works.
There are other standards that take contrast in consideration, and the WCAG used some of these considerations to develop their scoring. One is called the Human Factors Engineering of Computer Workstations (ANSI/HFES 100-2007) was published in 2007 and designated as an American standard for ergonomics. It combined and replaced two earlier standards that were created by separate committees. The goal of the combined standard was to accommodate 90% of computer users, and cover many aspects of computer use and ergonomics, including visual displays and contrast. So, that means we have physical screens to consider in our designs.
What does the ratio mean?
The contrast ratio explains the difference between the lightest color brightness and the darkest color brightness in a given range. It’s the relative luminance of each color.
Let’s start with an egregious example of a teal color text on a light gray background.
<h1>Title of Your Awesome Site</h1>
h1 {
background-color: #888888;
color: #1ABC9C;
}
It’s worth calling out that some tools, like WordPress, provide a helpful warning for this when there’s a poorly contrasted text and background combination. In the case of WordPress, a you get notice in the sidebar.
“OK,” you say. “Perhaps you think that teal on gray color combination is not exactly great, but I can still make out what the content says.“ (I’m glad one of us can because it’s pretty much a muddy gray mess to me.)
The contrast ratio for that fine piece of hypertext is 1.47:1.
I wanted a better understanding of what the contrast scores were actually checking and came to find that it requires the use of mathematics… with a side of understanding the differences between human and computer vision. This journey taught me about the history of computer vision and a bit about biology, and gave me a small review of some math concepts I haven’t touched since college.
Here’s the equation:
(L1 + 0.05) / (L2 + 0.05)
L1
is the relative luminance of the lighter of the colors.L2
is the relative luminance of the darker of the colors.
This seems simple, right? But first we need to determine the relative luminance for each color to get those variables.
OK, back to relative luminance
We mentioned it in passing, but it’s worth going deeper into relative luminance, or the relative brightness of any color expressed into a spectrum between 0 (black) and 1 (white).
To determine the relative luminance for each color, we first need to get the RGB notation for a color. Sometimes we’re working with HEX color values and need to covert that over to RGB. There are online calculators that will do this for us, but there’s solid math happening in the background that makes it happen. Our teal hex color, #1ABC9C
, becomes an RGB of 26, 188, 156
.
Next, we take each value of the RGB color and divide each one by 255 (the max integer of RGB values) to get a linear value between 0 and 1.
So now with our teal color it looks like this:
Component | Equation | Value |
---|---|---|
Red | 26/255 | 0.10196078 |
Green | 188/255 | 0.73725490 |
Blue | 156/255 | 0.61176471 |
Then we apply gamma correction, which defines the relationship between a pixel’s numerical value and its actual luminance, to each component part of the RGB color. If the linear value of a component is less than .03938, we divide it by 12.92. Otherwise, we add .055 and divide the total by 1.055 and take the result to the power of 2.4.
Our gamma-corrected color components from our teal color end up like this:
Component | Equation | Value |
---|---|---|
Red | ((0.10196078 +.055)/1.055) ^ 2.4 | 0.01032982 |
Green | ((0.73725490 +.055)/1.055) ^ 2.4 | 0.50288646 |
Blue | ((0.61176471 +.055)/1.055) ^ 2.4 | 0.33245154 |
This part of our equation comes from the formula for determining relative luminance.
We just sort of sped past gamma correction there without talking much about it and what it does. In short, it translates what a computer “sees” into the human perception of brightness. Computers record light directly where twice the photons equals twice the brightness. Human eyes perceive more levels of light in dim conditions and fewer in bright conditions. The digital devices around us make gamma encoding and decoding calculations all the time. It’s used to show us things on the screens that match up to our perception of how things appear to our eyes.
Finally, we multiply the different colors by numbers that signify how bright that color appears to the human eye. That means we determine the luminance of each color by multiplying the red component value by .2126, the green component value by .7152, and the blue component by .0722 before adding all three of those results together. You’ll note that green gets the highest value here,
So, one last time for teal:
Component | Equation | Value |
---|---|---|
Red | 0.01032982 X 0.2126 | 0.00219611973 |
Green | 0.50288646 X 0.7152 | 0.35966439619 |
Blue | 0.33245154 X 0.0722 | 0.02400300118 |
…and add them together for luminance!
L1 = 0.00219611973 + 0.35966439619 + 0.02400300118 = 0.38586352
If we do the same to get our L2
value, that gives us 0.24620133.
We finally have the L1
and L2
values we need to calculate contrast. To determine which value is L1
and which is L2
, we need to make sure that the larger number (which shows the lighter color) is always L1
and is divided by the smaller/darker color as L2
.
Now compare that result with the WCAG success criteria. For standard text size, between 18-22 points, a minimal result of 4.5 will pass with a grade of AA. If our text is larger, then a slightly lower score of 3 will do the job. But to get the highest WCAG grade (AAA), we have to have a contrast ratio result of at least 7. Our lovely combination fails all tests, coming far under 4.5 for regular text or 3 for headline style text. Time to choose some better colors!
I’m so glad we have computers and online tools to do this work for us! Trying to work out the details step-by-step on paper gave me a couple weeks of frustration. It was a lot of me getting things wrong when comparing results to those of automated contrast checkers.
Remember how teachers in school always wanted you to show your math work to prove how you got to the answer? I made something to help us out.
If you view this demo with the console open, you’ll see the math that goes into each step of the calculations. Go ahead, try our two example colors, like #1ABC9C
and #888888
.
I just want my page to have proper contrast, what do I do?!
There are a variety of accessibility resources that you can audit your site. Here’s a list I put together, and there’s another list here on CSS-Tricks.
But here are a few tips to get you started.
First, identify areas that are not serving your accessibility needs.
The WAVE accessibility tool is a good place to start. Run your site through that and it will give you contrast results and help identify trouble areas.
Follow the suggestions of the audit
Use best practices to improve your scores, and remove the errors. Once you identify contrast errors, you can try out some different options right there in the WAVE tool. Click on the color box to pop open a color picker. Then play around until the errors go away, and you’ll know what you can replace in your code.
Run the test again
This way, you can make sure your changes improved things. Congratulations! You just made your product better for all users, not just ones affected by the accessibility errors!
What comes next is up to you!
You can make it easier on yourself and start all new products with the goal of making them accessible. Make accessibility guidelines part of your requirements for both technology and design. You’ll save yourself potentially hundreds of hours of remediation, and potential legal complaints. U.S. government and education websites are required to comply, but other industries are often taken to task for not making their sites equally available for all people.
If you have the option, consider using established and tested frameworks and web libraries (like Bootstrap or Google’s Material Design) that have already figured out optimum contrast theme colors. In many cases, you can take just what you need (like only the CSS) or at least review their color palettes to inform choices. You should still check the contrast though because, while most standard text options in a framework may follow contrast ratio WCAG suggestions, things like alert and message styles may not. (I’m looking at you, Bootstrap!)
Derek Kay has reviewed a list of web frameworks with a focus on accessibility, which I suggest you read if you are looking for more options. The U.S. Web Design System shows one way to solve color/contrast puzzles using their CSS token system that labels colors to make contrast differences super clear), but they also link to several very good resources for improving and understanding contrast.
We took a deeper dive here than perhaps you ever really need to know, but understanding what a contrast ratio is and what it actually means should help you remember to keep contrast in mind when designing future sites, web apps, and other software.
Having a clearer understanding of what the contrast ratio means helps me to remember who poor contrast can affect, and how to improve web and mobile products overall.
I’m not the ultimate subject expert on contrast, just a very, very curious girl who sometimes has issues reading things on the web with low contrast.
If you have any additional thoughts, corrections or further research to share, please leave a comment and I’ll amend this article! The fuller our understanding of the needs and requirements of our sites is, the better we can plan improvements and ultimately serve the needs of our audiences.
Further Reading and References:
- How the Web became unreadable – Kevin Marks
- Understanding Gamma Correction – Cambridge in Colour
- Learn more about Gamma Correction – Science Direct
- Relative luminance – Wikipedia
- 1. Wavelength of Maximum Human Visual Sensitivity Susan Zhao 2007
At the beginning of the post is the suggestion that you can use a grayscale version of your design to quickly visually check contrast for easy misses.
This does not work in practice.
For example, green has a far higher luminance (~10×) than blue, but when both are converted to gray they can look the same, giving a false impression about their relative contrast.
You can see this in the math used to find the luminance (
Y
) of a color in the RGB color space:Y = 0.2126R + 0.7152G + 0.0722B
This is the same formula as further down in the article, which implies this with the equation and surrounding context. I think there is still room for confusion, however, and want to drive home the point that using grayscle as a proxy won’t do the trick.
Great point!
I absolutely agree with you. For people with different vision requirements, a simple grayscale review won’t/can’t catch all problems. The practice has come up as a suggestion across the web, but I personally will always trust actual spot checks with contrast checkers over this.
I, personally, can’t trust my eyes much at all with my vision prescription, but they do actually make finding contrast issues easier as I literally have trouble reading them. This may make my personal contrast needs a bit more extreme than the current best practices. (So we clearly can’t choose the wine in front of me. ;) ) As long as the text in question passes the metrics, I let it go.
A greyscale of a site is useable, but the greyscale must be properly created. You can NOT just sum together and average the RGB components like (R+G+B)/3.
The sRGB values must be converted to relative luminance, first linearize and then second, apply the sRGB coefficients of R0.2126, G0.7152, B*0.0722 these you sum together to relative luminance, Y.
Next, that Y must have the sRGB gamma applied. The “down and dirty” method is to apply the power curve exponent of ^0.455, and then multiply by 255 and round to integer, and then use that integer value for all three colors RʹGʹBʹ.
sRGBgrey = round(Y^0.455 * 255);
That is then a useful, accurate greyscale. FWIW I have a CVD simulator that shows color deficient vision, and also a proper greyscale, that you can process screenshots of your content in at https://www.myndex.com/CVD/
Unfortunately the WCAG ratings don’t take ambient light and font weight into account.
The approach of “tweak things until you just pass the threshold” tends to leave you with too-low contrast in a lot of cases.
A better approach is to start with black on white text (or vice versa in dark mode) and only colour it when it is communicating something specific.
Delegating to frameworks doesn’t always help – Bootstrap’s default blue link colour is too low contrast, for example.
I wrote more on this here : https://www.wired.com/2016/10/how-the-web-became-unreadable/
I agree with you, Kevin, and have read your excellent article. :D In fact, I actually mentioned your specific article when I was talking to Chris about this one.
I think, generally, ambient light is mostly beyond the responsibility of the developers, unless they have complete control/awareness over where the output will be broadcast.
TV, projector, handheld devices in a fishing boat, or stadium kiss cams will all have substantially different contrast requirements, beyond basic standards.
Sometimes a quiet word with presenters who have slide decks that are too hard(thin/light) to read can make future presentations easier to read. Hopefully, if developers receive feedback about their work in different ambient lighting locations they will make adjustments.
I did call out Bootstrap a little bit, as it’s true, not every part of it is compliant, or readable. Other frameworks can have the same partial compliance. We can not offload the responsibility for this.
Similarly, not all automated testing tools can understand opacity overlays and background photos behind text areas, meaning that something might add up to compliance, initially, but not actually be readable. We need to consider all products critically while developing and testing to make sure we can reach all audiences.
But! If we’re going from a site with no contrast accommodation to a site with tested contrast, and developers who start to keep contrast in mind… in most cases I am going to call that improvement.
It feels like contrast is a lesson we have to keep teaching over and over.
Hi Keven, interesting you should mention this — the new contrast method for WCAG 3 is APCA, and font weight and font size are an important part of the new guideline.
The APCA math itself is perceptually uniform as well.
Please explain the exact logic and calculations behind using #2e2f3e instead of black , for the text on THIS page!
Slight nitpick: in the relative luminance calculation, you use the value 0.03938 as the cut-off for gamma correction. The W3C formula specifies 0.03928 however.
Actually both are wrong. The IEC standard specifies 0.04045 as the threshold. It has been known to be wrong in the WCAG 2 spec for years. It has not been changed as in 8bit sRGB, there is literally no difference for luminance in the context of the ratio math. The problems with the old WCAG 2 math are due to other factors.