Accessibility 10 min read

Colour contrast and readable text: a practical guide to accessible typography

The WCAG contrast ratios, how to adapt a brand palette that fails them, and readable text beyond colour.

On this page 9 sections

Many colour contrast accessibility problems start in a brand guideline, not in the code. A pale grey that looked elegant on a mood board, a bright brand orange used for links, white headlines over a photo that turns out to be mostly sky. Each looks fine on a good screen indoors. For someone with low vision, or anyone reading a phone in sunshine, the words fade away.

The fix is rarely a new brand. It’s a set of rules about which colours may carry text, plus a few type decisions that let people enlarge and restyle text without breaking the page.

The short answer

WCAG 2.2 level AA sets three contrast minimums:

  • 4.5:1 for normal text against its background (success criterion 1.4.3).
  • 3:1 for large text, meaning at least 18pt (24px), or 14pt (about 18.7px) if bold.
  • 3:1 for interface components and meaningful graphics (success criterion 1.4.11): field borders, checkbox outlines, icon-only buttons, focus indicators, any button edge people need to spot the button, and the chart elements needed to read a chart.

Logos, pure decoration and disabled controls are exempt. The ratios are hard thresholds, not targets to round up to: 4.48:1 fails.

How WCAG measures colour contrast for accessibility

A WCAG contrast ratio compares the relative luminance, roughly the perceived lightness, of two colours, from 1:1 (a colour on itself) to 21:1 (black on white). Only lightness counts, not hue: two colours that look strikingly different can be close in lightness, and those are the pairs people with colour vision deficiency struggle to separate.

Level AAA raises text to 7:1, or 4.5:1 for large text (criterion 1.4.6). It isn’t expected site-wide, but it’s a sensible aim for long-form body text. WCAG 2.2 explained shows where contrast sits among the other criteria.

Three details that catch teams out

Every state counts. Text must pass in its hover, focus, visited, selected and error states, and on every background it can land on: the tinted band, the dark footer, the cookie banner.

Size is measured, not implied. A heading set at 20px regular weight is normal text in WCAG’s terms and needs 4.5:1.

Thin weights read lighter than their colour. The formula ignores stroke width, so a light 300-weight font at small sizes can pass and still be hard to read. Use a regular or medium weight for body text.

How to check contrast yourself

Every colour contrast checker uses the same WCAG formula; what differs is how you get the colours in.

  • Browser DevTools. In Chrome, inspect some text and click the colour swatch next to color in the Styles pane. The picker shows the ratio and the AA and AAA lines, so you can drag to the nearest passing shade.
  • A standalone checker. WebAIM’s Contrast Checker takes hex values. TPGi’s free Colour Contrast Analyser has an eyedropper that samples anything on screen, including text over a photo.
  • Automated audits. Lighthouse and axe flag low-contrast text in the code, but can’t reliably judge text over images, gradients or video. See what automated checkers can and can’t tell you.

For a whole palette, a contrast grid shows every pairing at once and turns a debate about taste into a list of allowed combinations.

Adapting a brand palette that fails

Many brand palettes include a colour that fails for small text on white, and it’s often the most recognisable one. Turning it into an accessible colour palette rarely means dropping that colour. It means deciding what each one is allowed to do:

  • Fills, large headlines and illustrations: the brand colour as it is, where it passes (3:1 for large text; decoration has no minimum).
  • Links and small text: a darker “text tint” of the same hue, or a lighter one on dark backgrounds.
  • Borders, icons and focus indicators: a shade that reaches 3:1 against whatever sits next to it.

A worked example: bright orange

Take a hypothetical brand orange, #FF7A00, and the pairings a designer would reach for first:

Pairing Ratio Result
Orange text on white 2.61:1 Fails, even for large text
White text on an orange button 2.61:1 Fails
Near-black (#1A1A1A) text on an orange button 6.66:1 Passes, and the button stays orange
Darker text tint (#B84F00) on white 5.05:1 Passes for links and small text

The fix usually follows the same pattern: keep the hue, change the lightness, and let the vivid original do the safe jobs. White text on a bright brand colour is a habit, not a rule. Building the tonal scale in OKLCH, which modern browsers support in CSS, keeps the hue steadier than HSL as lightness changes.

Greys follow the same maths. #767676 is the lightest neutral grey that reaches 4.5:1 on white (4.54:1); #777777 falls just short at 4.48:1, and the fashionable #999999 manages only 2.85:1.

Record the passing pairs as design tokens and in your brand guidelines for the website so nobody improvises a text colour later.

  • Button labels need 4.5:1 against the button fill, or 3:1 if the label is large text.
  • Outline (ghost) buttons rely on their border to look clickable. WCAG doesn’t strictly require it to reach 3:1 when the label is clear, but aim for it.
  • Links in paragraphs should be underlined. A colour-only link passes only if it reaches 3:1 against the surrounding text and 4.5:1 against the background: a narrow band of mid-tones with black text, and impossible with #333333 text on white.
  • Focus indicators are most dependable as a solid 2px outline with a small offset, in a colour that reaches 3:1 on both your light and dark sections. Accessible navigation covers focus in depth.

Text over hero images and video

Text over a photo has to pass against the part of the image behind it that’s closest to the text colour in lightness, and the crop changes at every screen width. Build the protection into the component, with a gradient scrim, solid overlay or text panel, so a new photo from an editor can’t quietly break contrast. A faint text shadow rarely does the job on its own.

With video, check the brightest frames, not the poster image. Video that plays automatically for more than five seconds alongside other content also needs a way to pause, stop or hide it (criterion 2.2.2, level A).

Dark mode and forced colours

A dark theme means checking every pairing again, usually with lighter text tints.

Windows contrast themes replace your colours with the user’s own (the forced-colors media feature, which Chrome DevTools can emulate). Anything drawn with a background colour alone, such as a card edge or custom checkbox, can vanish, so give components real borders, transparent if necessary, that the system can draw.

Don’t rely on colour alone

Criterion 1.4.1 (level A) says colour can’t be the only way you convey information or distinguish an element. Contrast ratios don’t solve this: a red and a green status colour can both pass against white and still be hard to tell apart for someone with red-green colour blindness.

Pattern Colour-only version Accessible version
Links in body text Blue text, no underline Underlined links
Form errors A red border A written message beside the field, plus an icon
Charts Colour-coded lines and a legend Direct labels, patterns or distinct markers
Selected tab or filter A colour change An underline, weight change or tick

Chrome’s Rendering panel can emulate colour vision deficiencies to expose these problems. Forms have extra rules, covered in how to design accessible forms.

Accessible typography beyond colour

Contrast gets text seen. These criteria keep it readable when people zoom, enlarge text or override spacing.

Is there a minimum font size for accessibility?

WCAG sets no minimum font size; it asks that text can be resized instead. In practice, treat the usual browser default of 16px as the floor for body text, and go larger for long reading. Keep form inputs at 16px or more too, because Safari on iPhone zooms into fields with smaller text. Set sizes in rem and don’t fix the root font size in pixels, so the site respects each visitor’s browser font setting.

Text that resizes to 200% and reflows

Text must scale to 200% without losing content or functionality (criterion 1.4.4). Reflow (criterion 1.4.10) asks that reading content fits a 320 CSS pixel width without horizontal scrolling, which is what a 1280px-wide window shows at 400% zoom. Parts that need a two-dimensional layout, such as data tables, maps and diagrams, are exempt.

Check it yourself. Zoom a 1280px-wide desktop window to 200%, then 400%, and look for these failures:

  • Text cut off inside fixed-height cards, banners or buttons
  • Menus or overlays you can no longer close
  • A sticky header that covers half the screen

On phones, also make sure the page code doesn’t block pinch-zoom with maximum-scale=1 or user-scalable=no.

Text-spacing overrides

Criterion 1.4.12 requires that nothing breaks when a reader sets line height to 1.5 times the font size, paragraph spacing to 2 times, letter spacing to 0.12 times and word spacing to 0.16 times. You don’t design with those values; the page just has to survive them. Test with a free text-spacing bookmarklet and watch for clipped or overlapping text.

No images of text

Criterion 1.4.5 asks for real text rather than text baked into images, with logos exempt. Images of text blur when zoomed, ignore people’s colour and spacing settings, and aren’t treated as page text by browser translation or search engines. On a multilingual site, every banner also needs a separate image per language.

Fonts and line heights for every script

A line height of about 1.5 suits body text in Latin scripts. Scripts with marks above and below the letters, such as Thai, Devanagari or Vietnamese, usually need more, or those marks clip into the line above. Check that your web font covers every script you publish in, or readers get a fallback system font you never tested. Avoid letter spacing in scripts whose letters join, such as Arabic.

Choosing typefaces and a type scale is covered in web typography for business websites, and language planning in how to plan a multilingual website.

A colour contrast and readability checklist

Frequently asked questions

Does our logo have to meet contrast requirements?

No. WCAG exempts logotypes and brand names from the contrast minimums. A strapline set as ordinary text beside the logo isn’t exempt, though.

Do placeholder text and disabled buttons need to pass?

Disabled controls are exempt. Placeholder text isn’t: W3C’s guidance on criterion 1.4.3 says the 4.5:1 minimum applies to it. Never use it in place of a visible label either, because it vanishes as soon as someone types.

Should we use APCA instead of WCAG contrast ratios?

APCA, the Accessible Perceptual Contrast Algorithm, is a newer method that accounts for font size, weight and light-on-dark versus dark-on-light text. It has been explored during the development of WCAG 3, but at the time of writing (August 2026) it isn’t part of any W3C Recommendation. Design and test against the WCAG 2.x ratios, and treat APCA as a second opinion.

What to do next

Contrast is cheapest to get right at the palette stage: decide the passing pairs once, build them into tokens and components, and every page inherits them. The wider picture is in our practical guide to web accessibility.

If your current site fails, the fix is often a few colour tokens and component changes. If colours are hard-coded across hundreds of templates, or key messages live inside images, the build itself is the problem. That’s when a website redesign earns its keep: it fixes contrast at the source, with the palette tested before design sign-off, accessible tints and states built as reusable styles, and text checked in every language the site uses. Not sure which you need? Talk to us about what your website needs.

Written by the PORVIX team

The people who design, build and maintain websites for growing businesses. We write about the questions that come up on real projects, in plain language, and update articles when the advice changes.

Published

How we work

Is your current site costing you enquiries?

A free, plain-language audit, by email within 2 business days.

Get a free audit
Get a free audit

Keep reading

More plain-language guides.

More on accessibility first, then other guides worth reading next.

All insights

Start here

Let’s build a website that brings in business.

Tell us about your project. You’ll hear back from a real person within one business day, with honest advice either way.

  • Free consultation
  • Fixed written quote
  • Your details stay private