Understanding Contrast Ratios
Understanding Color Contrast Ratios
Color contrast is one of the most fundamental aspects of web accessibility. Without adequate contrast between text and its background, content becomes difficult or impossible to read for many users. This guide explains everything you need to know about contrast ratios and how to ensure your designs meet accessibility standards.
What Is a Contrast Ratio?
A contrast ratio is a numerical measurement that compares the relative luminance (brightness) of two colors. It's expressed as a ratio like 4.5:1 or 7:1, where the first number represents the lighter color and the second represents the darker color.
The scale ranges from 1:1 (no contrast, identical colors) to 21:1 (maximum contrast, pure white on pure black).
How Contrast Ratios Are Calculated
The formula for calculating contrast ratio involves the relative luminance of each color:
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)Where L1 is the luminance of the lighter color and L2 is the luminance of the darker color.
Luminance itself is calculated from RGB values using a gamma-corrected formula:
function getLuminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255;
return c <= 0.03928
? c / 12.92
: Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}WCAG Contrast Requirements
The Web Content Accessibility Guidelines (WCAG) define specific contrast requirements:
Level AA (Minimum)
| Element Type | Minimum Ratio |
|-------------|---------------|
| Normal text (under 18pt or 14pt bold) | 4.5:1 |
| Large text (18pt+ or 14pt+ bold) | 3:1 |
| UI components and graphical objects | 3:1 |
Level AAA (Enhanced)
| Element Type | Minimum Ratio |
|-------------|---------------|
| Normal text | 7:1 |
| Large text | 4.5:1 |
Why Large Text Has Lower Requirements
Large text is naturally easier to read due to:
- Greater stroke width: Letters are thicker and more defined
- Larger character size: Easier for eyes to perceive shapes
- Better recognition: Familiar letter patterns are more apparent
WCAG defines large text as:
- Regular weight: 18pt (24px) or larger
- Bold weight (700+): 14pt (18.66px) or larger
Common Contrast Mistakes
Mistake 1: Light Gray on White
/* Bad - 2.5:1 ratio */
.placeholder {
color: #a0a0a0;
background: #ffffff;
}
/* Good - 4.6:1 ratio */
.placeholder {
color: #757575;
background: #ffffff;
}Mistake 2: Colored Text on Colored Background
/* Bad - 1.8:1 ratio */
.warning {
color: #ff6600;
background: #ffcc00;
}
/* Good - 4.5:1 ratio */
.warning {
color: #6b2400;
background: #fff3cd;
}Mistake 3: Ignoring Hover/Focus States
/* Check ALL states meet contrast requirements */
.button {
color: #ffffff;
background: #0066cc; /* 4.9:1 - passes */
}
.button:hover {
background: #3399ff; /* 2.9:1 - fails! */
}
/* Fixed */
.button:hover {
background: #0052a3; /* 5.7:1 - passes */
}Practical Examples
Dark Mode Considerations
Dark mode isn't just inverting colors, it requires careful contrast management:
/* Light mode */
:root {
--text-primary: #1a1a1a; /* Very dark gray */
--text-secondary: #525252; /* Medium gray */
--background: #ffffff; /* White */
}
/* Dark mode - not just inverted! */
[data-theme="dark"] {
--text-primary: #e5e5e5; /* Not pure white - easier on eyes */
--text-secondary: #a3a3a3; /* Light gray */
--background: #171717; /* Not pure black */
}Gradients and Images
When placing text over gradients or images:
/* Add overlay for consistent contrast */
.hero-text-container {
position: relative;
}
.hero-text-container::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.6),
rgba(0, 0, 0, 0.8)
);
}
.hero-text {
position: relative;
color: #ffffff;
}Checking Contrast in Code
CSS Custom Properties Approach
:root {
/* Define accessible color pairs */
--color-text-on-primary: #ffffff;
--color-primary: #0066cc;
--color-text-on-success: #155724;
--color-success-bg: #d4edda;
}
/* Use paired values together */
.button-primary {
color: var(--color-text-on-primary);
background: var(--color-primary);
}Testing Recommendations
- Test early and often: Check contrast during design, not after development
- Verify all states: Normal, hover, focus, active, disabled
- Consider context: Text over images needs overlays
- Test with real users: Automated tools can't catch everything
Was this article helpful?