1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Color Contrast Checker</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.color-input { margin-bottom: 10px; width: 100%; height: 100px; }
.color-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.color-box { padding: 10px; text-align: center; border: 1px solid #ccc; }
.hidden { display: none; }
.form-controls { display: flex; justify-content: space-between; align-items: center; }
.checkbox-controls { display: flex; gap: 20px; }
</style>
</head>
<body>
<header>
<h1>What color combinations are accessible?</h1>
</header>
<main>
<form id="colorForm" onsubmit="event.preventDefault(); updateColors();">
<label for="colors">Enter hex color codes (comma or newline-separated):</label>
<textarea id="colors" class="color-input" required></textarea>
<div class="form-controls">
<button type="submit">Check Contrast</button>
<div class="checkbox-controls">
<label><input type="checkbox" id="toggleFails" onchange="updateVisibility()"> Hide failing pairs</label>
<label><input type="checkbox" id="sortContrast" onchange="updateColors()"> Sort by contrast</label>
</div>
</div>
</form>
<br>
<br>
<section id="results" class="color-grid" aria-live="polite"></section>
</main>
<footer>
<p>Color Contrast Checker - A tool to help you find accessible color combinations.</p>
</footer>
<script>
function getContrastRatio(c1, c2) {
function luminance(r, g, b) {
let a = [r, g, b].map(v => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
let rgb1 = c1.match(/\w\w/g).map(x => parseInt(x, 16));
let rgb2 = c2.match(/\w\w/g).map(x => parseInt(x, 16));
let lum1 = luminance(...rgb1);
let lum2 = luminance(...rgb2);
return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05);
}
function getWCAGLevel(contrast) {
if (contrast >= 7) return "AAA";
if (contrast >= 4.5) return "AA";
return "Fail";
}
function createColorBox(color1, color2, contrast, level, accessible, showGlow) {
const classes = ["color-box"];
if (!accessible) classes.push("failing");
if (level === "AAA" && showGlow) classes.push("glowing-border");
return `
<div class="${classes.join(' ')}" style="background: ${color1}; color: ${color2};">
${color1} on ${color2}<br>
Ratio: ${contrast} ${accessible ? "✅" : "❌"}<br>
WCAG: ${level}
</div>
`;
}
function updateColors() {
const input = document.getElementById("colors").value;
const colors = input.split(/[\n,]+/).map(c => c.trim()).filter(c => c);
const results = document.getElementById("results");
const colorCombinations = colors.flatMap((color1, i) =>
colors.slice(i + 1).map(color2 => {
const contrast = getContrastRatio(color1, color2).toFixed(2);
const level = getWCAGLevel(contrast);
const accessible = level !== "Fail";
return { color1, color2, contrast, level, accessible };
})
);
const sortByContrast = document.getElementById("sortContrast").checked;
if (sortByContrast) {
colorCombinations.sort((a, b) => b.contrast - a.contrast);
}
const colorBoxes = colorCombinations.map(({ color1, color2, contrast, level, accessible }) => {
return createColorBox(color1, color2, contrast, level, accessible, false);
});
results.innerHTML = colorBoxes.join('');
updateVisibility();
}
function updateVisibility() {
let hideFails = document.getElementById("toggleFails").checked;
document.querySelectorAll(".failing").forEach(box => {
box.style.display = hideFails ? "none" : "block";
});
}
</script>
</body>
</html>
|