# Dynamically changing the text color based on background color contrast with vanilla JS

Last month, we looked at a technique for generating random colors with vanilla JS.

Reader Stephen Flannery showed me a demo he built with the technique where the text color changed from black to white if the color was too dark.

## Checking color contrast with vanillia JS

When I asked Stephen how he did it, he pointed me to this article from Brian Suda at 24 Ways on checking color contrast. In it, Brian shares this technique.

The second equation is called ‘YIQ’ because it converts the RGB color space into YIQ, which takes into account the different impacts of its constituent parts…

You’ll notice first that we have broken down the hex value into separate RGB values. This is important because each of these channels is scaled in accordance to its visual impact. Once everything is scaled and normalized, it will be in a range between zero and 255. Much like the previous ’50%’ function, we now need to check if the input is above or below halfway. Depending on where that value is, we’ll return the corresponding highest contrasting color.

And here’s a helper function he wrote to go with the technique.

``````function getContrastYIQ(hexcolor){
var r = parseInt(hexcolor.substr(0,2),16);
var g = parseInt(hexcolor.substr(2,2),16);
var b = parseInt(hexcolor.substr(4,2),16);
var yiq = ((r*299)+(g*587)+(b*114))/1000;
return (yiq >= 128) ? 'black' : 'white';
}
``````

The helper function is really neat. Let’s build on it.

## Adding flexibility to our contrast checker function

Brian’s function only accepts six-character hexcolors, and they cannot have a leading hash (`#`).

Let’s first modify it to accept a leading hash. We’ll use `Array.slice()` to get the first character and check if it equals `#`. If it does, we’ll use `Array.slice()` again to remove the leading hash and redefine `hexcolor`.

``````var getContrast = function (hexcolor){

// If a leading # is provided, remove it
if (hexcolor.slice(0, 1) === '#') {
hexcolor = hexcolor.slice(1);
}

// Convert to RGB value
var r = parseInt(hexcolor.substr(0,2),16);
var g = parseInt(hexcolor.substr(2,2),16);
var b = parseInt(hexcolor.substr(4,2),16);

// Get YIQ ratio
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;

// Check contrast
return (yiq >= 128) ? 'black' : 'white';

};
``````

Next, let’s modify the function to allow both three and six-character colors.

To do that, we’ll first check the length of `hexcolor`. If it’s `3`, we’ll use `Array.split()` to convert the `hexcolor` string into an array of characters. Then, we’ll use `Array.map()` to double each character, and `Array.join()` to combine it back into a string.

``````/*!
* Get the contrasting color for any hex color
* (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com
* Derived from work by Brian Suda, https://24ways.org/2010/calculating-color-contrast/
* @param  {String} A hexcolor value
* @return {String} The contrasting color (black or white)
*/
var getContrast = function (hexcolor){

// If a leading # is provided, remove it
if (hexcolor.slice(0, 1) === '#') {
hexcolor = hexcolor.slice(1);
}

// If a three-character hexcode, make six-character
if (hexcolor.length === 3) {
hexcolor = hexcolor.split('').map(function (hex) {
return hex + hex;
}).join('');
}

// Convert to RGB value
var r = parseInt(hexcolor.substr(0,2),16);
var g = parseInt(hexcolor.substr(2,2),16);
var b = parseInt(hexcolor.substr(4,2),16);

// Get YIQ ratio
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;

// Check contrast
return (yiq >= 128) ? 'black' : 'white';

};
``````

## Bringing it all together

Here’s a demo on CodePen. Reload the page to get a new color. You can download the modified helper function on the Vanilla JS Toolkit.

This technique works in all modern browsers, and IE9 and above.