about summary refs log tree commit diff stats
path: root/html/color-contrast-checker/index.html
blob: 30a1d66013ddb6f7b9e7f0989683f11d1d4e5c05 (plain) (blame)
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>