diff options
-rw-r--r-- | html/ccc/index.html | 73 |
1 files changed, 60 insertions, 13 deletions
diff --git a/html/ccc/index.html b/html/ccc/index.html index 5637101..9f2e4c6 100644 --- a/html/ccc/index.html +++ b/html/ccc/index.html @@ -6,7 +6,7 @@ <title>Color Contrast Checker</title> <meta name="description" content="Give it a list of colors, find accessible combinations."> <style> - body { font-family: Arial, sans-serif; padding: 20px; } + body { font-family: Arial, sans-serif; padding: 20px; size: 16px;} .color-input { margin-bottom: 10px; width: 100%; height: 100px; font-size: 16px; /* Prevent iOS zoom */ } .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; } @@ -87,8 +87,9 @@ </header> <main> <form id="colorForm" onsubmit="event.preventDefault(); updateColors();"> - <label for="colors">Enter colors as hex, rgb, or rgba values as either a comma separated list or a newline separated list:</label> + <label for="colors">Colors you want to check for contrast:</label> <textarea id="colors" class="color-input" required placeholder="Feel free to mix and match hex, rgb, and rgba values!"></textarea> + <p style="margin-top: 0;">You can enter colors as either a comma separated list or a newline separated list. Colors can be in hex (#RGB or #RRGGBB), rgb(r,g,b), or rgba(r,g,b,a) format. You. can mix and match color formats, too.</p> <div class="form-controls"> <button type="submit">Check Contrast</button> <div class="checkbox-controls"> @@ -103,7 +104,7 @@ <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> + <p>Color Contrast Checker — A tool to help you find accessible color combinations.</p> <p>This tool can only determine if two color combinations have a mathmatically correct contrast ratio. Digital accessiblity isn't just about math, though, so use this as a starting place, not as the final word on if a color combination is accessible.</p> </footer> @@ -218,41 +219,76 @@ // ===== Contrast Calculation and WCAG Compliance ===== function getContrastRatio(color1, color2) { + // Calculate relative luminance using WCAG formula + // L = 0.2126 * R + 0.7152 * G + 0.0722 * B + // where R, G, and B are the red, green, and blue values of the color + // the formula normalizes the RGB values to a 0-1 range + // and then applies coefficients to represent human perception of color + // Returns a value between 0 and 1, where 0 is darkest and 1 is brightest function luminance(r, g, b) { - let a = [r, g, b].map(v => { + // Convert RGB values to sRGB colorspace values + let channels = [r, g, b].map(v => { + // Step 1: Normalize RGB values to 0-1 range v /= 255; - return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; + + // Step 2: Convert to sRGB using WCAG formula + // If value is small (≤ 0.03928), use simple linear calculation + // Otherwise, use the more complex power formula + return v <= 0.03928 + ? v / 12.92 + : Math.pow((v + 0.055) / 1.055, 2.4); }); - return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; + + // Step 3: Apply luminance coefficients + // These coefficients represent human perception of color + // Red contributes 21.26%, Green 71.52%, and Blue 7.22% to perceived brightness + return channels[0] * 0.2126 // Red coefficient + + channels[1] * 0.7152 // Green coefficient + + channels[2] * 0.0722; // Blue coefficient } - // Parse colors and handle alpha compositing with white background + // Handle colors with transparency by compositing them with white background const parseAndComposite = (color) => { const parsed = parseColor(color); + // If color is fully opaque, return as is if (parsed.a === 1) return parsed; - // Composite with white background + // For semi-transparent colors, composite with white background + // Using alpha compositing formula: result = (foreground × alpha) + (background × (1 - alpha)) const alpha = parsed.a; return { - r: (parsed.r * alpha) + (255 * (1 - alpha)), - g: (parsed.g * alpha) + (255 * (1 - alpha)), - b: (parsed.b * alpha) + (255 * (1 - alpha)), - a: 1 + r: (parsed.r * alpha) + (255 * (1 - alpha)), // Blend red channel + g: (parsed.g * alpha) + (255 * (1 - alpha)), // Blend green channel + b: (parsed.b * alpha) + (255 * (1 - alpha)), // Blend blue channel + a: 1 // Result is fully opaque }; }; + // Process both colors, handling any transparency const rgb1 = parseAndComposite(color1); const rgb2 = parseAndComposite(color2); + // Calculate luminance for both colors const lum1 = luminance(rgb1.r, rgb1.g, rgb1.b); const lum2 = luminance(rgb2.r, rgb2.g, rgb2.b); - return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05); + // Calculate contrast ratio using WCAG formula: + // (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter color's luminance + // The 0.05 constant prevents division by zero and handles very dark colors + const lighter = Math.max(lum1, lum2); + const darker = Math.min(lum1, lum2); + return (lighter + 0.05) / (darker + 0.05); } + + // This is an incomplete check since it is only dealing with color, and doesn't consider font sizing + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast function getWCAGLevel(contrast) { + // Level AAA: Contrast ratio of at least 7:1 if (contrast >= 7) return "AAA"; + // Level AA: Contrast ratio of at least 4.5:1 if (contrast >= 4.5) return "AA"; + // Failing: Contrast ratio below 4.5:1 return "Fail"; } @@ -370,6 +406,17 @@ }); } + + + + + + + + + + + // ===== Testing Functions ===== function runContrastTests() { const testCases = [ |