Check color contrast ratio
This color contrast checker compares a foreground color with a background color using the WCAG relative luminance formula.
Run — free
Enter either three-digit or six-digit hexadecimal colors to receive the contrast ratio and clear pass or fail results for AA normal text, AA large text, AAA normal text, and AAA large text. It is useful for checking design tokens, reviewing interface components, documenting accessibility decisions, and catching unreadable color combinations before they reach users.
Read the contrast result correctly
The returned ratio describes the difference in relative luminance between the lighter and darker colors. A result of 1:1 means the colors have identical luminance, while the maximum possible result is 21:1 for black against white. WCAG uses different thresholds because larger or heavier text remains readable at a lower contrast than ordinary body copy. AA requires at least 4.5:1 for normal text and 3:1 for large text. AAA raises those thresholds to 7:1 and 4.5:1 respectively. The response evaluates every threshold separately, so you do not need to interpret one general pass flag. In WCAG terminology, large text is at least 18 point, or at least 14 point when bold. Do not round a displayed result upward when making a compliance decision. This checker compares the full-precision computed ratio with each threshold, even though it presents the ratio itself to two decimal places for a clean, stable result. That distinction matters for colors that sit extremely close to a boundary.
Choose the actual foreground and background
Test the colors that a person really sees, not merely two entries that happen to be adjacent in a design palette. For body copy, the foreground is the rendered text color and the background is the final solid color immediately behind it. Check links in every relevant state, including default, visited, hover, focus, and disabled appearances when those states still communicate information. Also test text inside buttons, badges, alerts, form controls, charts, and navigation elements. This capability accepts opaque hexadecimal colors in #RGB or #RRGGBB form. If an interface uses transparency, gradients, images, blending modes, shadows, or overlays, first determine the resulting rendered colors at the point being assessed. A single pair cannot represent every pixel of a varied background, so test the lowest-contrast region rather than an average or convenient sample. Contrast is also only one part of accessible presentation: focus visibility, non-text contrast, font choice, spacing, and avoiding color as the sole carrier of meaning still require separate review.
Build contrast checks into a reliable workflow
Use the checker early, when changing a token or component is inexpensive, and repeat the check whenever themes or brand colors change. Designers can compare candidate pairs before handing off a screen. Developers can call the same deterministic calculation from tests or release checks and store the returned flags with component snapshots. A practical token audit evaluates each semantic foreground against every background on which the token is permitted, rather than testing only one showcase combination. Keep the intended text size and weight next to each result because a pair that passes AA for large text may still fail ordinary labels or paragraphs. The API costs $0.002 per request, while the browser runner supports quick interactive checks. Neither path uses a network lookup to interpret the colors, and identical inputs produce identical output. Malformed colors are rejected instead of guessed, which helps expose missing hash marks, invalid hexadecimal digits, unsupported alpha channels, and accidental CSS names before an unreliable result enters an accessibility report. Treat a passing ratio as evidence for that exact pair and usage, not as blanket certification of an entire page.
What you can do with it
Review a design handoff
Confirm that proposed text and surface colors meet the intended WCAG level before implementation begins.
Audit design tokens
Test semantic foreground tokens against every background token allowed by a component system.
Protect a release pipeline
Call the deterministic checker from automated tests and fail changes that move required color pairs below their thresholds.
FAQ
Which color formats are accepted?
Use opaque hexadecimal notation in #RGB or #RRGGBB form. CSS names, alpha channels, rgb() functions, gradients, and malformed values are rejected.
What are the WCAG AA thresholds?
AA requires a ratio of at least 4.5:1 for normal text and at least 3:1 for large text.
What are the WCAG AAA thresholds?
AAA requires at least 7:1 for normal text and at least 4.5:1 for large text.
Why can the displayed ratio look like a threshold but fail?
The ratio is displayed to two decimal places, but pass decisions use the unrounded calculation so a value just below a threshold is not promoted by display rounding.
Does swapping foreground and background change the ratio?
No. The contrast formula compares the lighter and darker luminance, so reversing two opaque colors produces the same ratio and pass results.
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/web/color-contrast-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"foreground":"#1a1a1a","background":"#ffffff"}'const res = await fetch("https://api.kit.forhosting.com/web/color-contrast-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"foreground": "#1a1a1a",
"background": "#ffffff"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/color-contrast-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"foreground": "#1a1a1a",
"background": "#ffffff"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/color-contrast-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"foreground":"#1a1a1a","background":"#ffffff"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"foreground":"#1a1a1a","background":"#ffffff"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/color-contrast-check", 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": "#1a1a1a",
"background": "#ffffff"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.color_contrast_check",
"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.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
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. |