WCAG contrast ratio checker
See whether text stays readable on its background by WCAG rules: #111827 on white reaches 17.74 to 1, passing AA and AAA for both normal and large text. A number your accessibility tickets can quote. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Foreground
- #111827
- Background
- #ffffff
- Contrast ratio
- 17.74
This WCAG contrast ratio checker takes two hex colors—foreground and background—and returns the official contrast ratio used by Web Content Accessibility Guidelines, together with clear AA and AAA pass or fail flags for both normal text and large text. Designers, developers, and auditors use that ratio to decide whether body copy, buttons, labels, and UI chrome remain readable for people with low vision or color-vision differences. The engine follows the WCAG 2.x relative-luminance formula: each hex channel is linearized from sRGB, luminance is weighted with the standard 0.2126, 0.7152, and 0.0722 coefficients, and the contrast ratio is (L lighter plus 0.05) divided by (L darker plus 0.05). Thresholds are unrounded: normal text needs at least 4.5 for AA and 7 for AAA; large text needs at least 3 for AA and 4.5 for AAA. Results are deterministic with no network calls, sampling, or date-dependent shortcuts.
What the contrast ratio tells you
Contrast ratio is the single number WCAG uses to describe how strongly text stands out from its background after accounting for human luminance perception rather than simple RGB distance. A pure black on pure white pair reaches twenty-one to one, the theoretical maximum for opaque sRGB colors, while identical colors yield one to one and fail every text criterion.
Large text in WCAG terms means at least eighteen point regular or fourteen point bold equivalent; the tool does not measure font size itself—it only applies the large-text thresholds so you can map the matrix to your type scale. There is no page crawl, CSS parsing, opacity blending, or gradient sampling: you supply two explicit hex colors and receive a reproducible matrix suitable for design-token CI, Figma plugin backends, and ticket comments that must cite a numeric ratio rather than a screenshot opinion.
How hex inputs are read and how AA and AAA stay reproducible
Both values must be hex: optional leading hash, either three digits that expand to six (#abc becomes #aabbcc) or six full digits, case-insensitive. Alpha suffixes, rgb() functions, hsl() functions, named CSS colors, and bare integers are rejected with a message that names the field and shows what was received, so a bad token never becomes a silent fail-open. Each color is capped at thirty-two characters to keep validation cheap and to block paste accidents.
After parsing, channels are linearized with the WCAG piecewise sRGB curve—divide by 12.92 below the 0.04045 threshold, otherwise raise ((c plus 0.055) over 1.055) to the power 2.4—then combined into relative luminance. The ratio uses the lighter luminance in the numerator; pass flags compare the unrounded floating-point ratio against 4.5, 7.0, and 3.0 so a displayed 4.50 cannot hide a true 4.499 that should fail AA.
Where contrast checks fit in design systems, UI reviews, and CI
Product teams meet contrast requirements when a brand palette looks elegant on a marketing slide but fails body text on a white card, when a dark mode theme is generated from light tokens, or when legal and compliance stakeholders ask for documented AA evidence before launch. Engineers wire the same contract into pull-request checks that block a token change if normal-text AA drops below 4.5 for primary body colors, or they log large-text AAA status for hero headlines that intentionally sit closer to the surface.
QA and accessibility specialists use the numeric ratio in bug reports so developers can fix the exact pair instead of guessing from a screenshot. Content and marketing teams verify that campaign hex codes still pass when placed on light or dark site backgrounds. Pair it with color conversion tools when you need RGB or HSL intermediates, and with full-page accessibility audits when you must scan live markup rather than two explicit hex values.
What you can do with it
Design-token and palette review
Check every foreground/background pair in a brand palette against AA and AAA before locking tokens for light and dark themes.
Accessibility bug reports
Attach a numeric contrast ratio and the AA/AAA matrix to tickets so engineers can fix the exact hex pair, not a vague screenshot.
FAQ
What does the WCAG contrast ratio checker cost?
It is free in your browser on this page.
What are the AA and AAA thresholds used here?
Flags use the unrounded ratio; the displayed ratio is rounded to two decimals.
Does this tool scan my website for contrast issues?
No. It only compares two hex colors you provide.
Why did a pair fail AA when it looks fine to me?
Human perception varies and ambient light differs. WCAG uses relative luminance on linearized sRGB, not perceived vividness. Trust the numeric ratio for compliance checks, then adjust hex values until the matrix passes.
Which color formats does the WCAG checker accept?
Hex colors only, with three or six digits, with or without the leading hash and in any case. Named colors, RGB or HSL notation and transparency are refused, so a wrong value never passes as a success.
Related tools
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 \
-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", {
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",
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", 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", 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",
"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
max_color_chars | 32 |
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. |
Technical notes
Use it free in the browser for palette reviews and design tokens, or call the API at $0.002 per successful request when you need the same matrix in CI, design systems, or automated accessibility gates.
What the WCAG contrast ratio measures and what the response contains
This checker normalizes each input hex to a canonical lowercase #rrggbb form and an rgb(r, g, b) echo, then returns contrast_ratio rounded to two decimal places for display, plus a wcag object with normal_text and large_text branches each exposing boolean aa and aaa fields. The method tag wcag_relative_luminance documents that the path is the published relative-luminance formula, not a perceptual delta-E or APCA experiment. Invalid hex codes, missing fields, non-string values, and overlong strings are rejected with invalid_input and are never billed.
How hex inputs are read and how AA and AAA stay reproducible
The preferred fields are foreground for the text or icon color and background for the surface behind it, but aliases fg, color1, text, and hex1 for the first color and bg, color2, and hex2 for the second are accepted so forms, query strings, and spreadsheets can reuse the same contract without renaming columns. Pair the free browser check on this page with the priced API when design-system pipelines need the identical mathematical result without re-implementing luminance math in every language.
Where contrast checks fit in design systems, UI reviews, and CI
Designers can paste hex pairs from Figma or a token JSON file and read the AA and AAA matrix before committing a color scale. On this page the work runs entirely in your browser; through the API each successful lookup costs $0.002, failed validation is not charged, and nothing is stored after the response returns. That split—free interactive exploration and a priced, identical API—matches the rest of the KIT tier-A catalog and lets you move from a palette workshop to an automated pipeline without changing the mathematical result.
Integration use cases
CI gates for UI colors
Call the API from pull requests to fail builds when body or button hex pairs drop below normal-text AA (4.5:1).
Technical questions
Which color formats are accepted?
Hex only: #RGB or #RRGGBB, with or without the leading hash, case-insensitive. rgb(), hsl(), named colors, and alpha hex are rejected with invalid_input.