Playground
Contrast oracle
Two numbers for every pair, because they measure different things and the disagreement between them is the interesting part.
The text, icon or mark.
Any CSS colour: hex, oklch(), color(display-p3 …), or a var() you have on the page.
What it sits on.
Any CSS colour: hex, oklch(), color(display-p3 …), or a var() you have on the page.
128 / 84
Blood pressure · recorded this morning
This is body text at the pair you have chosen. Read it at arm’s length, and then read it again on the worst screen you own. That is the condition the floor exists for.
Small print, a unit, a timestamp: the sizes that fail first.
APCA
not measured
Lightness contrast. Signed: negative means light text on dark.
WCAG 2.2
not measured
The ratio procurement questionnaires ask about.
Verdict
not measured
Against the published opsinjs floor for this size.
color: #1f2933; background-color: #ffffff;
Why both numbers
The WCAG 2.2 contrast ratio is the figure written into procurement questionnaires, accessibility statements and regulation. You will be asked for it, and the answer has to be available. It is also a model of luminance contrast that predicts readability imperfectly. The imperfection shows most visibly for light text on mid-tone backgrounds, where the model is generous, and for very dark palettes, where it is harsh.
APCA is the perceptual model developed for WCAG 3. It accounts for polarity, because dark-on-light and light-on-dark are genuinely different problems, and it accounts for text size and weight. That is why its answer is a signed lightness contrast rather than a symmetric ratio. It has no legal standing.
opsinjs treats APCA as the design floor and WCAG 2.2 as the compliance floor, and publishes both for every token pair rather than choosing. A pair that fails either one does not ship.
What a passing number does not tell you
Contrast is one criterion. A screen can clear every threshold on this page and still be unreadable to the person it was built for: a number without its unit, a trend arrow whose direction is meaningless without knowing whether up is good, a status conveyed by colour alone to somebody printing in greyscale.
It also says nothing about the surface underneath. Translucent material rungs are measured against a worst-case backdrop with a minimum scrim, and that measurement is generated rather than checked by eye. See the contrast floor.
Finally: the figures published on this site describe the shipped presets. The moment you change a token they describe something else, which is what validating your theme exists for.
The same measurement, three ways
This tool posts to /api/contrast. So does pnpm contrast, which regenerates the committed conformance table, and so does the CI gate that fails a build when a measured pair regresses. There is one implementation, hand-written in lib/color/, with no colour library in the dependency tree. An answer you get here is therefore the answer the build gets, and not an approximation of it.
Contrast and APCA · Measured conformance for every token pair · Colour independence