Compute WCAG color contrast ratio
This calculator compares a foreground color with a background color using the WCAG relative luminance method.
Run — free
It returns each color’s luminance, the resulting contrast ratio, and clear AA and AAA pass indicators for normal and large text. Use it to review design tokens, validate interface colors before release, or add a repeatable accessibility check to an automated workflow. Inputs are explicit hexadecimal colors, calculations are deterministic, and invalid formats produce a useful validation error instead of an uncertain result. The browser tool is convenient for individual checks, while each API request costs $0.002 when you need the same calculation in code.
Understand the values returned by the calculator
The result starts by normalizing both inputs to six-digit lowercase hexadecimal colors, so shorthand such as #fff is reported consistently as #ffffff. It then gives relative luminance for the foreground and background. Relative luminance is a value from zero for black to one for white, derived from linearized sRGB channels and weighted to reflect the eye’s different sensitivity to red, green, and blue light. The contrast ratio compares the lighter luminance with the darker luminance and ranges from 1:1 for identical colors to 21:1 for black and white. The displayed ratio is rounded to two decimal places, while every pass decision uses the full unrounded value. That distinction matters near a threshold: a value that merely displays as 4.50 must not pass if its actual value remains below 4.5. The response groups four booleans under normal text and large text, making it straightforward to show a result to a designer or assert a policy in a test suite without interpreting prose.
Provide valid colors and interpret AA and AAA
Enter foreground and background as three-digit or six-digit hexadecimal colors, with or without the leading hash. Values such as #123, 123, #112233, and AABBCC are accepted and normalized. CSS color names, rgb() expressions, hsl() expressions, alpha-bearing four-digit or eight-digit hex values, empty strings, and non-string values are rejected because opacity and compositing would require additional context. For normal text, WCAG AA requires a contrast ratio of at least 4.5:1 and AAA requires at least 7:1. For large text, AA requires at least 3:1 and AAA requires at least 4.5:1. Large text generally refers to text at least 18 point regular or 14 point bold, but this capability does not inspect typography; it reports both matrices so your application can choose the relevant one. A passing number is one part of an accessibility review, not proof that an entire interface is accessible. Font weight, size, state changes, images, gradients, transparency, and user interaction still need appropriate testing.
Use contrast results in design and delivery workflows
Start with the actual foreground and background tokens used by a component rather than colors sampled from a compressed screenshot. For a manual review, compare body text, secondary text, links, button labels, form hints, focus indicators, and disabled states against every surface on which they appear. In a design system, build a small matrix of approved token pairs and call the API whenever a palette changes. A continuous integration check can reject a proposed body-text pair when normal-text AA is false, while allowing a decorative or large-display combination under a separately documented rule. Store the normalized colors and ratio with the test output so reviewers can reproduce the finding. Each automated request costs $0.002; invalid input returns an error rather than a misleading pass. The algorithm uses no network access, randomness, or current time, so the same pair always produces the same result. If the design includes transparency, a gradient, a photograph, or colors that change on hover, first determine the effective rendered colors or use a browser-based accessibility audit that can inspect the complete page context.
What you can do with it
Review design tokens
Compare text and surface tokens before publishing a light or dark theme.
Protect pull requests
Fail an automated check when a changed color pair no longer meets the required text threshold.
Document accessibility findings
Add normalized colors, luminance values, and a reproducible ratio to an issue or audit report.
FAQ
What color formats can I use?
Use three-digit or six-digit hexadecimal colors, with or without a leading hash. Alpha, named colors, rgb(), and hsl() are not accepted.
Which WCAG thresholds are applied?
Normal text uses 4.5:1 for AA and 7:1 for AAA. Large text uses 3:1 for AA and 4.5:1 for AAA.
Are pass results based on the rounded ratio?
No. The ratio is rounded only for display; pass indicators compare the full calculated value with each threshold.
Does a passing pair make my page WCAG compliant?
No. It confirms only the contrast calculation for the two supplied opaque colors. A complete accessibility review covers much more.
How much does an API request cost?
Each API request costs $0.002. You can also use the browser calculator for individual interactive checks.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/color/contrast-ratio-wcag \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"foreground":"#111827","background":"#ffffff"}'const res = await fetch("https://api.kit.forhosting.com/color/contrast-ratio-wcag", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"foreground": "#111827",
"background": "#ffffff"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/color/contrast-ratio-wcag",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"foreground": "#111827",
"background": "#ffffff"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/color/contrast-ratio-wcag", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"foreground":"#111827","background":"#ffffff"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"foreground":"#111827","background":"#ffffff"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/color/contrast-ratio-wcag", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"foreground": "#111827",
"background": "#ffffff"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "color.contrast_ratio_wcag",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |