Choose Theme

Dark Mode Color Contrast Checker

· 3 min read · #CSS #Accessibility
--

Ensure your dark mode colors meet WCAG contrast requirements. Run this in dev tools to check all text colors.

contrast-checker.js
function getContrastRatio(color1, color2) {
// Convert hex to RGB
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
}
// Get relative luminance
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;
}
const rgb1 = hexToRgb(color1);
const rgb2 = hexToRgb(color2);
if (!rgb1 || !rgb2) return null;
const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
const brightest = Math.max(lum1, lum2);
const darkest = Math.min(lum1, lum2);
return (brightest + 0.05) / (darkest + 0.05);
}
function checkDarkModeContrast() {
// Force dark mode
document.documentElement.classList.add('dark');
const results = [];
// Check all text elements
const textElements = document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, span, a, li');
textElements.forEach((el) => {
const styles = window.getComputedStyle(el);
const color = styles.color;
const bgColor = styles.backgroundColor;
// Convert RGB to hex
function rgbToHex(rgb) {
const match = rgb.match(/\d+/g);
if (!match) return null;
const [r, g, b] = match.map(Number);
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
const colorHex = rgbToHex(color);
const bgColorHex = rgbToHex(bgColor);
if (colorHex && bgColorHex) {
const ratio = getContrastRatio(colorHex, bgColorHex);
if (ratio && ratio < 4.5) {
results.push({
element: el.tagName.toLowerCase(),
text: el.textContent?.slice(0, 50),
color: colorHex,
bgColor: bgColorHex,
ratio: ratio.toFixed(2),
passes: false,
});
}
}
});
console.log('🌙 Dark Mode Contrast Report\n');
if (results.length === 0) {
console.log('✅ All text meets WCAG AA contrast requirements (4.5:1)');
} else {
console.log(`❌ Found ${results.length} contrast issues:\n`);
console.table(
results.map((r) => ({
Element: r.element,
Text: r.text,
Color: r.color,
'BG Color': r.bgColor,
'Ratio': r.ratio,
'Required': '4.5:1',
}))
);
console.log('\n💡 Recommended fixes:');
results.slice(0, 5).forEach((r) => {
console.log(`\n${r.element} { color: ${r.color} } on { background: ${r.bgColor} }`);
console.log(`Current ratio: ${r.ratio}:1 (needs 4.5:1)`);
console.log('Try increasing lightness or using a different shade');
});
}
}
// Run the checker
checkDarkModeContrast();

Usage:

  1. Open dev tools console
  2. Paste the script
  3. Review the report

Example output:

🌙 Dark Mode Contrast Report
❌ Found 3 contrast issues:
┌─────────┬──────────────┬─────────┬───────────┬───────┬──────────┐
│ Element │ Text │ Color │ BG Color │ Ratio │ Required │
├─────────┼──────────────┼─────────┼───────────┼───────┼──────────┤
│ p │ Some text... │ #6B7280 │ #111111 │ 3.2 │ 4.5:1 │
│ a │ Click here │ #60A5FA │ #1F2937 │ 4.1 │ 4.5:1 │
│ span │ Label text │ #9CA3AF │ #000000 │ 4.3 │ 4.5:1 │
└─────────┴──────────────┴─────────┴───────────┴───────┴──────────┘
💡 Recommended fixes:
p { color: #6B7280 } on { background: #111111 }
Current ratio: 3.2:1 (needs 4.5:1)
Try increasing lightness or using a different shade

Tailwind fix:

<!-- ❌ Fails contrast (3.2:1) -->
<p class="text-zinc-500 dark:text-zinc-500">Text</p>
<!-- ✅ Passes contrast (5.1:1) -->
<p class="text-zinc-500 dark:text-zinc-300">Text</p>

Automated testing:

// In your test suite
expect(getContrastRatio('#FFFFFF', '#000000')).toBeGreaterThan(4.5);

When to use:

  • Before deploying dark mode
  • After color scheme changes
  • Accessibility audits
  • Ensuring WCAG compliance

Related