More than 80% of audited websites have color contrast failures. The good news: once you know the rules, most fixes are a single CSS variable change. Here are the most common failure patterns and their fixes.
/* FAIL: gray-400 (#9ca3af) on white = 2.85:1 */
body { color: #9ca3af; }
/* FAIL: gray-500 (#6b7280) on white = 4.54:1 — barely passes */
body { color: #6b7280; }
/* PASS (safe): gray-700 (#374151) on white = 8.59:1 */
body { color: #374151; }
/* PASS (better): near-black on white = 19.5:1 */
body { color: #111827; }
For body text, aim for at least gray-600 (#4b5563, 7.16:1). Gray-500 technically passes but has no margin for slightly off-white backgrounds.
See how 321 websites scored →
View the 2026 ReportMuted text (metadata, captions, helper text) is a common failure area because designers want it subtle.
/* FAIL: caption text at #aaa on white = 2.32:1 */
.caption { color: #aaaaaa; }
/* PASS: minimum passing gray for normal text on white */
.caption { color: #767676; } /* 4.54:1 */
/* Better: more breathing room */
.caption { color: #6b7280; } /* 4.54:1, Tailwind gray-500 */
/* FAIL: Tailwind blue-400 = 2.85:1 */
a { color: #60a5fa; }
/* FAIL: Tailwind blue-500 = 3.35:1 (passes large text, not normal) */
a { color: #3b82f6; }
/* PASS: Tailwind blue-600 = 5.08:1 ✓ */
a { color: #2563eb; }
/* PASS: Tailwind blue-700 = 7.27:1 */
a { color: #1d4ed8; }
White (#ffffff) text requires very dark backgrounds to pass:
/* White text needs these minimum shades: */
/* Blue: blue-600 (#2563eb) = 5.08:1 ✓ */
.btn-blue { background: #2563eb; color: #fff; }
/* Green: green-700 (#15803d) = 5.74:1 ✓ */
.btn-green { background: #15803d; color: #fff; }
/* Red: red-700 (#b91c1c) = 5.96:1 ✓ */
.btn-danger { background: #b91c1c; color: #fff; }
/* Gray: gray-700 (#374151) = 8.59:1 ✓ */
.btn-secondary { background: #374151; color: #fff; }
/* FAIL: orange-500 (#f97316) with white = 2.43:1 */
/* Use dark text on orange instead: */
.badge-orange { background: #f97316; color: #1c1917; } /* 8.2:1 ✓ */
/* FAIL: default placeholder is usually #aaa = 2.32:1 */
::placeholder { color: #aaaaaa; }
/* PASS: minimum */
::placeholder { color: #767676; }
/* Practical: add slightly higher contrast + italic to signal it's a hint */
::placeholder {
color: #6b7280;
font-style: italic;
}
WCAG exempts disabled controls from contrast requirements — but "disabled" must mean truly non-interactive, not just visually dimmed. If a user can still focus or interact with it, contrast requirements apply.
/* OK for truly disabled states */
input:disabled {
color: #9ca3af;
background: #f3f4f6;
cursor: not-allowed;
}
/* FAIL: default browser outline removed */
:focus { outline: none; }
/* FAIL: thin light outline */
:focus-visible { outline: 1px dotted #ccc; }
/* PASS: 3:1+ contrast against adjacent background */
:focus-visible {
outline: 3px solid #2563eb; /* 5.08:1 against white */
outline-offset: 3px;
border-radius: 4px;
}
@media (prefers-color-scheme: dark) {
body {
background: #0f172a; /* slate-900 */
color: #e2e8f0; /* slate-200, 14.4:1 ✓ */
}
a {
color: #93c5fd; /* blue-300, 5.15:1 on slate-900 ✓ */
}
.muted {
color: #94a3b8; /* slate-400, 4.65:1 on slate-900 ✓ */
}
}
Define all colors as custom properties with contrast metadata in comments. This makes finding and fixing failures much easier:
:root {
/* All contrast ratios are against white (#fff) */
--color-text-primary: #111827; /* 19.5:1 ✓ */
--color-text-secondary: #374151; /* 8.59:1 ✓ */
--color-text-muted: #6b7280; /* 4.54:1 ✓ (minimum) */
--color-link: #2563eb; /* 5.08:1 ✓ */
--color-link-hover: #1d4ed8; /* 7.27:1 ✓ */
--color-placeholder: #767676; /* 4.54:1 ✓ (minimum) */
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--color-text-primary: #f1f5f9; /* 16.6:1 on #0f172a ✓ */
--color-text-secondary: #e2e8f0; /* 14.4:1 on #0f172a ✓ */
--color-text-muted: #94a3b8; /* 4.65:1 on #0f172a ✓ */
--color-link: #93c5fd; /* 5.15:1 on #0f172a ✓ */
}
}
Accessalyze scans every color combination on your live pages and reports the exact failing ratios with suggested fix values.
Scan for Contrast Issues →← Back to Accessalyze · ← WCAG Contrast Requirements
See real website accessibility scores: Browse 244+ free accessibility audits →
Try it yourself
Enter your website URL to get a free accessibility score.