Skip to content

Instantly share code, notes, and snippets.

@avioli
Created November 10, 2020 23:55
Show Gist options
  • Save avioli/b11813d43a8e5b795b38953f5d7ba3b3 to your computer and use it in GitHub Desktop.
Save avioli/b11813d43a8e5b795b38953f5d7ba3b3 to your computer and use it in GitHub Desktop.
A set of PHP functions to help get either white or black colour for foreground text over a given background
<?php
/**
* Determine if a black or white foreground color will have a better contrast
* ratio for a given background color
*
* @param string|int $bg_r A hex representation of the color
* or the value of the red color 0-255
* @param int $bg_g The value of the green color 0-255
* @param int $bg_b The value of the blue color 0-255
*
* @return string|array Either `#ffffff` or `#000000` if $bg_r was a string,
* otherwise `[255, 255, 255]` or `[0, 0, 0]`
*/
function foreground_color($bg_r, $bg_g = null, $bg_b = null) {
$is_string = is_string($bg_r);
if ($is_string) {
list($bg_r, $bg_g, $bg_b) = hex_to_rgb($bg_r);
}
$contrast = get_contrast($bg_r, $bg_g, $bg_b, 255, 255, 255);
if ($contrast >= 4.5) {
$result = [255, 255, 255];
} else {
$result = [0, 0, 0];
}
if ($is_string) {
$result = array_map('dechex', $result);
$result = array_map(function ($v) { return strlen($v) == 1 ? "{$v}{$v}" : $v; }, $result);
return '#' . implode('', $result);
}
return $result;
}
/**
* Compute contrast ratio between two colors according to WCAG 2.0 AA and AAA
* levels
*
* A result between:
* - 0 and 3 means there is not enough contrast, thus fails WCAG 2.0 test
* - 3 and 4.5 means it passes AA level only for large text (above 18pt or
* bold above 14pt)
* - 4.5 and 7 means it passes AA level for any size text and AAA only for
* large text (above 18pt or bold above 14pt)
* - 7 and 22 means it passes AAA level for any size text
*
* @param int $r1 The value of the first colours' red 0-255
* @param int $g1 The value of the first colours' green 0-255
* @param int $b1 The value of the first colours' blue 0-255
* @param int $r2 The value of the second colours' red 0-255
* @param int $g2 The value of the second colours' green 0-255
* @param int $b2 The value of the second colours' blue 0-255
*
* @return int The contrast ratio between two colours
*/
function get_contrast($r1, $g1, $b1, $r2, $g2, $b2) {
$lum1 = get_luminance($r1, $g1, $b1);
$lum2 = get_luminance($r2, $g2, $b2);
$ratio = ($lum1 + 0.05) / ($lum2 + 0.05);
if ($lum2 > $lum1) {
$ratio = 1 / $ratio;
}
return $ratio;
}
/**
* Compute the relative luminance - the relative brightness of any point in a
* colorspace, normalized to 0 for darkest black and 1 for lightest white
*
* @param int $r The value of the red colour 0-255
* @param int $g The value of the green colour 0-255
* @param int $b The value of the blue colour 0-255
*
* @return int The relative luminance of given colour
*/
function get_luminance($r, $g, $b) {
$rgb = [$r, $g, $b];
foreach ($rgb as $key => $val) {
$val = $val / 255;
$val = $val < 0.03928 ? $val / 12.92 : pow(($val + 0.055) / 1.055, 2.4);
$rgb[$key] = $val;
}
return 0.2126 * $rgb[0] + 0.7152 * $rgb[1] + 0.0722 * $rgb[2];
}
/**
* Convert a HEX color to a RGB array
*
* Both #RGB and #RRGGBB formats are supported, while # is optional.
*
* @param string $hex_color A hex color
*
* @return array|FALSE An [int, int, int] array or FALSE if format is incorrect.
*/
function hex_to_rgb($hex_color) {
$hex_color = trim($hex_color, '#');
$len = strlen($hex_color);
if ($len == 3) {
return array_map(function ($v) { return hexdec("{$v}{$v}"); }, str_split($hex_color));
} else if ($len == 6) {
return array_map('hexdec', str_split($hex_color, 2));
}
return FALSE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment