Your text needs a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text to meet WCAG 2.2 AA. Non-text elements like icons and borders need 3:1 too. AAA pushes this to 7:1 and 4.5:1 respectively, worth chasing if you serve users with low vision. Right now, paste your colour pair into a contrast checker or run an automated scan across your site to find where you stand.
TL;DR:
- Most contrast failures stem from choosing unsuitable palette roles, not from the core color itself, emphasizing the need for tonal ranges rather than fixed hues.
- Automated tools can identify contrast issues across entire sites, but manual testing remains essential for images, gradients, and dynamic content.
- Focus on fixing contrast at the design token level rather than individual components to prevent regression and ensure consistency across themes.
- Color tests should be repeated on actual hardware and in various lighting conditions, especially for dark mode and high contrast settings, to ensure real-world accessibility.
- Relying solely on hue differences for accessibility risks failure for color-blind users; pairing colors with icons, labels, or patterns ensures better distinction.
Table of Contents
- What WCAG requires: exact ratios and the “large text” rule
- How to check colour contrast: tools and workflows
- Non-text contrast: what to measure beyond body text
- Common failures and practical fixes designers can apply now
- Testing across themes, images and dynamic content
- Practical workflow from Kukoo: how an agency fixes contrast at scale
- Impact of different types of colour blindness and how to accommodate them in contrast design
- Testing contrast in realistic usage scenarios including varying ambient lighting and device settings
- Guidance on balancing colour contrast with brand colour palettes without compromising accessibility
- The gap between the WCAG spec and what actually gets shipped
- How Kukoo helps you build accessible colour systems
- Sources
What WCAG requires: exact ratios and the “large text” rule
Colour contrast accessibility comes down to three numbers you need memorised. WCAG 2.2 Level AA sets 4.5:1 for normal text, 3:1 for large text, and 3:1 for user interface components and graphical objects. These map to success criteria 1.4.3 (text contrast), 1.4.6 (the AAA equivalent), and 1.4.11 (non-text contrast). AAA raises the bar considerably: 7:1 for normal text, 4.5:1 for large text. Most product teams target AA. Government and healthcare services, where legibility failures carry real consequences, often push for AAA on body copy.

“Large text” isn’t a vague design term. WCAG defines it as 18 point (roughly 24px) regular weight, or 14 point (about 18.66px) when bold. Bold weight gets a lower threshold because thicker strokes are inherently easier to read at lower contrast, so the standard compensates for that.
The maths behind pass or fail is a luminance ratio: (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter colour’s relative luminance and L2 the darker. This is why two greys that look distinct on screen can still fail. The formula weights luminance, not perceived brightness or hue, so a colour shift that feels dramatic to the eye might barely move the ratio, while a subtle tonal change can tip a pairing from pass to fail.
How to check colour contrast: tools and workflows
Testing colour contrast happens at three different scales, and you need a method for each.
For a single colour pair, paste the hex codes into a contrast checker or use your browser’s DevTools colour picker, which usually shows the ratio and a pass/fail badge as you adjust values. This is the fastest sanity check when you’re deciding between two shades in a mock-up.
For a live page, sample colours directly rather than trusting your design file, because rendered colour can shift slightly through compression, filters, or CSS opacity. Use an eyedropper tool and, for text sitting over an image, sample multiple points across the image rather than one average, since a hero photo might be light in one corner and dark in another.
For a whole site, automated scanning is the only realistic option. Axe-core and Lighthouse both flag contrast failures at scale, but they can’t reliably judge text over gradients or photography, so manual review still matters for those cases.
Folding this into a working process looks like:
- Update your design tokens first, not individual components.
- Test each component that consumes the token, in every state.
- Gate merges with an automated check such as axe-core in CI.
- Re-check across every theme your product ships (light, dark, high contrast).
A workflow like this catches most regressions before they reach production, and it’s far cheaper than finding them after launch through an accessibility audit or a complaint.
Non-text contrast: what to measure beyond body text
Success criterion 1.4.11 covers everything that isn’t text but still communicates something visually. Button borders, form field outlines, icons that convey meaning, toggle switches, and focus indicators all need 3:1 contrast against their adjacent colours.
Every interactive state needs checking separately:
- Default state
- Hover state
- Focus state
- Active/pressed state
A button that passes at rest but drops to 2.1:1 on hover still fails. There are sensible exceptions: disabled elements, logotypes, and purely decorative graphics fall outside 1.4.11’s scope, since they aren’t conveying functional information.
Pro Tip: Focus rings are the most commonly missed non-text element. Test them against every background they might sit on, light and dark, because a ring that’s visible on white can vanish entirely on a mid-grey card.
Common failures and practical fixes designers can apply now
Low-contrast text remains the single most widespread accessibility failure on the web — a reason why web accessibility boosts inclusion and SEO impact for many businesses. Roughly 83.9% of home pages still fail basic contrast requirements, which tells you this isn’t an edge case; it’s the default outcome when contrast isn’t checked deliberately.

The fix that actually holds is changing the source, not the symptom. Update the design token, not the dozens of components that reference it. A11yFlow’s guidance on token-level fixes is worth following precisely because it prevents the same failure reappearing three sprints later in a component nobody remembered to update.
Some practical notes worth keeping close:
- The CSS
contrast-color()function can pick black or white automatically for a dynamic background, but treat it as a starting point. It doesn’t guarantee a 4.5:1 pass, so verify the output rather than trusting the helper blindly. - For dark themes, avoid pure black and pure white. Off-black backgrounds paired with off-white text tend to reduce eye strain while still clearing contrast minima comfortably.
- Check your focus rings against every surface they land on, not just the default background.
A simple checklist keeps this from slipping: change the token, review every component that consumes it, run automated and manual re-tests, then gate the change in CI so a regression can’t merge silently.
Testing across themes, images and dynamic content
Dark mode is where most contrast systems quietly fall apart. A palette built and tested in light mode almost never survives inversion untouched, so test each theme as a separate system rather than assuming one contrast audit covers both.
Images and gradients need a sampling strategy rather than a single spot check. Sample several points across a background image, and remember that overlays and text shadows change the measured contrast even when the underlying colours look unchanged in your design file.
Dynamic content adds another layer of risk:
- User-generated colours (avatar badges, status tags) need runtime verification, not a one-off design check.
- Helper functions like
contrast-color()need their actual output tested, not assumed. - Animated transitions should be checked at their darkest and lightest frames, since a colour that passes mid-transition might fail at either end.
Practical workflow from Kukoo: how an agency fixes contrast at scale
When we audit a brand or website build, colour tokens get checked before a single component ships. We test primary and secondary palettes against both light and dark backgrounds, flag any pairing under 4.5:1, and rework the token rather than patching individual pages. Over a decade of client work has taught us that most contrast failures trace back to a palette chosen for its look, never tested against real usage.
If you have design and front-end resource in-house, a good contrast checker and a CI gate will cover most cases. If you’re rebuilding a brand from scratch, pairing accessible colour choices with your identity work from the start saves a rebuild later, a lesson worth reading in our guide to choosing brand colours for maximum impact.
Impact of different types of colour blindness and how to accommodate them in contrast design
Roughly one in twelve men and one in two hundred women have some form of colour vision deficiency, most commonly red-green confusion (deuteranopia and protanopia), with blue-yellow deficiency (tritanopia) and total colour blindness being far rarer.
Here’s the reassuring part: because WCAG measures luminance rather than hue, a well-executed light versus dark contrast strategy already covers most colour-blind users without needing separate colour-blind-specific palettes. If a red error state and a green success state differ enough in lightness, someone with deuteranopia can still distinguish them even without perceiving the hue difference clearly.
Where teams go wrong is relying on hue alone to carry meaning. A red “error” badge and a green “success” badge that share similar luminance will look nearly identical to someone with red-green colour blindness, regardless of how distinct they appear to you. The fix isn’t complicated: pair colour with a second signal. Add an icon, a label, or a pattern, and choose colours far enough apart in lightness that the distinction survives desaturation. Running your interface through a greyscale filter is a quick, low-effort test. If two states become indistinguishable in greyscale, colour alone was doing all the work, and that’s a fragile design decision.
Testing contrast in realistic usage scenarios including varying ambient lighting and device settings
A contrast ratio calculated on your monitor in a dim studio doesn’t necessarily hold up on a phone screen in direct sunlight. Ambient light washes out subtle tonal differences long before it affects anything a calculator measures, which is why designers who only test indoors under controlled lighting routinely overestimate how legible their interface actually is outdoors.
Device settings compound this. Many phones ship with “true tone” or blue-light filtering enabled by default, subtly shifting how every colour on screen renders. A pairing that clears 4.5:1 in a design tool can read differently once a device’s colour profile, brightness curve, or accessibility display setting is layered on top.
The practical response is to test on actual hardware, not just simulators. Check your interface outdoors on a mid-range phone at midday, then again indoors under warm artificial light in the evening. Turn on your device’s high-contrast or “reduce transparency” settings and confirm nothing collapses. If your product supports a genuine high contrast design mode, that’s worth building for users who need every extra decibel of legibility, particularly people with low vision using assistive display settings on top of your existing palette. None of this replaces the calculator-based check; it’s a second pass that catches what the maths alone can miss.
Guidance on balancing colour contrast with brand colour palettes without compromising accessibility
Brand teams often treat accessibility as a constraint fighting against identity. In practice, the tension is smaller than it looks, and the fix rarely means abandoning a signature colour.
Most brand palettes fail contrast not because the core colour is wrong, but because it’s used in the wrong role. A vibrant brand blue might be perfect for a hero background or a logo, and still be unusable as body text on white, simply because it sits in the wrong luminance range for that job. The fix is building a tonal scale around each brand colour rather than using one flat hex value everywhere. Keep the saturated version for large surfaces and marks, and derive a darker or lighter variant, still clearly “on brand”, for anywhere it needs to carry text-level contrast.
This is precisely where accessible colour palettes and brand identity work overlap, and it’s worth treating as one exercise rather than two separate reviews. A palette re-derived this way, one core hue with several accessible tonal variants, protects recognition while covering every contrast requirement your interface throws at it. Trying to force a single brand hex to do every job on the page is the more common route to failure, not the accessibility rules themselves.
The gap between the WCAG spec and what actually gets shipped
Most contrast advice online stops at reciting the ratios, as if knowing “4.5:1” solves the problem. It doesn’t. The gap between reading the spec and shipping compliant interfaces is almost entirely a process gap, not a knowledge gap. Teams know the numbers. They lack a workflow that catches drift before it reaches production.
The conventional wisdom that a one-off audit “fixes” contrast is where most projects quietly fail again within a few months. Colours drift through new components, new themes, new marketing pages built outside the design system. Without a token-level source of truth and an automated gate, the fix decays. That’s the part most guides skip.
If there’s one thing worth prioritising above the ratios themselves, it’s this: build contrast checking into where colours are defined, not where they’re used. Fix the token once, and every component downstream inherits the fix. Chase individual pixel failures across a live site, and you’ll be doing this forever.
— Kukoo
How Kukoo helps you build accessible colour systems
Kukoocreative is the practical alternative to guessing your way through contrast fixes page by page. Rather than patching individual buttons after a complaint or a failed audit, we build accessible ratios into your colour tokens from the start, so every component that inherits the palette inherits the fix too.

A typical engagement starts with a colour audit across your existing brand and site, flagging any pairing under 4.5:1 and mapping out a tonal scale that protects your identity while clearing every WCAG threshold. From there, we rebuild the affected components, test across light and dark themes, and hand over a palette your team can maintain without re-litigating the same fix every quarter. If your brand colours need a proper review rather than a patch job, our logo design brief is the natural starting point, and our portfolio shows how this looks across real client builds. Get in touch and we’ll scope what an accessible colour system looks like for your brand.
Sources
- Ensure text and controls have enough color contrast — Western Washington University
- Color Contrast WCAG 2.2: A Developer’s Fix Guide — A11yFlow
- Automated testing using axe-core and pa11y — DWP accessibility manual
- Contrast and Color Accessibility — WebAIM