I am currently calculating the contrast of two foreground colors and a background color to programmatically decide if the foreground color should be white or black.
The formula for the contrast ratio is defined as
(L1 + 0.05) / (L2 + 0.05)
Where L1 is the relative luminance value of the lighter color and L2 is the relative luminance value of the darker color.
WCAG specifies that the optimal ratio for large text is 4.5:1 and for normal text 7:1.
I can't find an explanation as to why both values need to be incremented by 0.05...
Is it simply used to prevent dividing by 0 as the relative luminance for black is exactly 0?
Here are three reference sources:
- https://www.w3.org/TR/WCAG/#contrast-ratio (search for '0.05' using CTRL + F)
- https://www.accessibility-developer-guide.com/knowledge/colours-and-contrast/how-to-calculate/#the-formula
- https://planetcalc.com/7779/
Additionally, here is some code, have fun:
private static readonly Func<Color, double> CalcRelativeLuma = (c) =>
{
// Convert 8-Bit RGB to sRGB.
double R = c.R / 255.0;
double G = c.G / 255.0;
double B = c.B / 255.0;
// Calculate relative luminance for sRGB values.
R = R <= 0.03928 ? R / 12.92 : Math.Pow(((R + 0.055) / 1.055), 2.4);
G = G <= 0.03928 ? G / 12.92 : Math.Pow(((G + 0.055) / 1.055), 2.4);
B = B <= 0.03928 ? B / 12.92 : Math.Pow(((B + 0.055) / 1.055), 2.4);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};
private Color GetHighContrastColor(Color background)
{
// Calculate relative luminance of background.
double l1 = CalcRelativeLuma(background) + 0.05;
// Set contrast colors and calculate relative
// luminance of them.
Color darkContrast = Color.FromArgb(36, 36, 36);
Color lightContrast = Color.FromArgb(239, 239, 239);
double l2 = CalcRelativeLuma(darkContrast) + 0.05;
double l3 = CalcRelativeLuma(lightContrast) + 0.05;
// Calculate contrast ratio for dark and light contrast
// in regards to the background color.
double ratio_dark = Math.Max(l1, l2) / Math.Min(l1, l2);
double ratio_light = Math.Max(l1, l3) / Math.Min(l1, l3);
// Choose which of the contrast colors to use.
double highContrastRation = Math.Max(ratio_dark, ratio_light);
Color contrast = Color.Transparent;
if (highContrastRation == ratio_dark) {
contrast = darkContrast;
}
else {
contrast = lightContrast;
}
return contrast;
}
Short Answer
The 0.05 addition was intended to represent the flare on the face of the monitor from ambient illumination that was typical of CRT type displays, this flare value is recited in Annex D of the sRGB standard for Typical Viewing Conditions.
Longer Answer
I discuss some of the history in this GitHub thread, which includes some discussion with one of the members involved at the time WCAG 2.0 was developed. To be sure, the guideline was well meaning, but nevertheless a misstep in terms of useful or stable design guidance.
Unfortunately, just adding the CRT flare value to both sides of the formula doesn't actually correct the problem with using a simple contrast ratio in terms of its lack of perceptual uniformity, so the WCAG 2.x algorithm really only works when the background is very light or white.
Otherwise, it's relatively meaningless, with brute-forced thresholds, and a source of great misunderstanding and controversy.
The WCAG 2.x contrast math and method references an obsolete standard from 1988 that was centered on monochrome CRT matrix-type displays (pixel on or off only). Remember those old green/black displays?
To say that that ancient technology guideline is not relevant to graphically rich full color content on modern LCD or OLED displays is an understatement. I've written some articles on the topic of how and why the guideline is wrong, and ways to improve actual accessibility, including "Better reading on the web", "Please Stop Using Grey Text", and "A Contrast of Errors".
Can it be fixed?
There have been attempts to create better models, one attempt by making the math more like the original Weber, and adding the 0.05 to only one side of the equation (Huang/Peli), but this still does not correct the actual, functional problem: Ratios vs Perceptual Uniformity.
Ratios are not the best way to model human perception of high spatial frequency text contrast at supra-threshold levels on a self-illuminated display. Most especially for rich content on a color display. There is more at work than a simple distance between two colors.
And let's remember that color is not a real, "absolute" physical quantity that can be measured per se. Color and contrast are perceptions of the human vision system. They are context-sensitive and can be modeled. But context sensitive perceptions can not be measured in an absolute sense.
My favorite example that demonstrates this premise is the following checkerboard with a shadow on it. Both yellow dots are emitting the exact same CSS color from your display. Yet the one under the shadow is perceived as brighter because of the context.
Context needs to be considered whenever we are attempting to estimate or predict how some given stimulus will appear.
Power-curves to the Rescue
In the 1960s, Stevens showed that our perceptions of light changes tend to follow power curves. Stevens also showed that those curves are different based on the spatial frequency (size/thickness of details), and we have found this helps define the perception of text, especially body text, which is at a high spatial frequency.
And since the 1960s, much work has been done in developing perceptually uniform models of color appearance. These newer models have led to substantially more accurate methods of determining contrast for text, and non-text for that matter.
If you'd like to do a deeper dive, please see the article "The Realities And Myths Of Contrast And Color". And for an example of a contrast method that uses a more comprehensive model of contrast perception, read the "Easy Intro to APCA" or see the calculator at apcacontrast.com.
(Full disclosure, I am the creator of the APCA algorithm, as part of my work as an invited expert of the W3C/WAI and Director of Research for the non-profit IRT. Any opinions expressed herein are mine and do not necessarily reflect the views of the W3C or its membership).