
I pasted oklch(0.45 0.08 250) into a contrast checker and the field went red. Not because the pair failed. Because the box would only take a hash and six digits.
The color was already in the CSS. I had to run it through a converter, copy a hex, paste that hex, and then read a badge that said Fail. The badge did not offer a replacement. It just sat there.
The contrast checker on Softery takes the oklch. It prints the WCAG ratio with the two luminances written out. If the pair misses 4.5:1, it hands you the nearest color that would have passed.
The Field Only Wanted a Hash
WebAIM's checker is the one a lot of people still open. It is honest about WCAG 2. It is also a pair of hex fields. Chrome's own panel is the other one, and a thread on r/webdev asked which tool people use because that panel does not always work. In the same thread someone asked if the checker could take a URL instead of hex.
I ranked this page seventh out of 1,754 ideas, score 9.50, about fourteen hours of work. The color family in that list is long: a format converter, a palette from an image, a shade scale. Contrast is first because the unanswered asks were specific, and because a fail with no next step is how a gray ships anyway.
I am not going to tell you the old checkers are useless. They are fine if your file still speaks hex. A lot of CSS does not.
What 4.5 Actually Is
WCAG 2 contrast is not a vibe. It is this:
(L1 + 0.05) / (L2 + 0.05)
L1 is the lighter relative luminance. L2 is the darker one. Relative luminance is a weighted mix of the linearized sRGB channels: 0.2126 red, 0.7152 green, 0.0722 blue. Black on white is 21:1. White on white is 1:1.
AA for normal text is 4.5:1. AAA is 7:1. Large text, 18pt or 14pt bold, can pass AA at 3:1. Those numbers are WCAG 2.2 success criteria 1.4.3 and 1.4.6. They have not moved. A model that tells you "looks fine" is not those numbers.
The page writes the formula with the two luminances filled in. If you want to argue with the badge, you can redo the arithmetic on paper.
The Gray That Almost Makes It
#777777 on #ffffff is 4.48:1. That is the gray a lot of body text still uses. It fails AA for normal text by two hundredths. #767676 on the same white is 4.54:1 and it passes.
I did not pick those two hexes for drama. They are the pair people paste when they want to know where the line is. The page defaults to #777777 on white so the fail and the suggestion are visible before you type anything. If you came here to check a passing pair, swap in your own values. The default is a demonstration, not a brand color.
Two hundredths is not a rounding argument. The criterion is 4.5, and 4.48 is under it. A checker that only shows a red X still leaves you to guess which way to walk.
A Fail Badge Does Not Mix Paint
The usual result is a badge. Pass or Fail. Sometimes a second badge for large text. You already knew the gray felt light. You opened the page to find out what to change it to.
Can't you use directly with a website instead of having to type the colors there?
That is the URL ask. The other missing piece is the color itself. If the pair fails, this page walks lightness in OKLCH and stops at the first value that clears 4.5:1. Hue stays. Chroma stays unless it has to drop so the color can exist at that lightness. You get a hex and an oklch you can paste back into the CSS.
I would rather hand you #767676 than tell you #777777 is wrong and walk away.
Walking Lightness, Keeping Hue
OKLCH is a useful space for this because L is the lightness you actually mean. Walking L on a gray does not drag it through a brown. Walking L on a blue keeps it a blue, just darker or lighter.
The walk starts at the current L and steps by 0.005 toward 0 and toward 1. The first step that reaches 4.5:1 wins. If both directions work, the smaller change wins. If the text and the background are both dark, the page prefers to lighten the background, because darkening near-black text does not create contrast on a dark gray.
If the pair already meets 4.5:1, there is no suggestion. I am not going to invent a "better" gray for a pair that already passes AA. You can still read AAA and APCA and decide on your own.
The suggestion is for AA normal text, 4.5:1. That is the line most reviews cite. If you need 7:1, darken further. The formula is on the page.
APCA Sits in the Next Line
APCA reports a lightness contrast, Lc, not a ratio. Black text on white is about Lc 106 with the apca-w3 library I used. Same-on-same sits near zero. The sign flips when the text is lighter than the background.
A lot of people now prefer APCA for body text on a dark surface, where WCAG 2 can look too kind. I show the signed Lc next to the WCAG table. I do not turn it into a pass or fail badge. APCA's published cutoffs have moved, and WCAG 2 is the number a reviewer will still quote.
Another thread on r/webdev asked people to use a non-AI contrast checker, "actual math thats idempotent". That comment sat at score 12. I took it as a constraint. The page does not ask a model whether the pair passes. It runs the formula. Run it twice and you get the same 4.48.
The Thread Asked for a URL
Hex fields assume you already know the two colors. A lot of the time you do not. You have a live page, a component in production, and a manager who wants to know if the body text passes. Opening DevTools on every pair is how an afternoon disappears. Chrome's panel is the thing people already have, and the thread that ranked this idea said it does not always work.
So the second tab takes a URL. You paste the address, the server fetches the HTML and the same-origin stylesheets, pulls out color and background pairs, scores them, and sends the scores back. Failing rows sort first.
I am not going to pretend this is a painted screenshot. Colors that only exist after a client render, or only inside a bundled script, will not show up. If a pair is missing, paste those two values into the first tab. That tab does not need the network at all.
A Tab Cannot Read Another Origin
A page in your browser cannot read computed styles from someone-else.com. The same-origin rule is why the two-color tab can be honest about staying local, and why the URL tab cannot do the same trick.
The card for this tool said to run that fetch off the client and store nothing. That is what the route does. The browser posts the URL. The server fetches. The HTML is scored and dropped. It is not written to a table.
I will say this plainly, because the rest of Softery is proud of "nothing leaves the tab": the URL path leaves the tab. The two-color path does not. If you only needed oklch and a suggestion, unplug the cable after the page loads.
What the Fetch Actually Does
The server asks for the HTML, then for stylesheets on the same host, up to eight of them. It reads color, background-color and background in style attributes and in CSS rules. A rule that sets both sides becomes a pair. An orphan text color is paired with the body background, or with white if the body never set one.
Each pair is scored with the same function the first tab uses. You get the hexes, the ratio, AA for normal text, and a suggested color when it fails. You can open a row in the two-color tab and keep editing there.
The HTML cap is 1.5 MB. A stylesheet cap is 200 KB. The fetch gives up after eight seconds. Those limits exist so one request cannot sit on the machine.
What I Will Not Fetch
Loopback, private ranges and link-local addresses are refused before a socket opens. Credentials in the URL are stripped. file: and javascript: are rejected. Redirects are followed a short way and checked again at each hop.
That is not a feature list. It is the minimum for a route that will fetch a URL a stranger typed. The two-color tab has no such problem, because it never fetches.
I also will not run the page's JavaScript. A headless browser would see more pairs and would cost more to keep up. I shipped the declared colors. If your theme only exists after a client render, use the first tab.
robots.txt Is Not Optional
Before the HTML fetch, the server reads /robots.txt on that host. If the wildcard agent disallows the path, the audit stops. A missing robots file is treated as allow, the way crawlers have done it for years.
Some people will paste their own staging URL and hit a Disallow. That is the point. A checker that ignores robots.txt is a fetcher with a UI. I would rather show a 403 and let you paste the two colors by hand.
The route also stops after ten audits in a minute from one address. That is a courtesy to the hosts on the other end, not a product meter. The two-color tab has no meter.
How to Check the Two-Color Path
Open the network panel. Change a color. You should not see the hex leave. The ratio, the formula, the APCA number and the suggestion are all JavaScript in the tab.
Paste oklch. Paste lab. Paste navy. Paste a three-digit hex. The swatch and the math use the same parsed color. I used culori for the parse, because I did not want a second hand-rolled color grammar. The WCAG arithmetic is in this repo so the formula on screen is the formula in the test.
#000 on #fff has to be 21. #777 on #fff has to sit under 4.5. Those are tests, not screenshots.
How to Read the Formula
A result that says (1.000 + 0.05) / (0.184 + 0.05) = 4.48 is the whole argument. The 1.000 is white. The 0.184 is the gray. Add 0.05 to each side so a pair that includes black does not divide by zero. That is the WCAG 2 method. It is not new.
| Use | AA | AAA |
|---|---|---|
| Normal text | 4.5:1 | 7:1 |
| Large text (18pt, or 14pt bold) | 3:1 | 4.5:1 |
| UI graphics | 3:1 | not a WCAG 2 ratio |
Large text is a real exception, not a loophole you apply to a 16px paragraph. If the preview looks like body text, use the 4.5 line.
Bing's autocomplete returned 31 suggestions across "contrast checker" and "color contrast" when we ranked this idea. People type this query a lot. They still get hex boxes.
What You Do Next
If you have two colors, use the first tab. If you have a live page, use the second, then fix the failing rows one at a time in the first.
- Open Contrast Checker.
- Paste the text color. oklch is fine.
- Paste the background.
- Read the formula. If it fails, apply the suggested color and copy the hex or the oklch.
- If you came with a URL, audit it, then open the rows that fail.
The other tools on Softery follow the same habit: do the useful bit, and do not ask for an account to hand you a number you could have computed yourself.
Last updated: September 4, 2026 | Reading time: 10 minutes
Written by Evgeniy Poznyak, who has pasted the same gray into too many hex boxes.