Contrast: Improve automatic contrast text logic to include opacity and background (#34190)

This commit is contained in:
Torkel Ödegaard 2021-05-18 12:32:27 +02:00 committed by GitHub
parent c2ee2f10a7
commit 8ecfad6995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 4 deletions

View File

@ -177,6 +177,10 @@ describe('utils/colorManipulator', () => {
expect(getContrastRatio('#FFF', '#FFF')).toEqual(1);
});
it('Can also take into account opacity for background', () => {
expect(getContrastRatio('#FFF', 'rgba(255,255,255,0.1)', '#000')).toEqual(17.5);
});
it('returns a ratio for dark-grey : light-grey', () => {
//expect(getContrastRatio('#707070', '#E5E5E5'))to.be.approximately(3.93, 0.01);
});

View File

@ -175,12 +175,13 @@ export function recomposeColor(color: DecomposeColor) {
* Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
* @param foreground - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param background - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param canvas - A CSS color that alpha based backgrounds blends into
* @returns A contrast ratio value in the range 0 - 21.
* @beta
*/
export function getContrastRatio(foreground: string, background: string) {
export function getContrastRatio(foreground: string, background: string, canvas?: string) {
const lumA = getLuminance(foreground);
const lumB = getLuminance(background);
const lumB = getLuminance(background, canvas);
return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
}
@ -190,13 +191,23 @@ export function getContrastRatio(foreground: string, background: string) {
*
* Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
* @param color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
* @param background - CSS color that needs to be take in to account to calculate luminance for colors with opacity
* @returns The relative brightness of the color in the range 0 - 1
* @beta
*/
export function getLuminance(color: string) {
export function getLuminance(color: string, background?: string) {
const parts = decomposeColor(color);
let rgb = parts.type === 'hsl' ? decomposeColor(hslToRgb(color)).values : parts.values;
if (background && parts.type === 'rgba') {
const backgroundParts = decomposeColor(background);
const alpha = rgb[3];
rgb[0] = rgb[0] * alpha + backgroundParts.values[0] * (1 - alpha);
rgb[1] = rgb[1] * alpha + backgroundParts.values[1] * (1 - alpha);
rgb[2] = rgb[2] * alpha + backgroundParts.values[2] * (1 - alpha);
}
const rgbNumbers = rgb.map((val: any) => {
if (parts.type !== 'color') {
val /= 255; // normalized

View File

@ -257,7 +257,9 @@ export function createColors(colors: ThemeColorsInput): ThemeColors {
function getContrastText(background: string, threshold: number = contrastThreshold) {
const contrastText =
getContrastRatio(dark.text.maxContrast, background) >= threshold ? dark.text.maxContrast : light.text.maxContrast;
getContrastRatio(dark.text.maxContrast, background, base.background.primary) >= threshold
? dark.text.maxContrast
: light.text.maxContrast;
// todo, need color framework
return contrastText;
}