Visual Contrast of Text Subgroup/APCA model

From Silver
Jump to: navigation, search

The SAPC/APCA Model for Predicting Contrast

To support the foregoing and provide designers with practical and useable guidance for color choices, the SAPC model was created (S-Luv Advanced Perceptual Contrast), and the resultant reduction to executable code, the APCA (Advanced Perceptual Contrast Algorithm).

The larger SAPC model takes into consideration human visual perception of contrast including the factors of spatial frequency expressed as font weight and size, padding and spacing, light adaptation for local, page-wise, and global (ambient), and the luminance and color aspects of the CSS colors used, based on an sRGB display with a standardized observer.

The basic APCA code is a reduction of the larger model, using a simplified estimation of local light adaptation, and using a lookup table to define a minimum font size for a given predicted contrast value.

Advanced Perceptual Contrast Algorithm

The code repository for APCA and SAPC is:

Note: Files that are intended for supporting the Silver/WCAG 3 conformance model all have **APCA** in the file name. Files with SAPC in the name are part of ongoing research and should NOT be used for developing conformance tools.

Plain English Walkthrough

VERSION 0.98G-4g

  • Convert the sRGB background and text colors to luminance Ybackground and Ytext
  • Convert from 8 bit integer to decimal 0.0-1.0
  • Linearize (remove gamma) by applying a ^2.4 exponent
  • Apply sRGB coefficients and sum to Y
  • Y = (R/255.0)^2.4 * 0.2126 + (G/255.0)^2.4 * 0.7152 + (B/255.0)^2.4 * 0.0722
  • We will call these Ytext and Ybackground
  • Determine if Ytext or Ybackground is brighter (higher luminence, for contrast polarity)
  • Soft-clamp if it is less than 0.022 Y
  • Soft Clamp: subtract the darker color Y from 0.022
  • Then apply a ^1.414 exponent to the result
  • Then add that result back to the Y of the darker color
  • (0.022 - Y)^1.414 + Y
  • Apply power curve exponents to both colors for perceptual lightness contrast
  • For dark text on a light background, use ^0.57 for Ytext and ^0.56 for Ybackground
  • For light text on a dark background, use ^0.62 for Ytext and ^0.65 for Ybackground
  • Subtract Ytext from Ybackground, then {insert scale method} to scale the contrast value
  • Always subtract the Ytext value from the Ybackground value.
  • For light text on a dark background, this will generate a negative number.
  • This is intentional, so that negative values only apply to light text on dark BGs, and positive values only apply to dark text on a light BG.

For dark text on a lighter background:

  • If the result is less than 0.1, then set contrast as 0. Otherwise multiply by 100 and subtract 2.7.
  • Lccontrast = (Ybackground^0.56 - Ytext^0.57) {insert scale method} * 100 - 2.7

For light text on a darker background:

  • If the result is greater than -0.1 (closer to 0), then set contrast as 0. Otherwise multiply by 100 and add 2.7.
  • Lccontrast = abs(Ybackground^0.65 - Ytext^0.62) {insert scale method}* 100 + 2.7


Here's a screenshot, but this Wiki does not seem to have LaTeX or MathML — I have both available.

NOTICE: There have been minor changes to the scaling from that shown below.

The latest math for APCA contrast


NEW!! Oct 24 2021 For the most up to date code and related information, visit:



WCAG 2.x to Silver/APCA Comparison Table

When you have a very light background and dark text, and the perceptual middle is around #999 to #AAA, then APCA Lc 60 is roughly equivalent to WCAG 4.5:1. Similarly, APCA Lc 45 is about WCAG 3:1 and APCA Lc 75 is about at WCAG 7:1.

This similarity only holds true in a very narrow range near the color #9e9e9e (per the G-4g scaling) or luminance 32Y to 40 Y. This narrow band is the only point where WCAG 2 and APCA “represent” the same contrast. It applies to colors where the foreground and the background are each equidistant from the mid color. Outside of this very narrow range only APCA remains perceptually accurate, the old method does not. The following table demonstrates these relationships.

This Tables is Revised for G Series Constants (G-4g)

WCAG 2.x to APCA (0.98G) Comparison Table
in HEX
as INT
10.2:1 Lc 90 #3B #F5 #98 59 245 152 93
7:1 Lc 75 #4C #E8 #9A 76 232 154 78
4.5:1 Lc 60 #60 #DB #9D 96 219 157 62.5
3:1 Lc 45 #70 #CC #9E 112 204 158 46
2:1 Lc 30 #7E #BD #9E 126 189 158 32
1.5:1 Lc 15 #8C #AE #9D 140 174 157 17

"Engineered on Purpose" 😁

The SAPC math is engineered to put the key values at easy to remember levels: Lc 30 45 60 75 90. And I specifically wanted Lc 45, Lc 60, Lc 75 to line up at some point with WCAG 2.x's 3:1, 4.5:1, and 7:1, if for no other reason than to be able to say that "Lc 60 is about like the old 4.5:1" to help in moving to this new guideline.

Because APCA is perceptually uniform (within reasonable tolerances), a change by a certain Lc amount has a similar effect regardless of the present value or luminance. In the current scaling, increasing contrast by adding Lc 15 is approximately equivalent to the contrast change of going from a 400 weight font to a 700 weight font, for fonts smaller than about 32px.

As it happens, the old 2.x math method really starts to breakdown when both colors are below #aaa or if the text is above #999 among other things. This became a convenient color to have as a line up point so that there would be a point where SAPC and WCAG values matched and results were "similar".

EDIT: The new G Series exponents demonstrate that the old WCAG2 spec is completely out of line with perception. Darker color pairs that WCAG2 incorrectly pass will fail under APCA, and light text on dark backgrounds that WCAG incorrectly fail will now pass under APCA. Ultimately, APCA will provide as much as 50% more colors, and do so correctly.

Return to the Visual Contrast Subgroup Main Page