diff --git a/src/components/page/theming/_utils/color.ts b/src/components/page/theming/_utils/color.ts index c825c8e8604..7511fbb630a 100755 --- a/src/components/page/theming/_utils/color.ts +++ b/src/components/page/theming/_utils/color.ts @@ -133,6 +133,28 @@ const rgbToYIQ = ({ r, g, b }: RGB): number => { return (r * 299 + g * 587 + b * 114) / 1000; }; +const RED = 0.2126; +const GREEN = 0.7152; +const BLUE = 0.0722; +const GAMMA = 2.4; + +const luminance = ({ r, g, b }: RGB) => { + const a = [r, g, b].map((v) => { + v /= 255; + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, GAMMA); + }); + return a[0] * RED + a[1] * GREEN + a[2] * BLUE; +}; + +// Original source: https://stackoverflow.com/a/9733420 +const contrast = (rgb1: RGB, rgb2: RGB) => { + const lum1 = luminance(rgb1); + const lum2 = luminance(rgb2); + const brightest = Math.max(lum1, lum2); + const darkest = Math.min(lum1, lum2); + return (brightest + 0.05) / (darkest + 0.05); +}; + export class Color { readonly hex: string; readonly hsl: HSL; @@ -176,8 +198,11 @@ export class Color { return /(^#[0-9a-fA-F]+)/.test(value.trim()); } - contrast(threshold = 128): Color { - return new Color(this.yiq >= threshold ? '#000' : '#fff'); + contrast(): Color { + const blackContrastRatio = contrast(this.rgb, { r: 0, g: 0, b: 0 }); + const whiteContrastRatio = contrast(this.rgb, { r: 255, g: 255, b: 255 }); + + return new Color(blackContrastRatio >= whiteContrastRatio ? '#000' : '#fff'); } mix(from: string | RGB | HSL | Color, amount = 0.5): Color {