While building this site I had to pick a grey for secondary text. The easy answer was Tailwind's zinc-500, which is what half the internet uses. I measured it before using it, and it doesn't make the cut. Here are the numbers, because they're more useful than the conclusion.
The minimum is not a matter of taste
The WCAG accessibility guidelines at level AA require 4.5:1 of contrast for normal text and 3:1 for large text — from 24px, or 18.5px bold. In Spain this isn't a design recommendation: Royal Decree 1112/2018 binds the public sector, and Law 11/2023 extends it to a good number of private companies. And even where nothing binds you, text nobody can read sells nothing.
Contrast is calculated from the relative luminance of the two colours, not from what your eye makes of them. That's the trap: on pure black, a grey that looks readable on your good monitor falls apart the moment someone opens it outdoors on a phone.
The numbers, measured against #000000
| Grey | Contrast | Good for normal text? |
|---|---|---|
#FFFFFF | 21.00:1 | Yes |
#A3A3A3 | 8.33:1 | Yes, comfortably |
#8A8A8A | 6.08:1 | Yes |
#757575 | 4.56:1 | Yes, barely |
#71717A (zinc-500) | 4.35:1 | No |
#5A5A5A | 3.04:1 | Large text only |
#3D3D3D | 1.93:1 | Decoration only |
zinc-500 misses by 0.15. It isn't a scandalous failure, but it is a failure: run an automated audit and it gets flagged. The interesting part is that zinc-400 (#A1A1AA) gives 8.19:1 and clears it easily — the gap between the two steps is huge, so there's no elegance to mourn. Go up one step and move on.
What we did here
We defined three text tokens and no more:
#FFFFFFfor headings and primary text.#A3A3A3for secondary text and descriptions. 8.33:1 leaves plenty of room.#858585for captions and minor notes. This is the floor: nothing below that value carries text on this site. Full stop.
Three tokens force you to decide the hierarchy instead of nudging the grey down every time something "feels too heavy". It's a constraint, and that's exactly why it works.
The trap that nearly caught us
The floor was going to be #757575, which gives 4.56:1 and passes. It passes against pure black. But this site has cards on a background a shade lighter than the base — #0a0a0a — and on those the same grey drops to 4.30:1. On the next surface up, #141414, it falls to 4.00:1.
So the token passed the check against the colour we had measured, and failed in exactly the three places it was actually used. That's why the floor ended up as #858585: 5.69:1 on black, 5.37:1 on #0a0a0a, 4.99:1 on #141414.
Measure every grey against every background it can land on, not just the page background. A card raised by 4% of luminance eats 0.3 points of ratio without looking any different to the eye.
What this does not fix
Correct contrast doesn't make a site accessible. You still need a visible keyboard focus, a sensible tab order, alt text, accessible names on controls, and a layout that survives 200% zoom. Contrast is one of the few things you can settle with a formula, which is why it's the first thing to check and the first thing to fix — not the last thing you need.
We also haven't touched level AAA, which asks for 7:1. Our secondary grey clears it without trying; the floor does not.
How to check yours in a minute
Open your browser's developer tools, inspect the text and look at the colour picker: Chrome and Firefox show you the contrast ratio against the real background and warn you when it misses AA. If you'd rather compute it yourself, the formula is in the WCAG specification linked above.
If any text on your site comes back under 4.5, you've got a ten-minute fix and one fewer excuse.
Any questions, let me know and we'll look at it.