Contrast Checker

Use the InputColor control to select colors and the Color class to inspect the colors and check if they provide enough contrast to comply with accessibility standards.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import { InputColor } from '@grapecity/wijmo.input'; import { Color, toggleClass, format } from '@grapecity/wijmo'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create InputColor controls let cpFore = new InputColor('#icFore', { value: 'black', valueChanged: updateRatio }); let cpBack = new InputColor('#icBack', { value: 'white', valueChanged: updateRatio }); updateRatio(); // update the contrast ratio when the colors change function updateRatio() { // get lightness ratio let lFore = getRelativeLuminance(cpFore.value); let lBack = getRelativeLuminance(cpBack.value); let ratio = (Math.max(lFore, lBack) + .05) / (Math.min(lFore, lBack) + 0.05); // show sample text let sample = document.querySelectorAll('.sample'); for (let i = 0; i < sample.length; i++) { let style = sample[i].style; style.color = cpFore.value; style.background = cpBack.value; } // show the result let e = document.getElementById('ratio'); e.innerHTML = format('<b>{ratio:g1}</b>:1', { ratio: ratio }); e.style.borderColor = ratio < 7 ? 'whitesmoke' : 'darkgreen'; pass('aa-normal', ratio >= 4.5); pass('aa-large', ratio >= 3); pass('aaa-normal', ratio >= 7); pass('aaa-large', ratio >= 4.5); } function pass(id, pass) { let e = document.getElementById(id); e.textContent = pass ? 'Pass' : 'Fail'; toggleClass(e, 'fail', !pass); } // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance function getRelativeLuminance(color) { let c = new Color(color); let r = getChannel(c.r); let g = getChannel(c.g); let b = getChannel(c.b); return (r * 0.2126 + g * 0.7152 + b * 0.0722); } function getChannel(rgb) { rgb /= 255; return rgb <= 0.03928 ? rgb / 12.92 : Math.pow((rgb + 0.055) / 1.055, 2.4); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity InputColor Contrast Checker</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <h1> Contrast Checker </h1> <div class="row"> <div class="col-md-4"> <h3> Foreground Color </h3> <div id="icFore"></div> <h3> Background Color </h3> <div id="icBack"></div> </div> <div class="col-md-4"> <h3> Contrast Ratio </h3> <div id="ratio"></div> <h3> Result </h3> <p class="sample"> Normal Text </p> <p> WCAG AA: <span id="aa-normal" class="result"></span> WCAG AAA: <span id="aaa-normal" class="result"></span> </p> <p class="sample large"> Large Text </p> <p> WCAG AA: <span id="aa-large" class="result"></span> WCAG AAA: <span id="aaa-large" class="result"></span> </p> </div> </div> <h3> Explanation </h3> <p> WCAG 2.0 level AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text. </p> <p> WCAG 2.1 requires a contrast ratio of at least 3:1 for graphics and user interface components (such as form input borders). </p> <p> WCAG Level AAA requires a contrast ratio of at least 7:1 for normal text and 4.5:1 for large text. </p> <p> For more details, please see <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance" target="_blank"> The Science of Color Contrast </a>. </p> </div> </body> </html>
#sample { padding: 12px; font-style: bold; } #ratio { font-size: 200%; padding: 12px; font-style: bold; text-align: center; border-radius: 4px; border: 2px solid whitesmoke; } .sample { padding: 6px; margin: 6px 0; border: 2px solid whitesmoke; } .large { font-size: 14pt; font-weight: bold; } .result { font-weight: bold; margin-right: 2em; padding: 3px 6px; border-radius: 6px; color: white; background: darkgreen; } .result.fail { background: darkred; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);