Skip to content

Instantly share code, notes, and snippets.

@csandman
Last active May 17, 2022 07:20
Show Gist options
  • Save csandman/5d11582f61c12dce429512b8d4585762 to your computer and use it in GitHub Desktop.
Save csandman/5d11582f61c12dce429512b8d4585762 to your computer and use it in GitHub Desktop.
Calculate the minimum shade or tint of a color in order to get a W3C compliant contrast between them
// https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
function getMinContrastColor(rgb, isLargeFont) {
const minContrastRatio = isLargeFont ? 3.0 : 4.5;
let contrastIsDark = isContrastDark(rgb);
let i = 0.01;
let contrastRgb = [];
let contrastRatio = 0;
while (contrastRatio < minContrastRatio && i < 1) {
contrastRgb = calculateGradient(rgb, contrastIsDark, i);
contrastRatio = getContrastRatio(rgb, contrastRgb, contrastIsDark);
i += 0.01;
}
return contrastRgb;
}
function getContrastRatio(rgb1, rgb2, isDark) {
return isDark
? (getLuminance(rgb1) + 0.05) / (getLuminance(rgb2) + 0.05)
: (getLuminance(rgb2) + 0.05) / (getLuminance(rgb1) + 0.05);
};
function isContrastDark(rgb) {
const isDark = getLuminance(rgb) > Math.sqrt(1.05 * 0.05) - 0.05; // ~= 0.179
return isDark;
}
function getLuminance(rgb) {
const lumRgb = rgb.map(el => {
el = el / 255.000;
return el <= 0.03928 ? el / 12.92 : ((el+0.055)/1.055) ** 2.4;
});
return 0.2126 * lumRgb[0] + 0.7152 * lumRgb[1] + 0.0722 * lumRgb[2];
}
function calculateGradient(rgb, isDark, opacity) {
return rgb.map(val =>
calculateIndividualColor(val, isDark ? 0 : 255, opacity)
);
};
function calculateIndividualColor(color, bColor, opacity) {
return Math.round(opacity * bColor + (1 - opacity) * color);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment