Skip to content

Instantly share code, notes, and snippets.

@typeofweb
Created April 2, 2025 11:14
Show Gist options
  • Save typeofweb/d0367e039ad9637bda6228211fb9b5c7 to your computer and use it in GitHub Desktop.
Save typeofweb/d0367e039ad9637bda6228211fb9b5c7 to your computer and use it in GitHub Desktop.
Contrast Ratio
type RGBA = { r: number, g: number, b: number, a: number };
const fg = { r: 255, g: 0, b: 0, a: 0.5 }; // semi-transparent red
const bg = { r: 255, g: 255, b: 255, a: 1 }; // white
console.log(contrastRatio(fg, bg)); // 2.44
// ––––––––––––
function contrastRatio(fg: RGBA, bg: RGBA) {
function blend(fg: RGBA, bg: RGBA) {
const alpha = fg.a + bg.a * (1 - fg.a);
const r = (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / alpha;
const g = (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / alpha;
const b = (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / alpha;
return { r, g, b };
}
function luminance({ r, g, b }: RGBA) {
const toLinear = (c) => {
c /= 255;
return c <= 0.03928
? c / 12.92
: Math.pow((c + 0.055) / 1.055, 2.4);
};
const R = toLinear(r);
const G = toLinear(g);
const B = toLinear(b);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
const fgBlended = blend(fg, bg);
const bgOpaque = { r: bg.r, g: bg.g, b: bg.b }; // ignore bg.a here
const L1 = luminance(fgBlended);
const L2 = luminance(bgOpaque);
const lighter = Math.max(L1, L2);
const darker = Math.min(L1, L2);
return (lighter + 0.05) / (darker + 0.05);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment