4, use white. * * @param string $hex Background colour (3- or 6-digit hex, with or without #) * @param string $light Return value for “white” (default '#ffffff') * @param string $dark Return value for “black” (default '#000000') * @return string */ function contrast_text_color(string $hex, string $light = '#ffffff', string $dark = '#000000'): string { // --- normalise ---------------------------------------------------- $hex = ltrim($hex, '#'); if (strlen($hex) === 3) { // #abc → #aabbcc $hex = preg_replace('/./', '$0$0', $hex); } [$r, $g, $b] = [ hexdec(substr($hex, 0, 2)) / 255, hexdec(substr($hex, 2, 2)) / 255, hexdec(substr($hex, 4, 2)) / 255, ]; // --- convert sRGB → linear RGB ----------------------------------- $linear = function (float $c): float { return $c <= 0.04045 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4); }; $R = $linear($r); $G = $linear($g); $B = $linear($b); // --- relative luminance (ITU-R BT.709) ---------------------------- $L_bg = 0.2126 * $R + 0.7152 * $G + 0.0722 * $B; // 0–1 // --- contrast ratios vs black (L=0) and white (L=1) --------------- $contrast_black = ($L_bg + 0.05) / 0.05; // bg lighter than black $contrast_white = 1.05 / ($L_bg + 0.05); // white vs bg // --- pick the winner --------------------------------------------- $useDark = $contrast_black >= $contrast_white; // override rule if dark is true but white "looks better" if ($useDark && $contrast_black < 5.5 && $contrast_white > 4) { $useDark = false; // switch to white } return $useDark ? $dark : $light; } }