Starting points for your own code. Run them in the environment shown below.
Node.js 22+ · ES module (.mjs)
Current inputs apply only to fields used by the example. Selected files are not embedded.
Install dependencies
function luminance(hex) {
if (!/^#[0-9a-f]{6}$/i.test(hex)) throw new Error("Use #RRGGBB");
const rgb = [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16) / 255)
.map(v => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
return rgb[0] * 0.2126 + rgb[1] * 0.7152 + rgb[2] * 0.0722;
}
const a = luminance("#ffffff"), b = luminance("#101415");
const ratio = (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
console.log({ ratio, normalTextAA: ratio >= 4.5, largeTextAA: ratio >= 3 });
Put it to work
Compare #b6ebbd with #101415 against AA and AAA text contrast thresholds.
Details that matter
Format and meaning
Convert HEX, RGB, and HSL. Contrast uses two opaque sRGB colors.
Using it correctly
WCAG AA requires 4.5:1 for normal text and 3:1 for large text.
Common pitfalls
The palette varies the base color’s lightness as a starting point. Check each pairing separately.
Supported scope and limits
Opaque HEX, RGB, HSL; WCAG 2.x contrast formula
Split inputs that exceed the limit. If a format or algorithm is unsupported, choose a tool that matches your requirements instead of silently changing the format.