APCA Contrast Checker
Compute the APCA Lc value for a text and background pair, with polarity, the font size and weight lookup, and the WCAG 2 ratio beside it — entirely on this device.
APCA is the WCAG 3 draft contrast metric and is not ratified. Nothing on this page is a conformance claim — WCAG 2 is still the ratified requirement today.
- Level
- —
- Screen luminance, text
- —
- Screen luminance, background
- —
- WCAG 2 ratio, for comparison
- —
Enter a text color and a background color to see the Lc value.
| Size | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 |
|---|---|---|---|---|---|---|---|---|---|
| 10px | prohibited | prohibited | prohibited | prohibited | prohibited | prohibited | prohibited | prohibited | prohibited |
| 12px | prohibited | prohibited | prohibited | non-content text only | non-content text only | non-content text only | non-content text only | prohibited | prohibited |
| 14px | prohibited | prohibited | non-content text only | 100 | 100 | 90 | 75 | prohibited | prohibited |
| 15px | prohibited | prohibited | non-content text only | 100 | 90 | 75 | 70 | prohibited | prohibited |
| 16px | prohibited | prohibited | non-content text only | 90 | 75 | 70 | 60 | 60 | prohibited |
| 18px | prohibited | non-content text only | 100 | 75 | 70 | 60 | 55 | 55 | 55 |
| 21px | prohibited | non-content text only | 90 | 70 | 60 | 55 | 50 | 50 | 50 |
| 24px | prohibited | non-content text only | 75 | 60 | 55 | 50 | 45 | 45 | 45 |
| 28px | prohibited | 100 | 70 | 55 | 50 | 45 | 43 | 43 | 43 |
| 32px | prohibited | 90 | 65 | 50 | 45 | 43 | 40 | 40 | 40 |
| 36px | prohibited | 75 | 60 | 45 | 43 | 40 | 38 | 38 | 38 |
| 48px | 90 | 60 | 55 | 43 | 40 | 38 | 35 | 35 | 35 |
| 60px | 75 | 55 | 50 | 40 | 38 | 35 | 33 | 33 | 33 |
| 72px | 60 | 50 | 45 | 38 | 35 | 33 | 30 | 30 | 30 |
| 96px | 50 | 45 | 40 | 35 | 33 | 30 | 25 | 25 | 25 |
Published lookup table: https://readtech.org/ARC/tests/visual-readability-contrast/
Press ? for shortcuts
Do it offline:$ python3 -c "Y=lambda h:sum(c*(int(h[i:i+2],16)/255)**2.4 for c,i in zip((.2126729,.7151522,.0721750),(0,2,4)));t,b=Y('888888'),Y('ffffff');print(round(((b**.56-t**.57)*1.14-.027)*100,4))"
How to use it
- Enter the text color, then the background color — hex, rgb(), hsl(), oklch() and CSS color names all parse, and the background must be opaque.
- Read the Lc slot: the sign is the polarity, the magnitude is what you compare against a level, and the band beneath names the highest published range it reaches.
- Set the font size and weight to get a verdict from the published lookup table; leave them empty and only the Lc value is reported.
- Use Swap to see the reverse-polarity design — the number changes, because Lc is not symmetric — and copy or share the pair when you are done.
Enter a text color and a background color to read their APCA Lc value: a signed number from roughly -108 to 106 that carries polarity, the published use-case band, and the minimum the published font-use table asks for at your size and weight. APCA is the metric proposed in the WCAG 3 draft; it is not ratified, and WCAG 2 remains the ratified requirement today. Everything is computed in this tab.
How it works
APCA starts from screen luminance, written Ys. Each sRGB channel is divided by 255, raised to the power 2.4, and the three results are weighted 0.2126729, 0.7151522 and 0.0721750. There is no piecewise segment near black in that step: the 0.04045 / 12.92 kink belongs to the relative-luminance definition WCAG 2 uses, not to this one. For the mid grey #888888 the two definitions give 0.221206 and 0.246201 — a small gap at the input that the rest of the algorithm amplifies.
Very dark colors are then lifted: below Ys 0.022 the value becomes Ys + (0.022 - Ys) raised to the power 1.414, the algorithm's way of accounting for the light a real screen reflects instead of pretending black is black. Which color is lighter decides the branch. Dark text on a light background is scored as (bg ** 0.56 - text ** 0.57) * 1.14, light text on a dark background as (bg ** 0.65 - text ** 0.62) * 1.14, each with a 0.027 offset removed and a ±0.1 dead zone clipped to nothing, and the survivor is multiplied by 100.
Those two branches use different exponents on purpose, which is why Lc has a sign and why it is not symmetric: #888888 on white reads 63.1 while white on #888888 reads -68.5. A quotient of two luminances cannot express that difference — dividing the lighter by the darker gives one number whichever way round the design is drawn — and the perceptual asymmetry between dark-on-light and light-on-dark text is precisely what the sign records.
Because thin strokes need more contrast than thick ones, a single threshold cannot answer the question on its own. The published font-use table pairs 15 sizes from 10px to 96px with the nine hundred-step weights and gives a minimum Lc per cell, or marks the cell as prohibited or as non-content text only. This page reads that table verbatim; it never interpolates between cells.
Examples
#888888 text on #ffffff · 16px/400Lc 63.1
polarity Dark text on a lighter background
band Lc 60
font use 16px/400 requires Lc 90 — 26.9 short
WCAG 2 3.54:1Mid grey on white, computed here from the published constant block and rounded to Lc 63.06 — the value the one-liner on this page reproduces. It clears the Lc 60 band, yet body text at 16px/400 is asked for Lc 90, so the table refuses the pair by 26.9 points. Mid grey on white is a common label color and this is the verdict it earns.
#ffffff text on #888888 · 16px/400Lc -68.5
polarity Light text on a darker background
band Lc 60
font use 16px/400 requires Lc 90 — 21.5 short
WCAG 2 3.54:1The same two colors, swapped. The WCAG 2 figure is identical because a ratio has no direction, while Lc moves from 63.1 to -68.5: the reverse-polarity branch scores this arrangement 5.5 points further from zero. Swapping a pair produces a different design, not the same design seen from the other side.
#3b82f6 text on #ffffff · 24px/700Lc 63.9
polarity Dark text on a lighter background
band Lc 60
font use 24px/700 requires Lc 45 — 18.9 to spare
WCAG 2 3.68:1A saturated blue heading. At 24px bold the published table asks for Lc 45 and the pair clears it with 18.9 points in hand, while the WCAG 2 figure beside it is 3.68. The size and weight are the reason the two answers part company here.
#e0e0e0 text on #111111 · 14px/400Lc -87.4
polarity Light text on a darker background
band Lc 75
font use 14px/400 requires Lc 100 — 12.6 short
WCAG 2 14.30:1A typical dark-mode body pair, and the disagreement running the other way: the WCAG 2 figure is a comfortable 14.30 while the table asks for Lc 100 at 14px/400 and the pair reaches 87.4. The #111111 background falls under the 0.022 threshold, so the soft clamp applies and the tool says so.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Swapping the colors changes the number, not just the sign
Black text on white reads Lc 106.0; white text on black reads -107.9. The magnitudes differ by 1.8 because the two polarity branches raise the luminances to different powers. Compare magnitudes against a level, and never assume the mirror of a pair inherits its verdict.
Two different reasons Lc comes back as exactly 0
If the two screen luminances differ by less than 0.0005 the algorithm treats them as one color and reports 0 with no polarity — identical inputs land here. Separately, a real but tiny difference whose score falls inside the ±0.1 dead zone is clipped to 0, which is what #767676 on #808080 does. The tool names which of the two happened.
Colors under the 0.022 threshold are lifted before comparison
Screen luminance below 0.022 is replaced by Ys + (0.022 - Ys) ** 1.414. That soft clamp is why #111111 and #000000 do not behave like an unbounded dark end, and why the 0.022 threshold appears in the tool's own wording whenever one of your colors crosses it.
Translucent text is flattened; a translucent background is refused
APCA is defined on opaque colors. Text given as rgb(0 0 0 / 50%) is composited onto the background color you entered — Lc 67.1 over white — and the tool reports that it did so. A translucent background has no known backdrop, so it is refused rather than guessed; transparent is refused the same way.
The lookup table is a grid, and nothing between cells is invented
A request for 17px at weight 450 is judged by the published 16px/400 cell — the largest published row and column at or below what you asked for, which is the cautious direction in this table — and the cell actually consulted is shown. Below 10px there is no cell at all. Cells marked prohibited or non-content text only return no minimum, because the table gives none.
What the color field accepts, and where it stops
Hex in 3, 4, 6 or 8 digits, rgb(), hsl(), oklch() and the CSS color names parse. lab(), lch() and color() are outside the grammar this field registers and are refused with the same message a typo gets. An oklch() color outside sRGB is measured as the clamped color a screen can actually show: oklch(0.7 0.32 150) on white reads Lc 41.2. Each color is capped at 256 characters.
Common errors
`blurple` is not a valid color — expected hex (3/4/6/8 digits), rgb(), hsl(), oklch(), or a CSS color name.- The same refusal covers a misspelled name and a notation this field does not register, such as lab(50% 40 30). Nothing is guessed from a near miss, so no Lc value is reported for it.
`rgb(255 255 255 / 50%)` is translucent — the backdrop behind a translucent background is unknown, so the perceptual contrast cannot be computed. Enter the flattened background color.- Half-opaque white sits over something, and that something decides the answer. Read the flattened color out of your browser inspector and enter it. Translucent text is different: it is composited over the background you gave and reported.
The font size and weight lookup needs a size between 1 and 1000 px and a weight between 100 and 900. The Lc value below is still exact.- A weight of 42 or an empty size stops the table lookup and nothing else. The Lc value, its polarity and its band are unaffected, because they never depended on the font.
Color is 257 characters — larger than the 256-character limit for a single color.- A color literal in any registered notation is far shorter than 256 characters, so input past the cap is refused instead of trimmed to something you did not type.
FAQ
Is APCA required for accessibility conformance?
No. APCA is the contrast metric proposed in the WCAG 3 working draft, and that draft is not ratified — its details can still change. WCAG 2 and its ratified contrast requirement are what audits and regulations refer to today. Treat Lc as design evidence, not as a legal position.
Why does APCA disagree with the contrast ratio for the same colors?
They measure differently from the first step. APCA raises each sRGB channel to the power 2.4 with no piecewise segment, so #888888 has screen luminance 0.221206 where WCAG 2's relative luminance is 0.246201, and APCA then subtracts the two values with polarity-specific exponents instead of dividing them. Different inputs, different arithmetic, different answer.
What does a negative Lc mean?
It means reverse polarity: the text is lighter than the background. The sign records the arrangement and the magnitude records the contrast, so -87.4 is compared against a level as 87.4. Positive values are dark text on a lighter background.
Which Lc value should I aim for?
Set the size and weight and read the cell: the published table asks Lc 90 at 16px/400, Lc 75 at 18px/400 and Lc 45 at 24px/700, and marks 10px as prohibited at every weight. Without a font context the published bands are the guide — Lc 90, 75, 60, 45, 30 and 15, each with its own use case.
Which version of the algorithm does this use?
APCA-W3 0.1.9, with the constant block transcribed from the published sources: exponent 2.4, the three luminance coefficients, the 0.56 / 0.57 and 0.65 / 0.62 polarity exponents, the 1.14 scale, the 0.027 offset, the 0.022 black threshold with clamp 1.414, the 0.0005 luminance floor and the 0.1 clip. Those constants give Lc 63.06 for mid grey on white, and the one-liner on this page reproduces that figure independently.
Do the colors I enter leave this page?
No. Parsing, the luminance arithmetic and the table lookup all run in this tab, and there is no request to send them in. A share link carries the pair in the URL fragment, which browsers keep out of the request they make.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Re-run the measurement for the current pair and font context.
- ⌘/Ctrl + S
- Download the readout as a .txt file (a browser save-dialog override).
- ⌘/Ctrl + C
- Copy the Lc summary while nothing is selected; a selection copies itself.
- ⌘/Ctrl + Shift + C
- Copy the Lc summary even when text is selected.
- t
- Advance the theme palette; idle while a color field has focus.
- ? or ⌘/Ctrl + /
- Open the shortcut sheet, where these seven bindings appear with the glyphs your keyboard uses.
- Esc
- Dismiss the topmost overlay.