Bhinish Dhiman
Bootstrap and Tailwind status colours fail for colour-blind users
Bootstrap's danger red and success green sit at a perceptual distance of about 74 for typical vision. Under deuteranopia that falls to 4. Tailwind's red-500 and green-500 fall from 76 to 8. Both pairs pass contrast individually, so no contrast audit catches it.
Most design systems ship a red for errors and a green for success. It is such an established convention that nobody audits it. We measured what happens to those defaults under simulated colour vision deficiency, and the numbers are worse than the convention suggests.
What we measured
We took the default status colours from two widely used systems and computed the CIEDE2000 perceptual distance between each red and green pair — first for typical vision, then for each simulated deficiency using the Machado et al. (2009) model.
Roughly speaking, a distance under about 10 means two colours are hard to tell apart side by side. Under 5 and they read as the same colour.
| Pair | Typical | Deuteranopia | Protanopia | Achromatopsia |
|---|---|---|---|---|
Bootstrap danger / success |
74.1 | 4.0 | 25.4 | 10.0 |
Tailwind red-500 / green-500 |
76.1 | 7.8 | 25.9 | 12.9 |
| Generic red / green | 70.6 | 6.0 | 23.0 | 10.1 |
| Blue / orange | 62.4 | 74.2 | 66.1 | 28.7 |
Deuteranopia is the column that matters most, because deuteranomaly — its milder form — affects roughly 5% of men. For those users, Bootstrap’s error and success states are separated by a perceptual distance of four. That is not a subtle degradation. It is the same colour.
The last row is the control. Blue against orange does not merely survive; it separates further under deuteranopia than under typical vision.
Why every contrast audit misses this
This is the part worth internalising, because it explains how the problem survives in systems that are otherwise carefully built.
A contrast checker asks one question: is this foreground separable from this background? Bootstrap’s #dc3545 on white is 4.53:1. Its #28a745 on white is 3.01:1. Both are on the shelf of acceptability, both get signed off.
But the checker never compares the red to the green, because they are never on top of each other. They appear in different places — one on the error toast, one on the success toast — and the tool has no concept of a relationship between them.
Colour-blindness failures live in exactly that blind spot. They are pairwise failures between foreground colours, and the standard toolchain only tests foreground against background.
Protanopia is different, and it matters
Notice that protanopia scores much better in the table — around 25 rather than 4. That is not because protanopes find these colours easy.
Protanopia involves reduced sensitivity to red light overall, so reds appear darker as well as hue-shifted. That lightness difference is what pushes the distance back up: the two colours become separable, but by brightness rather than by hue.
Which is a useful hint about the fix.
What actually works
Separate by lightness. Lightness perception is unaffected by every red-green deficiency. If your error and success colours differ by a clear step in lightness as well as hue, they stay distinguishable no matter what the cones are doing. This single change fixes most palettes.
Use the blue-orange axis for two-value encodings. Charts, toggles, before-and-after comparisons. Blue against orange is the most robust two-colour pairing available, and the measurement above shows why.
Stop making colour load-bearing. This is the one that actually satisfies WCAG, and it is the fix that does not depend on getting the maths right. Success Criterion 1.4.1 requires that colour never be the only means of conveying information. In practice:
- An icon on the alert, not just a coloured border
- Direct labels on chart series, not just a colour key
- Solid versus dashed lines
- The word “Error” in the error message
Once a second cue is present, the palette stops carrying the meaning on its own, and a collapse becomes cosmetic rather than functional.
A note on the framework defaults
None of this means Bootstrap or Tailwind are badly built. Both give you the tools to override these values, and both document the accessibility requirement.
But defaults are what ship. A default that is invisible to 5% of men is a default worth changing in your own theme layer rather than inheriting. It costs one line in a config file, and the alternative is a class of bug that no automated audit in your pipeline will ever surface.
Check your own palette
The measurements in this post came from our colour blindness simulator, which runs every pair in a palette against eight simulated vision types and reports the ones that collapse. It runs entirely in your browser — paste your brand colours in and see which pairs your contrast audit has been quietly passing.