Autolink Color Contrast

Typical documents use black for the text color and white for the background. This gives the highest contrast. Hyperlinks are often displayed in blue, which gives good contrast on a white background. But other combinations of colors may not be so easily distinguishable. This post describes how RichEdit handles such combinations and muses about possible better ways to ensure sufficient contrast.

The post RichEdit Colors includes a section on autolink color and mentions what happens if the contrast between the foreground and background colors isn’t sufficient, that is, the colors are too similar. The current criterion for the colors being too similar is that the squared length between the colors in the three-dimensional RGB space is less than 4% of the maximum squared length (see IsTooSimilar() function below). Unfortunately this criterion doesn’t work well for dark gray backgrounds.

In this discussion, it’s helpful to keep in mind the definition of an RGB (red green blue) COLORREF as 0x00bbggrr, that is, blue is the most significant color byte and red is the least, so pure blue is 0x00FF0000, pure green is 0x0000FF00, and pure red is 0x000000FF. The high byte is nonzero for palette colors or it may be used as an alpha channel. This is the opposite order from that used in the handy color contrast analyser.

The current RichEdit code uses pure blue (0x00FF0000) for auto hyperlinks unless the following function returns TRUE


(Couldn’t resist putting a little math notation into this program! If only C++ understood it…) If this function returns TRUE, then the color indices COLOR_HIGHLIGHTTEXT and COLOR_WINDOWTEXT are tried. The client can choose what COLORREFs these correspond to or delegate to the Windows GetSysColor() function. In particular, if the background color is black (0x00000000) and the foreground color is blue (0x00FF0000), we get  for which the IsTooSimilar() function returns FALSE. Since the link is pretty hard to read, can we get a better color contrast criterion?

Relative Luminance Criterion

The Web Content Accessibility Guidelines define a color contrast criterion using the relative luminance. The theory considers both human eye physiology and display technology and is described in W3C Standard Default Color Space. Summarizing the results, we have the standard nonlinear sRGB’ components defined in terms of the COLORREF color bytes byAs such, the sRGB’ components are normalized to 1 instead of to 255. The linear sRGB components used in the relative luminance formula are defined in terms of sRGB’ by 


The relative luminance ℓ is given by

Notice that the green component contributes most of the luminance, while the blue gives very little. The color contrast ratio 𝑟 is

defined by in which ℓ> is the larger relative luminance of the two colors and ℓ< is the smaller.

The criterion for sufficient color contrast is

𝑟 > 3 for print sizes over 14pt bold equivalence or 18pt normal

𝑟 > 4.5 for smaller text.

The IsTooSimilar() function could be defined using this criterion. For the blue on black case above, 𝑟 = 2.444, which has insufficient color contrast and RichEdit would fall back to a white foreground color. Note that the contrast between the regular background color and the selection background color doesn’t have to be as large as that between various colors and the text color. Since character stem widths are so small, text needs considerable color contrast to be seen clearly.

Defining Fallback Colors

In general, the best approach is for the RichEdit client to supply carefully vetted color choices in the first place. The client has control over the text and background colors for selected and unselected text as described in RichEdit Colors. However, the client currently has no control over autolink color.

According to MSDN, the color index COLOR_HOTLIGHT is intended to be used “for a hyperlink or hot-tracked item”. Currently RichEdit doesn’t ask for COLOR_HOTLIGHT. On my laptop the Windows GetSysColor(COLOR_HOTLIGHT) returns 0x00CC6600 which easily passes the current IsTooSimilar() test above for a white background and also passes the luminance contrast test with 𝑟 = 5.57. It fails the luminance contrast test for a black background with 𝑟 = 3.77 < 4.5. RichEdit could ask the client for COLOR_HOTLIGHT first and if that fails the contrast test, try pure blue, etc. In principle, ITextDocument2::SetEffectColor() could take a flag to allow the client to specify link color(s) instead of underline colors, but this extension hasn’t been made to date.

A common alternate color choice is the reverse video color (flip the color bits by XORing with 0x00FFFFFF). Interestingly enough this choice fails the luminance contrast test for COLORREFs like 0x666666 and its reverse video value 0x999999, for which 𝑟 = 2.02 < 4.5, so reverse video isn’t a color-backup panacea. Guess the person who went around suing companies for using the XOR cursor without paying him royalties didn’t have such a good thing after all!