Skip to content

Instantly share code, notes, and snippets.

@hail2u

hail2u/apca.js

Last active Feb 6, 2021
Embed
What would you like to do?
Get contrast of colors using APCA (Advanced Perceptual Contrast Algorithm)
// https://github.com/Myndex/SAPC-APCA#the-plain-english-steps-are
// Example:
// const contrast = getAPCAContrast("rgb(255, 255, 255)", "rgb(136, 136, 136)");
// This returns `66.89346308821438` (66.893%)
// SAPC-APCA README says:
// > #888 vs #fff • 66.89346308821438
// 80% means 7:1 in WCAG 2.0
// 60% means 4.5:1 in WCAG 2.0
// Web UI: https://hail2u.net/pub/test/702.html
const linearize = (val) => (val / 255.0) ** 2.4;
const clampLuminance = (luminance) => {
const blkThrs = 0.03;
const blkClmp = 1.45;
if (luminance > blkThrs) {
return luminance;
}
return Math.abs(blkThrs - luminance) ** blkClmp + luminance;
};
const getLuminance = (color) => {
const [red, green, blue] = color.match(/\d+/gu);
const y =
0.2126729 * linearize(red) +
0.7151522 * linearize(green) +
0.072175 * linearize(blue);
return clampLuminance(y);
};
const getContrast = (background, foreground) => {
const deltaYmin = 0.0005;
const scale = 1.25;
const backgroundLuminance = getLuminance(background);
const foregroundLuminance = getLuminance(foreground);
if (Math.abs(backgroundLuminance - foregroundLuminance) < deltaYmin) {
return 0.0;
}
if (backgroundLuminance > foregroundLuminance) {
return (backgroundLuminance ** 0.55 - foregroundLuminance ** 0.58) * scale;
}
if (backgroundLuminance < foregroundLuminance) {
return (backgroundLuminance ** 0.62 - foregroundLuminance ** 0.57) * scale;
}
return 0.0;
};
const scaleContrast = (contrast) => {
const loClip = 0.001;
const loConThresh = 0.078;
const loConFactor = 1 / loConThresh;
const loConOffset = 0.06;
const absContrast = Math.abs(contrast);
if (absContrast < loClip) {
return 0.0;
}
if (absContrast <= loConThresh) {
return contrast - contrast * loConFactor * loConOffset;
}
if (contrast > loConThresh) {
return contrast - loConOffset;
}
if (contrast < -loConThresh) {
return contrast + loConOffset;
}
return 0.0;
};
const getAPCAContrast = (background, foreground) => {
const contrast = getContrast(background, foreground);
const scaledContrast = scaleContrast(contrast);
return scaledContrast * 100;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment