WebTech1223411 logo WebTech1223411Web tech, read closely
Accessibility

Color Contrast Ratios Explained for Readable Web Text

The grey #777777 on a white background has a contrast ratio of 4.48 to 1. The grey #767676, one step darker and visually indistinguishable to most people, comes in at 4.54 to 1.

Two swatches of grey text on a white background with their contrast ratios shown beside them

The grey #777777 on a white background has a contrast ratio of 4.48 to 1. The grey #767676, one step darker and visually indistinguishable to most people, comes in at 4.54 to 1. One fails the WCAG minimum for body text and the other passes. That tiny gap shows why color contrast ratios matter: they turn "looks readable to me" into a number anyone on the team can check.

Low contrast text is consistently one of the most common accessibility failures found on home pages, ahead of missing alt text in many audits. It affects people with low vision, older users, anyone reading a phone in sunlight and anyone on a cheap, dim laptop screen. Here is how the ratio is calculated, which thresholds apply to what, and how to fix failures without wrecking a brand palette.

How Color Contrast Ratios Are Calculated

The ratio compares the relative luminance of two colors, which is roughly how bright each one appears to the human eye. The formula is (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter color's luminance and L2 the darker one. Results range from 1 to 1, the same color on itself, up to 21 to 1, pure black on pure white.

Luminance weights the three channels unevenly. Green contributes far more to perceived brightness than blue, which is why a saturated blue such as #0000FF reaches 8.6 to 1 on white while a bright green such as #00FF00 manages only about 1.4 to 1. Hue tells you very little; you have to measure.

The Thresholds That Apply

WCAG 2.2 sets different minimums depending on what the content is. The AA level is what most laws and policies reference.

ContentAA minimumAAA minimum
Body text and small labels4.5 to 17 to 1
Large text (24px regular, or about 18.7px bold)3 to 14.5 to 1
Icons, input borders, focus indicators, chart lines3 to 1Not defined
Logos and disabled controlsExemptExempt

The third row is the one teams forget. Success criterion 1.4.11 covers non-text contrast: a text field outlined in light grey #D0D0D0 on white is roughly 1.5 to 1 and fails, even if the label above it passes easily. The same rule applies to focus rings, which is one reason focus outlines keep getting removed rather than redesigned.

Where Contrast Usually Fails

In audits, the same handful of patterns cause most failures:

  • Placeholder text. Browser defaults and many design systems use a pale grey that lands around 2.5 to 1 to 3 to 1. If the placeholder carries information, it must meet 4.5 to 1.
  • Text on images. A white headline over a photo passes on the dark sky and fails on the bright beach. The ratio must hold across the whole area behind the text.
  • Brand buttons. White text on a mid orange or light teal often sits near 3 to 1, fine for large text but not for a 14px button label.
  • Footer and meta text. Small, light grey copyright lines and timestamps are frequent offenders.
  • Hover and visited states. The default link passes, then the hover color quietly drops below the threshold.

Why "Just Pass AA" Is Not the Whole Job

A common shortcut is to treat 4.5 to 1 as a finish line. It is a floor. Thin fonts change the picture: a 300 weight typeface at 16px passes the ratio but reads far worse than a 400 weight at the same color, because the formula knows nothing about stroke thickness. If your brand font is light, aim well above the minimum.

The opposite advice, push everything to maximum contrast, also misleads. Pure white text on pure black at 21 to 1 can cause a halo effect for some readers with astigmatism, especially in dark mode. Many dark themes use an off-white such as #E6E6E6 on a near-black around #121212, which still lands comfortably above 13 to 1 and is gentler to read.

WCAG 3 drafts explore a different model called APCA that accounts for font size, weight and polarity. It is not a legal standard yet, but it explains why some combinations that pass WCAG 2 still feel weak.

Checking Contrast in Your Workflow

Most browser DevTools show the contrast ratio when you inspect a text element, with a pass or fail mark against AA and AAA. Chrome and Edge also draw the AA and AAA lines inside the color picker, so you can drag toward a passing shade. Design tools such as Figma have contrast plugins that check whole frames.

Automated scanners catch flat text on flat backgrounds reliably. They struggle with text over gradients, images and semi-transparent overlays, and they cannot judge states that only appear on hover or focus. As covered in our piece on why automated scanners miss real problems, a manual pass over those cases is still needed.

One low-tech check I still use: view the page with the screen brightness at its lowest setting, then again in grayscale using the operating system's color filter. Anything that disappears in either test deserves a measurement. It takes two minutes and often finds the faint divider lines and icon buttons that nobody thought to inspect.

Fixing Failures Without Losing the Brand

You rarely need a new palette. A few targeted changes usually do it:

  1. Keep the brand color for large elements, backgrounds and illustrations, and create a darker variant for text and small labels.
  2. For buttons, try dark text on the brand color instead of white. A mid orange that fails with white text often passes with near-black.
  3. Add a scrim behind text on images, such as a 60% black gradient, and test the lightest part of the photo.
  4. Define contrast-safe tokens in your design system, for example text-muted at 4.6 to 1 minimum, so nobody reinvents the pale grey.

Document the measured ratio next to each token. When a designer adjusts a shade six months later, the number makes the trade-off visible.

FAQ

Does contrast apply to disabled buttons?

No. WCAG exempts inactive controls. Still, if users need to know a button exists and why it is disabled, very faint styling can confuse them, so pair it with a clear message.

Is 4.5 to 1 required for every heading?

Only for headings below the large text size. A heading at 24px regular or about 18.7px bold needs 3 to 1 at AA.

Do I have to check dark mode separately?

Yes. Every color pair is new in a dark theme, and muted text, borders and links often fail there even when the light theme passes.

The Practical Takeaway

Measure, do not guess. Check body text against 4.5 to 1, large text and interface parts against 3 to 1, and test placeholders, hover states, image overlays and dark mode by hand. Turn the passing shades into named tokens so the fix sticks. More practical guides are in our accessibility section.

MC
Maren Castellanos

Maren worked in cross-browser QA before editing, and knows the DevTools Network tab better than most people know their inbox. She writes about how browsers load and render pages and about the tools that show what is really happening.

More posts by Maren

More in Accessibility

01doorganico 084goad 3hot8 4465456 after5internprogram ahandfulofblessings ahmadpc ahomcybersecurity allodialresources annodex antiquesandperiodcollectibles aotc asianworldkashmir beedlectrics bloggertemplatefree bluefeatheragency bodyweightsolution campaign08blog carfixadvisor catstreetcollective cbstvsports cnfanspurchasing committeeof500yearsofdignityandresistance covertopsdjs cs-kaiyuangame cuestacomunicaciontotal cxoutlet dancebetgame deskgearhub destinyvillastay detectmagazine di33 diamondsteelbuildings donmo dyanadropship emarsysfinance emimattl escapechan eskitarih evautoplay everythingweddings-orlando farmzone feedviewerapp foxstudiosscratch gamevui123 gleasongrade greetingsday hacklabasu haskell iw-kaiyun jagoanproperti jilidskripsi joltbusinessfinance juegodepokergratis juegospucca kaeru-festival latestmehndidesigns lestanzediottaviano litemagazines localcommunityhero lustrsazan mariashebets menswheels newwebstechs1 nikkit ootheday oxigenvpn plurfitness prefetial probudgetcarpetcleaning psychicmrsmartin pyaten qvantor rejoignezlogis rexzbot riselayouts ruchin rwxqsgd5 sakaltime shibiaowang shop-bot skrtips strcoding tetapatrium topselller uneplanteartificielle vipertechcr wo-kygaming yamaha-mekar yeknava yoybuysheets zenunwa yamaha-mekar jilidskripsi ankertoto ankertoto ankertoto ankertoto