WCAG 1.4.3 Contrast (Minimum): ratios and tests
WCAG 1.4.3 Contrast (Minimum) is a Level AA success criterion: text and images of text need a contrast ratio of at least 4.5:1 against their background, or 3:1 for large text (18 point, or 14 point bold). Text in logos, inactive controls, pure decoration and incidental text in pictures has no contrast requirement.
Published . Last reviewed .
Low contrast text is the most common failure in WebAIM's automated scan of the top million home pages. This page covers the thresholds in CSS pixels, each exception and where people misread it, the formula with working code, real failing and passing CSS, how to measure text over photos, gradients and transparency, the false results tools produce, and exactly what GotAlt's contrast tools measure and cannot see.
What WCAG 1.4.3 requires
Success criterion 1.4.3 asks that "the visual presentation of text and images of text has a contrast ratio of at least 4.5:1" (W3C, WCAG 2.2), with a lower 3:1 minimum for large-scale text and two exceptions. It is Level AA, has been in WCAG since WCAG 2.0 (December 11, 2008), and is kept in WCAG 2.1 and 2.2. Its Level AAA counterpart, 1.4.6 Contrast (Enhanced), raises the bar to 7:1 and 4.5:1.
Because it is Level AA, 1.4.3 is inside the targets the laws in our register name: WCAG 2.1 AA for the ADA Title II web rule, WCAG 2.0 AA for Section 508 and Ontario's AODA, and WCAG 2.1 AA through EN 301 549 for the European Accessibility Act. Our laws hub covers each. This page explains a technical standard and is not legal advice.
| What it applies to | Level AA (1.4.3) | Level AAA (1.4.6) |
|---|---|---|
| Normal text: under 24px, or under about 18.7px if bold | 4.5:1 | 7:1 |
| Large text: 24px and up, or about 18.7px and up if bold | 3:1 | 4.5:1 |
| Images of text | Same as text of the same size | Same as text of the same size |
| Logos and brand names | No requirement | No requirement |
| Inactive controls, pure decoration, invisible text, incidental text in a picture | No requirement | No requirement |
| Icons, input borders, focus rings, chart lines | Not 1.4.3: 1.4.11 Non-text Contrast asks 3:1 | No higher requirement |
How often 1.4.3 fails
In the 2026 WebAIM Million (data collected February 2026), low contrast text below the WCAG 2 AA thresholds was found on 83.9% of the top million home pages, up from 79.1% in 2025. On average each home page had 34 distinct instances of low-contrast text. No other failure category WebAIM reports is as widespread; missing alt text, the next, was on 53.1% (see our WCAG 1.1.1 guide and the most common accessibility errors).
The practical upside: contrast failures usually trace back to a small number of colors in a stylesheet. Fixing the source color, a CSS variable or a design token, fixes every place that uses it.
What counts as large text, in CSS pixels
WCAG defines large scale text as "at least 18 point or 14 point bold". In CSS, a point is 1/72 inch and a pixel 1/96 inch, so:
- 18pt = 24px. Text 24px or larger is large at any weight.
- 14pt = 18.67px. Bold text from about 18.7px up is large. W3C's Understanding document approximates this as "approximately 18.5px"; our bookmarklet uses the exact 18.67px.
Points that cause most of the mistakes:
- Bold has no number in WCAG. Our bookmarklet treats a computed
font-weightof 700 or more as bold, so a 20px semibold (600) label is normal text in our results and needs 4.5:1. - Size is measured as delivered. The glossary says the size "does not include resizing that may be done by a user", so browser zoom does not turn small text into large text.
- Responsive sizes change the category. A heading that is 32px on desktop and 20px regular on a phone is large text on one and normal text on the other. It needs 4.5:1 to pass at every width.
- Thin fonts are a trap. WCAG's own note warns that fonts with "extraordinarily thin strokes" are harder to read at lower contrast. A 24px weight-300 heading meets the definition of large text and still reads poorly at 3:1. Aim higher for light weights.
The exceptions, and where they get misread
The normative text gives two exceptions besides large text. Each is narrower than it is often treated.
Incidental text
Text has no contrast requirement when it is part of an inactive user interface component, is pure decoration, is not visible to anyone, or is part of a picture that contains significant other visual content (WCAG 2.2). In practice:
- Inactive components means genuinely disabled ones, such as a button with the
disabledattribute. A button styled gray but still clickable is active, and its text must pass. - Pure decoration is strict. W3C's Understanding document says text is only purely decorative "if the words can be rearranged or substituted without changing their purpose". Faint oversized words in a background pattern can qualify. A pale "Step 2 of 4" cannot.
- Not visible to anyone covers text hidden from sight, such as screen-reader-only text that is clipped off screen. Tools that flag its color are reporting a false positive.
- Part of a picture covers text that happens to appear in a photo, such as a shop sign in a street scene. In our reading, a headline you place over a photo so people will read it is not incidental: it is the content, and W3C's failure technique F83 covers text over background images.
Logotypes
"Text that is part of a logo or brand name has no contrast requirement" (WCAG 2.2). The logo is exempt; the navigation links, buttons and slogan in body text around it are not.
What is not an exception
None of the exceptions mentions placeholder text, link states or error messages. W3C's Understanding 1.4.3 says the requirement covers text "including placeholder text and text that is shown when a pointer is hovering over an object", so placeholder text is measured like any other. Hover, focus, visited and selected states each present text, so each color pair needs to pass, not only the resting state.
How the contrast ratio is calculated
Our color contrast guide walks through the formula step by step. In short, WCAG defines the contrast ratio as (L1 + 0.05) / (L2 + 0.05), where L1 is the relative luminance of the lighter color and L2 of the darker. Ratios run from 1:1 (identical colors) to 21:1 (black on white). Relative luminance linearizes each sRGB channel and then weights them, green most heavily and blue least. This section adds what decides borderline results: exact ratios for real pairs near the thresholds, and code you can run.
Never round up
W3C's Understanding document is explicit that computed values "should not be rounded (e.g., 4.499:1 would not meet the 4.5:1 threshold)" (Understanding 1.4.3). A tool that displays one decimal place can turn a fail into an apparent pass. The pairs below were computed with the function below; several sit right on a threshold.
| Text on background | Exact ratio | Normal text (4.5:1) | Large text (3:1) |
|---|---|---|---|
#777777 on #FFFFFF | 4.478:1 | Fails | Passes |
#767676 on #FFFFFF | 4.542:1 | Passes | Passes |
#959595 on #FFFFFF | 2.995:1 (shows as "3.00" when rounded to two decimals) | Fails | Fails |
#949494 on #FFFFFF | 3.034:1 | Fails | Passes |
#FFFFFF on #16A34A | 3.296:1 | Fails | Passes |
#FFFFFF on #15803D | 5.016:1 | Passes | Passes |
#FFFFFF on #3B82F6 | 3.678:1 | Fails | Passes |
#FFFFFF on #2563EB | 5.169:1 | Passes | Passes |
#AAAAAA on #FFFFFF | 2.323:1 | Fails | Fails |
Our color contrast checker shows the ratio rounded down to two decimals and decides pass or fail on the exact value, so a borderline pair can read 4.49 and fail.
The calculation in code
The whole calculation fits in a few lines of JavaScript:
function channel(v) {
const c = v / 255;
return c <= 0.04045
? c / 12.92
: ((c+0.055)/1.055) ** 2.4;
}
function luminance([r, g, b]) {
return 0.2126 * channel(r)
+ 0.7152 * channel(g)
+ 0.0722 * channel(b);
}
function contrast(a, b) {
const la = luminance(a);
const lb = luminance(b);
const hi = Math.max(la, lb);
const lo = Math.min(la, lb);
return (hi+0.05) / (lo+0.05);
}
const gray = [119, 119, 119];
const white = [255, 255, 255];
contrast(gray, white);
// 4.478..., so #777777 fails
The threshold 0.04045 replaced 0.03928 in May 2021; WCAG notes the change has no practical effect on results.
Failing and passing CSS
Gray body text
Failing on a white background. One step lighter than the threshold:
p {
color: #777777; /* 4.48:1 */
}
Passing. One step darker is enough:
p {
color: #767676; /* 4.54:1 */
}
A brand-colored button
Failing at 16px. White on this green is 3.30:1, which only large text may use:
.btn {
background: #16a34a;
color: #ffffff;
font-size: 16px;
}
Passing. A darker shade of the same hue reaches 5.02:1:
.btn {
background: #15803d;
color: #ffffff;
font-size: 16px;
}
Text color with no background color (F24)
Failing, per F24. The text color is set but the background is left to the visitor's default. Someone whose browser or system default background is black gets #333333 on black, about 1.66:1:
body {
color: #333333;
}
Passing. Specify both, as a pair:
body {
color: #333333;
background-color: #ffffff;
}
WCAG's glossary assumes white when no background is specified, but its next note and F24 still treat setting one color without the other as a failure, because the visitor's own default is unknown.
Transparent text
Failing. Black at 50% opacity on white paints as #7F7F7F or #808080 depending on how the browser rounds, 4.00:1 or 3.95:1: a fail either way.
.meta {
color: rgba(0, 0, 0, 0.5);
}
Passing. At 60% it paints as #666666, 5.74:1:
.meta {
color: rgba(0, 0, 0, 0.6);
}
Watch for opacity on a parent too. .card.is-muted { opacity: 0.5; } fades every piece of text inside the card, and a checker that reads only the text's own color will not see it.
White text over a photo
Failing. Wherever the photo is pale, such as sky or sand, the white text has little or no contrast:
.hero {
background: url(beach.jpg);
background-size: cover;
color: #ffffff;
}
Passing. Put a semi-transparent dark panel behind the text. Black at 55% opacity darkens even a pure white pixel to about #737373, so white text on it measures at least 4.74:1 wherever it lands. At 50% the worst case is 3.95:1, which fails for normal text.
.hero-text {
background: rgba(0, 0, 0, 0.55);
color: #ffffff;
}
Placeholder text
Failing. A pale placeholder that carries an instruction, at 2.32:1:
input::placeholder {
color: #aaaaaa;
}
Passing:
input::placeholder {
color: #767676;
}
How to test WCAG 1.4.3, step by step
- List every distinct text style: body, links, navigation, buttons, form labels, placeholders, error and success messages, badges, footer text, and text inside images. Each color pair is one test, however many times it repeats.
- Read the specified colors, not screenshot pixels. W3C notes that "due to anti-aliasing, particularly thin or unusual fonts may be rendered by user agents with a much fainter color", and advises using colors from the user agent or the stylesheets rather than the text as shown on screen (Understanding 1.4.3). An eyedropper on a letter's soft edge reads a lighter color than the one specified.
- Get the size and weight at each breakpoint to decide whether 4.5:1 or 3:1 applies.
- Compute the ratio without rounding, in the contrast checker or with the function above.
- Blend transparency first. Semi-transparent text or panels must be blended with what is behind them before measuring.
- Measure text over images and gradients at its worst point. W3C's F83 test starts with the area of the image that is darkest, for dark text, or lightest, for light text; if that fails, check the background behind each letter. Use the image contrast checker on a screenshot, and repeat at phone and desktop widths, because
background-size: covercrops the photo differently at each. - Test every state and theme: hover, focus, visited, selected, error, and dark mode if you offer it.
- Sweep the live page with the accessibility bookmarklet to catch the pairs you did not list, including content added by scripts and pages behind a login.
For steps 2 and 3, select an element in your browser's developer tools and run this in the console, where $0 is the selected element:
const s = getComputedStyle($0);
console.log(s.color,
s.backgroundColor,
s.fontSize, s.fontWeight);
A background of rgba(0, 0, 0, 0) means the element is transparent: select its parent and repeat until you reach a solid color, or an image that needs step 6. Our guide to testing website accessibility fits this into a full manual pass.
Common false positives and false negatives
| Situation | What tools tend to report | What is actually true |
|---|---|---|
| Screen-reader-only text clipped off screen | Low contrast | Not visible to anyone, so exempt. |
| Disabled button | Low contrast | Inactive components are exempt. GotAlt's bookmarklet skips them. |
| Text inside a logo | Low contrast | Exempt as a logotype. |
| Eyedropper on an anti-aliased letter edge | Fail | Often a pass: measure the specified color instead. |
| A ratio of 4.48:1 shown as "4.5" | Pass | Fail: ratios are never rounded up. |
| Text over a background image | Pass or fail against the fallback background-color | Unknown until the image itself is measured (F83). |
| Text over a gradient | Measured against one color or skipped | Must pass at the weakest part of the gradient behind the text. |
| Hover and focus colors | Not tested | In scope: each state presents text. |
| Text with an outline or shadow | Shadow ignored | WCAG's contrast ratio note says a narrow border around a letter counts as the letter and a wide halo counts as background, so measure what is actually adjacent. |
| A dark theme | Only the default theme tested | Each theme the site offers is a separate set of color pairs. |
What GotAlt's tools measure, and what they cannot see
Contrast needs rendered colors. Our site scanner and deep audit read the HTML a server sends, which contains no computed styles, so neither checks contrast; our methodology explains why. Three free tools cover 1.4.3 instead, each from a different angle.
| Tool | What it measures | What it cannot see |
|---|---|---|
| Contrast checker | One color pair you enter, with optional text opacity blended over a solid background. Gives the WCAG ratio, AA and AAA results, and the nearest passing color. Runs in your browser. | Your live page; opacity on a parent element, blend modes, filters, text shadows, gradients and images; which size category or exception applies to your design. WCAG 2 formula only, not APCA. |
| Image contrast checker | Text on a photo, screenshot or gradient. You draw a box around the text; it reports the worst ratio, the 5th percentile, the median and the share of the background that fails, and passes only if every measured pixel does. The image never leaves your device. | Background that matches the text color, such as white text over a white sky, cannot be told from the letters and is not measured; only wide areas of it are flagged. Fine texture near letters can be set aside as anti-aliasing. Gradient, outlined or shadowed text; whether the box holds text at all. A screenshot can differ from the live page, so recheck borderline results there. Boxes over 1,000,000 pixels are sampled on a grid. |
| Accessibility bookmarklet | The rendered text on any page you open, including staging and logged-in pages: each text color against the background found by walking up its ancestors, at 4.5:1 or 3:1 for large text (24px, or 18.67px at weight 700 and up). Skips disabled controls and text hidden with opacity: 0. Up to 3,000 text elements per run. | Text over images, gradients, video or overlays is listed for a manual check rather than guessed. Text shadows, filters, blend modes, placeholder text and CSS-generated text are not measured. It sees the page once, in its current state, so hover and focus colors need a separate look. |
A passing measurement is a measurement of the colors checked, not a statement that a page meets 1.4.3 or any law. The right size category, the exceptions and states that only appear on interaction still need a person's judgment. Contrast is also only one criterion: our page on why rule scanners miss issues covers the rest of the gap.
Related criteria and further reading
- 1.4.6 Contrast (Enhanced) (AAA): 7:1, or 4.5:1 for large text.
- 1.4.11 Non-text Contrast (AA): 3:1 for icons, input borders, focus indicators and meaningful graphics.
- 1.4.1 Use of Color (A): color alone must not carry meaning, which matters for links set apart from body text only by color.
- 1.4.5 Images of Text (AA): real text is easier to keep readable than text baked into a picture.
Our color contrast guide covers choosing an accessible palette. For every criterion in one place, see WCAG 2.2 criteria explained and the WCAG 2.2 checklist. Theme and template notes are in our guides for Shopify, WordPress, Wix, Squarespace and Webflow.
Questions about WCAG 1.4.3
Is WCAG 1.4.3 Level A or AA?
Level AA. Its AAA counterpart is 1.4.6 Contrast (Enhanced), which asks for 7:1, or 4.5:1 for large text.
What is large text in pixels for WCAG 1.4.3?
24px or larger at any weight, or about 18.7px or larger in bold. Those are WCAG's 18 point and 14 point bold converted at 96 pixels per inch. Large text needs 3:1; everything else needs 4.5:1.
Does a contrast ratio of 4.48:1 pass?
No. W3C says computed ratios should not be rounded, and gives 4.499:1 as an example that does not meet 4.5:1. #777777 on white is 4.48:1 and fails; #767676 is 4.54:1 and passes.
Do logos and disabled buttons need to meet 4.5:1?
No. Text that is part of a logo or brand name, and text in an inactive control such as a disabled button, has no contrast requirement under 1.4.3. A button that only looks disabled but still works is not exempt.
Does 1.4.3 apply to icons and input borders?
No, those fall under 1.4.11 Non-text Contrast, which asks 3:1 for user interface components and graphics needed to understand the content. 1.4.3 covers text and images of text.
Does placeholder text need 4.5:1?
Yes. W3C's Understanding 1.4.3 says the criterion applies to text in the page "including placeholder text", so a placeholder is measured like any other text.
Does APCA or WCAG 3 replace the 4.5:1 rule?
No. WCAG 3 is a W3C Working Draft, and W3C's introduction to WCAG 3 says it will not supersede WCAG 2. No law references it, and 1.4.3's ratios are the ones the laws in our register point to.
Check the rest of your site for free
Contrast needs the tools above. The free scan covers other WCAG failures: 16 rule-based checks on up to 3 pages, including missing alt text, form labels, headings, page language and link text. No signup.
Scan my site for free See a real report first