Color contrast and accessibility: the ratio, WCAG minimums and the ADA

The checker at the top of this page compares two colors and returns a number, from 1:1 to 21:1. This guide explains where that number comes from and which threshold it has to reach for what you are coloring.

Lightness before hue
#Color contrast in web accessibility
Color contrast is the difference in lightness between text and its background: 1:1 for two colors of the same lightness, 21:1 for black on white. It matters most to people with low vision and to everyone whose eyesight fades with age.
Hue barely enters the calculation. A red #FF0000 and a green #009400 give 1.00:1: to the formula, they are two identical grays. The W3C designed it that way, because someone who struggles to tell colors apart can't rely on hue to read.


Image description:
On the left, a banner announces the "Holiday Makers Market" on Saturday, December 13, on Pearl Street. All the text is bright red (#FF0000) on a bright green background (#009400). The two colors differ in hue, but the text vibrates and is hard to read.
On the right, the same banner with each color replaced by the gray of the same lightness. Text and background turn into the same mid gray: the rectangle looks plain and no word is visible anymore. That is what the ratio measures.
The thresholds come from the WCAG (Web Content Accessibility Guidelines), the W3C standard that US and European rules point to. For the short definition, see the glossary entry on the contrast ratio.
#How to calculate the contrast ratio between two colors
Bright blue text #0061F3 on a yellow #FFD23F background jumps out at you. Yet the checker returns 3.63:1: a fail for body text.
Text description of the animation
An online store's product card: a photo of a teapot, the name “Stoneware teapot”, the price $32.00 and a yellow “Add to cart” button in bright blue text, at body text size. The character dd, the Includdy logo with arms and legs, comes in from the left, looks at the button and gives a thumbs up. The question appears: does this button have enough contrast?
A “Contrast checker” label appears under the card and a line of light sweeps across the button, like a scanner.
A red “Fail” stamp lands askew on the corner of the button. Below it, in large type: ratio 3.63:1, at least 4.5:1 needed. dd jumps.
The word ratio flashes. The question: what is this ratio, and where does 3.63 come from? A question mark floats above dd.
The card fades. The yellow button is magnified until a single pixel of the screen shows: a square made of three strips of light, red, green and blue, which light up one after the other.
Their names appear under the three strips: red, green, blue. Every pixel mixes these three lamps to make a color.
The pixel disappears. The button's two colors appear at the top: on the left the yellow background #FFD23F, on the right the blue text #0061F3. The words red, green and blue rise above each column. Each code splits into three chips: FF, D2 and 3F for the yellow, 00, 61 and F3 for the blue.
Under each chip, its lamp's setting appears, from 0 to 255: 255, 210 and 63 for the yellow, 0, 97 and 243 for the blue.
A fraction bar and the number 255 slide under each setting: each setting is divided by 255, the highest possible value, to get a relative value. A moment later, each fraction gives way to its result, from 0 to 1: 1, 0.82 and 0.25 for the yellow, 0, 0.38 and 0.95 for the blue.
Bars rise to show the real light the screen gives off for each share, with their value above. For the yellow: 1, 0.64 and 0.05. For the blue: 0, 0.12 and 0.90.
dd explains that color codes are tuned for the eye, which tells shades apart better in the dark than in the light: so they don't measure the amount of light directly. To get it back, each share is raised to the power of about 2.2. The word “power” and the number 2.2 appear in large type, then the 2.2 shrinks and moves into place as an exponent next to each share. Each bar then drops to its real light: 0.82 gives 0.64, 0.25 gives 0.05, 0.38 gives 0.12 and 0.95 gives 0.90. The values shown come from the exact WCAG formula, a little more precise, which the power of 2.2 approximates well.
The columns fade. A bell curve over the rainbow shows how sensitive the eye is to each color of light: high in the green, low in the blue. Near the three lamps, badges give each one's share of the screen's white, which depends on the eye's sensitivity and on each lamp's light: red 21%, green 72%, blue 7%. The columns come back and the badges move down under the bars. Each bar is multiplied by its badge and shrinks: for the yellow, 0.213, 0.461 and 0.004; for the blue, 0.000, 0.085 and 0.065 (calculated with the exact values, then rounded).
In each column, the three bars stack and their values add up: 0.213 + 0.461 + 0.004 ≈ 0.677 for the yellow (the exact sum, since the values shown are rounded), 0.000 + 0.085 + 0.065 = 0.150 for the blue. Each stack becomes a gray: a light gray of 0.68 for the yellow, a dark gray of 0.15 for the blue, once rounded.
The two grays rise into a calculation: (0.68 + 0.05) / (0.15 + 0.05), and with the exact values, (0.6771 + 0.05) / (0.1502 + 0.05) = 3.63. A note explains that 0.05 stands for the room light the screen reflects. Counting the room light, the yellow gives off 3.63 times as much light as the blue: that is the ratio. dd points at the result and nods.
Finally, the product card comes back with its “Fail” stamp. dd asks how to fix this button. The text blue darkens to #0050C8: the red stamp flies off, a green “Pass” stamp replaces it, and the line becomes “Ratio 4.88:1, above 4.5:1”. dd gives a thumbs up.
Finally, an animation of the Includdy contrast checker, in a browser window at the address includdy.com/contrast. The checker assembles: the WCAG 2.2 and APCA tabs, the “Text color” and “Background color” fields, the result card and the preview; the camera glides from one part to the next and a pointer makes every move. At first, black text on white: 21.00:1. Caption: “Let's try this blue and this yellow in the checker.” The pointer types #0061F3 in the text field, then #FFD23F in the background field. Caption: “Enter the text color, then the background color.” The card turns red: 3.63:1, “Large text only”. dd points at the score. Caption: “The checker instantly says the contrast is too low for normal text.” Under the card, “Possible improvements” opens with three nearby combinations: text adjusted 4.54:1, text and background adjusted 4.52:1, background adjusted 4.50:1. Caption: “The tool then suggests nearby colors that pass the test.” The pointer clicks “Text adjusted”: the text becomes #004fe1, the card turns green, 4.54:1, “Readable, AA for normal text”. Caption: “One click, and your text is readable.” Finally, the window moves to the left and a card appears on the right: a QR code that opens includdy.com/contrast, the address includdy.com/contrast in large type and the words “Free, no sign-up”. dd gives a thumbs up. Final caption: “It's free, with no sign-up, at includdy.com/contrast”.
Finally, the window and the card fade out. dd stays alone on stage and does a little dance: he points a finger in the air, wiggles a bottom he doesn't have and winks. Then he jumps into his place in the word “inclusion buddy”: his two letters become its blue “dd”. The word folds into “includdy”, the logo comes to the front, and the slogan appears below it as the voice says it: “Web accessibility made simple. Save time and stay compliant.”
- Three lamps per pixel. The screen mixes a red, a green and a blue lamp, each set from 0 to 255. The code
#0061F3gives these settings: 0, 97 and 243. - A share from 0 to 1. Each setting is divided by 255, the highest value possible: 97 becomes 0.38, and 243 becomes 0.95.
- The real light. Color codes are tuned for the eye, which sees shades better in the dark: they don't measure the amount of light. To get it back, each share is raised to the power of about 2.2: 0.38 gives 0.12, and 0.95 gives 0.90.
- How sensitive the eye is. The eye is far more sensitive to green. Based on that sensitivity and the light of each lamp, green accounts for 72 of every 100 parts of lightness, red for 21 and blue for 7. Each light is multiplied by its weight: the blue lamp, almost at full power, now counts for only 0.065.
- The sum. The three results are added up, which gives the lightness of the color, from 0 to 1. Our blue comes to 0.1502, and the yellow climbs to 0.6771.
- The division. The lighter value is divided by the darker one, adding 0.05 on each side for the room light the screen reflects: (0.6771 + 0.05) / (0.1502 + 0.05) ≈ 3.63.
In black and white, these two colors would turn into a dark gray and a light gray fairly close to each other: that gap is what the ratio measures. Black (0) on white (1) gives 21, hence the 21:1 ceiling. The same weight of green explains why pure green #00FF00 on white only gets 1.37:1.
#WCAG and ADA color contrast requirements: 4.5:1, 3:1 and 7:1
The threshold depends on the size of the text and the level you aim for: AA, the one the law asks for, or AAA, the stricter one.
| Type of text | Level AA | Level AAA |
|---|---|---|
| Body text | 4.5:1 | 7:1 |
| Large text: 24 px (18 pt), or 14 pt bold, about 18.7 px | 3:1 | 4.5:1 |
So an 18 px bold subheading stays below the large text cutoff and still needs 4.5:1.

Image description:
Page of a pottery studio, "Clay & Salt". At the top, the logo and four icons: search, account, favorites, cart.
The heading "Our Saturday workshops" (marker A) is set at 24 px, in terracotta #C0703E, on white. Below it, a short dark gray paragraph introduces the workshops.
A card follows, with a photo of a stoneware mug. Its subheading "Intro to the wheel" (marker B) uses the same terracotta, at 18 px bold. Below it: a description, "Saturday 10 a.m., $65" and a black "Book" button.
Both texts have exactly the same color; only their size changes the threshold they must reach.
- The gray
#6B7280used for secondary text reaches 4.83:1 on white and passes. - The gray
#9CA3AFused for placeholder text in form fields only reaches 2.54:1. #777777gives 4.48:1 and fails: the ratio is never rounded.
Secondary text is what most often lands right on the edge of the threshold. On the product card below, look at the description line under the mug's name.


Image description:
Two identical product cards: on the left, a photo of a speckled stoneware mug; on the right, its name "Speckled mug", a line of description, the price $28.00 and a black "Add" button.
The description "Handmade stoneware from Portland, 10 oz. Dishwasher safe." is gray #777777 in the first card (marker A) and gray #374151 in the second (marker B). In the second, the text is also slightly larger, 16 px.
Why 4.5? Good eyesight can read from 3:1, and around age 80 people need about one and a half times more contrast.
#ADA and Section 508 color contrast requirements
US accessibility rules don't set their own contrast numbers. They point to WCAG level AA, so the thresholds above are the ones to meet.
- ADA Title II, for state and local governments: the Justice Department's 2024 rule adopts WCAG 2.1 Level AA. After a one-year extension published in April 2026, governments serving 50,000 people or more must comply by April 26, 2027, and smaller ones and special districts by April 26, 2028.
- Section 508, for federal agencies: the Revised 508 Standards incorporate WCAG 2.0 Level AA. WCAG 2.0 already has the 4.5:1 rule for text, but the 3:1 rule for interface components only arrived with WCAG 2.1.
- ADA Title III, for businesses open to the public: no regulation sets a technical standard for websites yet. The Justice Department's guidance lists poor color contrast first among the barriers it expects businesses to avoid.
If you sell to consumers in the EU, the European Accessibility Act has applied since June 28, 2025. Its reference standard, EN 301 549, builds on WCAG: version 2.1 AA in the edition in force, and 2.2 AA in the edition adopted in August 2026, which is not yet cited under the Act. The text contrast thresholds are the same in WCAG 2.0, 2.1 and 2.2, so a pair that passes one version passes the others.
#Non-text contrast for buttons, form fields and icons
Success criterion 1.4.11, Non-text Contrast, asks for 3:1 on whatever lets people identify an interface component: the border of a field with no background color, the focus indicator, the check mark in a checkbox, an icon alone in a button.
On a field, the border shows where to type. A very light gray border #E5E7EB gives 1.24:1 on white: in direct sunlight, the field disappears. Compare the outlines of the email field and the checkbox.


Image description:
Two versions of a cheese shop's order form, step 1 of 3, "Your details". Each has a "Full name" field filled with "Emily Carter", an "Email address" field filled with "[email protected]", a checkbox to get an email when Friday's cheeses arrive, and a dark brown "Continue to pickup" button.
In the first version, the borders of the fields and the checkbox are very light gray, #E5E7EB: on white, the outlines barely show (marker A on the email field). In the second, they are mid gray, #6B7280, and each field stands out clearly (marker B).
An icon on its own, with no word next to it, is the only clue to what the button does: it also has to reach 3:1. Look at the four icons in the header.


Image description:
Two versions of a pottery shop's header. On the left, the "Clay & Salt" logo; on the right, four icons on their own, with no text: magnifying glass, account silhouette, favorites heart, shopping bag. Under the header, the breadcrumb and the heading "Stoneware bowls".
In the first version, the four icons are very pale gray, #D1D5DB (marker A), almost invisible. In the second, they are dark gray, #4B5563 (marker B).
A button's background, on the other hand, can blend into the page as long as its text is enough to spot it; that text is what has to reach 4.5:1. White on the #FF6B00 orange of many buy buttons only gives 2.86:1, and the error red #EF4444 on white gives 3.76:1.


Image description:
Two versions of the buy box for a "Stoneware serving bowl, 9 in" at $46.00. It shows a quantity selector set to 1, a large rounded "Add to cart" button with a bag icon, and the line "Ships within 48 hours, plastic-free packaging".
In the first version, the button is bright orange #FF6B00 with white text that lacks sharpness (marker A). In the second, it is burnt orange #C2410C, still orange, and the white text reads clearly (marker B).
The focus outline shows which element you reached with the Tab key. If it blends into the background, someone navigating by keyboard loses their place on the page. In the menu below, the focus is on "Order".


Image description:
Two versions of a bakery's header, "Miller Street Bakery", with a brown "My cart" button and a menu of four links: "Our breads", "Order", "Hours", "Visit us". Below it, a cream banner reads "Sourdough, baked every morning".
Keyboard focus is on "Order". In the first version, it has a sky blue outline, #BFDBFE, that you can barely tell from the white (marker A). In the second, the outline is dark blue, #1D4ED8, and jumps out (marker B).
#Contrast exceptions: logos, decorative text and disabled buttons
No threshold applies to text in a logo, to a disabled element, to purely decorative text or to text that happens to appear in a photo. An icon that repeats the word written next to it is exempt too. The page below gathers four of these cases.

Image description:
Page of an online grocery, "green pantry". At the top, the logo: a leaf and the name in light green (marker A), then the search and cart icons.
The heading "Today's fruits and veggies" is very dark green. To its right, the word "Local" is written very large, in an almost white green (#E8F5EE): it is decoration (marker B). Below, the line "Free delivery on orders over $40" starts with a light gray truck icon (marker C).
At the bottom, the "Your cart" box lists a seasonal box at $24.90 and a wildflower honey at $9.50, for a total of $34.40. The line "Choose a pickup time to continue." comes before the "Place order" button, grayed out and disabled (marker D).
Brand guidelines only exempt the logo: buttons and links in the brand color still have to meet the thresholds. If your brand color is too light to carry text, keep it for the logo and use a darker version for buttons and links.


Image description:
Two versions of the "Seasonal box, 13 lb" page at the grocery "green pantry": a description, the price $24.90, a large "Order this box" button and the sentence "Ready in 2 hours. See pickup locations", whose end is an underlined link.
In the first version, the button has a light green background, #4CAF7A, with white text (marker A), and the bold link uses that same green (marker B). In the second, the button and the link switch to the darker green #1A8755 (markers C and D). The logo stays light green in both.
#Link color contrast in body text
A link marked only by its color has to reach 4.5:1 with the background and 3:1 with the surrounding text (technique G183). The default blue #0000EE reaches 9.40:1 on white, but only 2.23:1 next to black text: without its default underline, it would be hard to tell apart from the text.
Underline your links: the requirement against the surrounding text goes away, and the one against the background stays. An underline that only shows on hover is not enough.


Image description:
Two versions of a post on a bakery's blog, "Sourdough, our slowest ingredient". The paragraph is black on white and contains a link, "our country loaf recipe", in blue.
In the first version, the link is not underlined: only its blue color sets it apart from the black text around it (marker A, at the start of its line). In the second, the same blue link is underlined (marker B).
#Contrast of text over images and transparent text
You measure the color actually shown on screen. Black text at 50% opacity on white turns into the gray #808080: 3.95:1, a fail. The events page below uses that transparency for its subtitles.


Image description:
Two versions of a bar's October events page, "The Copper Mug". Two events are listed with their date: "Friday night jazz" on October 17 and "Board game night" on October 24. Under each black title, a subtitle gives the time and details.
The subtitles are black made transparent. At 50% opacity, they look mid gray (marker A); at 75%, a darker gray (marker B).
On a photo, measure the least favorable spot under the letters, and add a dark overlay if the background varies too much. A bright, busy photo, like this florist's, is the hardest case.


Image description:
Two versions of a florist's homepage banner, "Margaux's Flowers". The photo shows the bright inside of a shop: white and pink flowers in zinc buckets, in front of a large sunlit window.
On top, in white: the florist's name, a menu, the heading "Same-day seasonal bouquets" and the sentence "Order by 2 p.m. for delivery anywhere in Austin."
In the first version, the photo is shown as is: the white heading gets lost in the white flowers and the light wall (marker A). In the second, a dark gray overlay covers the whole photo, which is still recognizable, and the white text reads everywhere (marker B).
Text drawn into the image itself counts too: the tagline of a banner exported as a JPG, the prices on a promo graphic, a screenshot. WCAG applies the same thresholds to these images of text as to the text on the page. A tool that only reads the code can't see those letters, so check them separately. Such an image also needs alt text that repeats its words: our alt text generator writes it for you.


Image description:
Two versions of a garden center's promo graphic. The photo shows green plants in terracotta pots, on light wood shelves, in front of a white wall.
In the first version, the slogan "20% off all houseplants, through October 31" is written in white, at the top right, straight on the white wall and the leaves: you can barely read it (marker A). It is part of the image file.
In the second, the same slogan is written in white on a dark green band (#14532D) at the bottom of the image, and it reads clearly (marker B).
#The APCA contrast algorithm: three changes from WCAG 2
APCA (Accessible Perceptual Contrast Algorithm) is another way to calculate contrast, created by Andrew Somers. Like WCAG, it starts from the lightness of the two colors. After that, it differs on three points.
Text description of the animation
Three large color chips line up: on the left black text #000000, in the middle a button's green background #16A34A, on the right white text #FFFFFF. dd, the Includdy logo with arms and legs, comes in from the left and looks at them. Like WCAG, APCA starts by measuring the luminance of each color, the light it gives off. The black and white chips fade and, under the green, the three lamps of the first cartoon come back: red, green, blue. Under each name, its pair of the code, 16, A3 and 4A, then its setting out of 255: 22, 163 and 74. Bars rise to each lamp's share: 0.09, 0.64 and 0.29. Then “power 2.4” appears in large type, and the 2.4 moves into place as a small exponent next to each share. At this step, APCA uses a plain power of 2.4, where the exact WCAG formula follows a two-part curve, close to a power of 2.2. The bars drop to the real light: 0.00, 0.34 and 0.05. Under the bars, the eye's weights appear, the same as for WCAG: ×21%, ×72% and ×7%, and on the right, small, the eye's sensitivity curve comes back as a reminder, with a red, green and blue dot for each lamp. The bars shrink again: 0.0006, 0.2443 and 0.0037. Then they stack into a single gray bar, while their values settle above it, one under the other, as a written addition: 0.0006 + 0.2443 + 0.0037 = 0.2486. That is the green's gray for APCA. Beside it, a small note recalls WCAG's: 0.2686. The gray rises back under the green chip. The chips get their color back and, under each one, its gray appears with its value: 0 for black, 0.2486 for the green, 1 for white, and each one takes its place on a ruler that runs from black to white. dd points at them.
The chips fade and the grays line up in two columns. On the left, a green “Continue” button in black: dark text on light. On the right, the same button in white: light text on dark. The green's gray splits in two to go into both columns. The first difference from WCAG: the direction of contrast matters. dd looks at one button, then the other, a question mark above his head.
To show why, the buttons and grays move apart for a moment, and two panels appear under the labels “Dark on light” and “Light on dark”: on the left, a black “Aa” on a white panel, on the right, a white “Aa” on a black panel. The eye adapts to the light around the text: on a dark background, light text acts a bit like a small lamp. For example, it can look bolder and a little blurry, which is the halo, and the white “Aa” starts to glow. APCA measured powers specific to each direction, one for the background and one for the text: 0.56 and 0.57 when the text is dark on light, 0.65 and 0.62 when it is light on dark. They appear under the panels. dd looks at one panel, then the other, and points at the white “Aa”, then the buttons and grays come back.
First the background. In each column, the power appears in large type: “power 0.56” on the left, “power 0.65” on the right. The word fades, the number shrinks and moves into place, small, at the top right of 0.2486: it is an exponent. The result appears next to it: 0.2486 to the power of 0.56 equals 0.4587, and 0.2486 to the power of 0.65 equals 0.4047. Then only this result stays in the gray. dd points at the powers, leans to follow their path, then nods.
Then the text. APCA first lifts pure black a little: its 0 becomes 0.0045. The text's powers appear in large type in turn, 0.57 for black and 0.62 for white, then become exponents the same way. Black gives 0.0461 and white stays at 1.
The second difference from WCAG: APCA subtracts instead of dividing. The grays move apart and, in each column, two bars rise side by side, the background on the left and the text on the right, labeled “WCAG 2”: these are WCAG's luminances first, plus 0.05. The green is 0.2686 + 0.05 = 0.3186, black 0 + 0.05 = 0.05 and white 1 + 0.05 = 1.05. WCAG divides: it looks at how many times taller one bar is than the other. Copies of the short bar slide in one by one from the right and stack on top of it up to the top of the tall one, the last one cut off: × 6.37 on the left, the 6.37:1 ratio of the black text, and × 3.30 on the right, the 3.30:1 ratio of the white text. Then the label becomes “APCA” and the bars change height: they take APCA's corrected lightness values, 0.4587 and 0.0461 on the left, 0.4047 and 1 on the right. APCA subtracts: it measures the gap between the tops of the two bars. A bracket marks this gap: 0.4126 on the left, minus 0.5953 on the right. dd points at the stack, then leans toward the gap and nods. The bars fade and the grays come back. In each column, the two values sit on either side of a minus sign: the background's lightness minus the text's lightness. On the left, 0.4587 minus 0.0461 equals 0.4126. On the right, 0.4047 minus 1 equals minus 0.5953. This gap then goes through three operations, each new value replacing the previous one. First, it is multiplied by 1.14, a fixed scale factor: 0.470 on the left, minus 0.679 on the right. Then APCA tests the gap: under 0.1, the score is zero, which wipes out contrasts that are too weak; here, it is above 0.1. Otherwise, it is pulled 0.027 closer to zero: 0.443 on the left, minus 0.652 on the right. Finally, multiplied by 100, it becomes the Lc score: Lc 44.3 for black text, Lc minus 65.2 for white text. The minus sign flashes: it means light text on a dark background. dd points at each operation, nods, then points at the minus sign.
The third difference from WCAG: APCA's score gives a precise minimum text size, where WCAG only has two categories, normal text and large text. The calculations fade, the scores move up under the buttons, and below them two green squares appear with tiny “Aa” letters. The letters grow. White becomes readable from 21.75 px, at normal weight, and stops early. Black has to grow much more, up to 44 px, still at normal weight. dd leans in to follow the race.
The black column fades and the white square gets a green outline: APCA predicts which text the eye reads best, here the white. dd gives a thumbs up and nods.
To sum up, everything fades and dd runs off to the left, his heels in a little cloud of dust. Three cards appear one after the other in the middle. First card: “Direction of contrast matters”, with a small black-on-white “Aa” next to a white-on-black “Aa”. Second card: “APCA subtracts lightness”, with a minus sign next to a division sign crossed out in red. Third card: “The score gives a minimum size”, with a small “Aa” that grows. dd drops from the top of the frame onto the first card, raising dust, and nods. At each new card, he takes a run-up to the edge, crouches and jumps over the gap to the next card, where he lands in a cloud of dust. On each card, he stops for a moment, looks at the card and nods. On the third, he watches the “Aa” grow and gives a thumbs up. Then he leaps toward the top right corner and leaves the frame, before running back in from the left to his place, next to the bubble.
Finally, an animation of the Includdy contrast checker, in a browser window at the address includdy.com/contrast. The checker assembles: the WCAG 2.2 and APCA tabs, the “Text color” and “Background color” fields, the result card and the preview; the camera glides from one part to the next and a pointer makes every move. At first, black text on white. Caption: “Let's put this green button in our checker, then give it white text.” The pointer pastes #16A34A in the background field, then types #FFFFFF in the text field. In WCAG 2.2, the card turns red: 3.30:1. The pointer clicks the APCA tab; the indicator slides and stretches. Caption: “Switch to APCA, and the minimum text size shows up.” The card shows Lc −65.2, “Large text only, minimum size: 22 px, or 15 px bold”, “light text on a dark background”. The camera glides to the preview, which says: “At this contrast, normal text must be at least 22 px” (APCA's table gives 21.75 px, the tool rounds up). Caption: “In white, at normal weight, you need at least 22 pixels.” The pointer replaces #FFFFFF with #000000: Lc 44.3, “Very large text only, minimum size: 44 px, or 25 px bold”, and the preview, “Readable only when large”, asks for at least 44 px. dd's eyes widen. Caption: “In black, the letters would have to be twice as big.” Finally, the window moves to the left and a card appears on the right: a QR code that opens includdy.com/contrast, the address includdy.com/contrast in large type and the words “Free, no sign-up”. dd gives a thumbs up. Final caption: “It's free, with no sign-up, at includdy.com/contrast”.
Finally, the window and the card fade out. dd stays alone on stage and does a little dance: he points a finger in the air, wiggles a bottom he doesn't have and winks. Then he jumps into his place in the word “inclusion buddy”: his two letters become its blue “dd”. The word folds into “includdy”, the logo comes to the front, and the slogan appears below it as the voice says it: “Web accessibility made simple. Save time and stay compliant.”
- Direction matters. The eye doesn't read dark text on a light background the same way as light text on a dark background. APCA runs each lightness through a curve that depends on that direction.
- A subtraction instead of a division. APCA subtracts the lightness of the text from that of the background. The result is called the Lc score (for lightness contrast), and its sign gives the direction: negative for light text on a dark background.
- A minimum size instead of a threshold. The score doesn't say "pass" or "fail": it gives the text size from which people read without effort, depending on the font weight.
On a green button #16A34A, white text gets Lc −65.2: at normal weight, it reads from 21.75 px up. Black text gets Lc 44.3: it would have to be set at 44 px. The values come from the apca-w3 library.
#WCAG 2 vs APCA: when the two disagree
The two methods don't always give the same answer. On a blue button #2F80ED, WCAG passes black text at 5.43:1 and rejects white at 3.87:1. APCA says the opposite: Lc −71.1 for white, readable at 19.5 px and larger at normal weight, and Lc 38.5 for black, which would have to be set at 71 px. To the eye, many people find the white easier to read.
Text description of the animation
A screenshot of a small website appears in the middle of the screen: “Clayroom Studio”, a made-up pottery studio, with a form and its blue #2F80ED “Send” button, in normal weight. dd comes in from the left. Caption: “This form has a blue button. For its text, black or white?” On “button”, the view zooms gently onto that button, which fills the middle of the screen.
One button, and a hesitation: on “black”, its text turns black; on “white”, it turns white again; then it keeps switching, black, white, black, white, more and more slowly. dd thinks, hand on his chin, a question mark above his head, his eyes on the button.
Caption: “Let's compare both: black on top, white below.” The button splits into two large buttons of the same blue, one above the other: the top one with black text, the bottom one with white text. Then the question: “Which one do you think reads better?” dd looks at one button, then the other, and opens his hands.
The two buttons shrink and line up side by side at the top of the screen, black on the left, white on the right. dd follows them with his eyes.
Under each button, the WCAG 2 verdict appears: black text gets 5.43:1, a pass, in green with a check mark; white text gets 3.87:1, a fail, in red with a cross. Caption: “For WCAG 2, at normal text size, black passes and white fails.” dd hesitates and shakes his head.
Caption: “Yet to the eye, many people find white easier to read. Could WCAG 2 be wrong?” dd points at the white button, whose red verdict shakes.
Below, each button's APCA score appears: Lc 38.5 for black text, Lc −71.1 for white text. The minus sign means light text on a dark background. Caption: “For APCA, white scores Lc −71.1, versus 38.5 for black.” dd's eyes widen, then he nods.
Under each score, a bar shows the minimum size to read the text. Caption: “This score also gives a minimum text size.” Black's bar fills completely, readable from 71 px at normal weight; white's stops at a quarter, readable from 19.5 px.
Caption: “Like our eye, APCA prefers white.” White's verdict lights up and dd gives a thumbs up.
Caption: “Make the blue a little darker, and both methods approve white.” The black text column fades. The white text button darkens, from blue #2F80ED to blue #1470EB. Its verdicts turn green: WCAG 2 gives 4.62:1, a pass, with a check mark; APCA gives Lc −76.8, readable from 18 px. dd gives a thumbs up and nods.
The buttons and their verdicts disappear. Two cards appear side by side, each with a small white “Aa” on blue #2F80ED. On the left, “WCAG 2” shows 3.87:1 on a scale that stops short of the 4.5:1 mark: “compliance, the standard US rules point to”. On the right, “APCA” shows Lc −71.1 on a scale that runs left from zero, on the negative side, and passes the −60 mark (Lc 60 in absolute value), the minimum for 24 px text, or 16 px bold: “readability, closer to the eye, but experimental”. Caption: “US rules point to WCAG 2. APCA, still experimental, has no legal standing.” dd looks at both cards.
Caption: “For compliance, check with WCAG 2,” dd points at the WCAG 2 card and its 4.5:1 line pulses; the APCA card fades.
Caption: “and for readability, look at APCA.” dd points at the APCA card, whose score grows for a moment; the WCAG 2 card fades.
The cards disappear and dd runs off to the left, heels flying, kicking up little clouds of dust. In the middle, a screenshot of a small website appears, a made-up pottery studio, “Clayroom Studio”: a navigation bar, a photo of shelves of bowls and cups, and a “Contact us” form with the fields Name, Email and Your message, then the “Send” button in bold white text, blue #2F80ED. A thin line starts from this button, a dot first, then a line that stretches to a panel on the right: a “Lightness” slider that goes from black to white through the blue, with the color's code under its handle, #2F80ED. Below, two verdict cards for the button's white text: WCAG 2, threshold 4.5:1, and APCA, threshold Lc 60, with the note “for 16 px bold text”. At first, WCAG 2 gives 3.87:1, a fail with a red cross, and APCA Lc −71.1, above the threshold with a green check mark. Final caption: “If they disagree, find a color that passes both tests.”
Right away, dd pokes his head in from the top right corner, glances at the slider, jumps onto it and lands on the track, left of the handle. He puts his hand on the handle and pulls it steadily toward the dark side; he strains, and drops of sweat fly off his head. The button darkens with it, the code under the handle keeps changing and the values roll by like a counter: the WCAG 2 ratio rises from 3.87:1 to 4.47:1, still with its red cross, and the APCA score goes from Lc −71.1 to Lc −75.8. dd stops just before the threshold to catch his breath, sweating. Then one last push: at 4.52:1, on blue #1872EB, WCAG 2 passes and its cross becomes a green check mark, and the handle stops on #1470EB: 4.62:1 and Lc −76.8, both with a green check mark. dd lets go of the handle, gives a thumbs up and nods.
Finally, an animation of the Includdy contrast checker, in a browser window at the address includdy.com/contrast. The checker assembles: the WCAG 2.2 and APCA tabs, the “Text color” and “Background color” fields, the result card and the preview; the camera glides from one part to the next and a pointer makes every move. At first, white text on black. Caption: “One last try with our blue button and its white text.” The pointer pastes #2F80ED in the background field: in WCAG 2.2, the card turns red, 3.87:1. Caption: “Look at its score with WCAG, then with APCA.” A click on the APCA tab: Lc −71.1, “Large text only”. Back to WCAG 2.2; under the card, three combinations: background adjusted 4.52:1, text and background adjusted 7.36:1, text adjusted 4.50:1. Caption: “One click on the suggestion, and both methods agree.” The pointer clicks “Background adjusted”: the background becomes #2274e0, the card turns green, 4.52:1, “Readable”. On the APCA tab, the same pair gives Lc −76.3, “Readable, body text from 18 px”. dd gives a thumbs up. Lower down, the “04 Export” section rises into view with the colors' CSS code, line by line, and the pointer clicks “Copy”, which becomes “Copied”. Caption: “Then copy the whole palette into your code.” Finally, the window moves to the left and a card appears on the right: a QR code that opens includdy.com/contrast, the address includdy.com/contrast in large type and the words “Free, no sign-up”. dd gives a thumbs up. Final caption: “It's free, with no sign-up, at includdy.com/contrast”.
Finally, the window and the card fade out. dd stays alone on stage and does a little dance: he points a finger in the air, wiggles a bottom he doesn't have and winks. Then he jumps into his place in the word “inclusion buddy”: his two letters become its blue “dd”. The word folds into “includdy”, the logo comes to the front, and the slogan appears below it as the voice says it: “Web accessibility made simple. Save time and stay compliant.”
The two methods disagree in both directions: #767676 on white passes WCAG at 4.54:1, but only gets Lc 71.6 with APCA, below the Lc 75 minimum for body text. Switching formulas would make sites that conformed yesterday fail today, and the other way around: the debate has gone on for more than five years.
The law follows WCAG 2. Check conformance with it and readability with APCA; at #1470EB, the button passes both with white text.
#How to check color contrast on a website
Paste your colors into the tool at the top of this page, in any CSS format: it accounts for transparency and suggests nearby colors that pass. To fix a color, keep the hue and change the lightness: the orange #C2410C takes white text to 5.18:1 and is still orange.
Also check what people forget: placeholder text in fields, error messages, focus, hover states and dark mode.
Check dark mode as a palette of its own. A gray that passes on white can fail on a near black background, and APCA often asks for lighter text there, or failing that, larger or bolder text.


Image description:
Two versions of an online shop's "My orders" page, in dark mode: an almost black background and white headings. Two orders are listed, #4821 at $62.40 and #4790 at $18.00, each with a package icon.
Under each order number, a line gives the delivery date and the number of items. In the first version, it is gray #6B7280, dull and hard to read (marker A). In the second, it is very light gray #D1D5DB, in medium weight (marker B).
To check a page that is already live, the Includdy app scans it and finds low contrast text where it appears, including on complex backgrounds and in images.

