If you are playing around with Sheet colors with Apps Script, you sometimes find yourself with font colors that don’t go well with the background colors you’ve chosen. However, we can use Yiq values to decide whether the luminance of the background color would be best with a light or a dark foreground font color. Here’s a small Apps Script library to figure it out for you.
The library provides a few more methods you might find useful.
Yiq.getContrasts ({backgrounds [, light, dark]})
Given an array of arrays of background colors in hex format ‘#xxxxxx’ , return the same size of array of contrasting foreground colors. See below if you want to do it yourself rather than use Yiq.setFontContrasts.
If you know there’s only 1 color, you can optimize setting colors by using the setFontColor (as opposed to setFontColors), because that’ll make a shorter call to the spreadsheet api.
Yiq.setFontContrasts() does this automatically. It’ll calculate the fontColors for the range’s background colors, and if they are all the same it’ll optimize the call rather than sending a large array of individual cell colors if it can get away with it.
Light and dark settings
By default, Yiq uses #212121 for dark, and #ffffff for light. If you want to change that, each of the methods above take a couple of extra arguments to allow you to change those if you want.
Using different values for dark and light font colors
Basing the background color on the font color
All the examples so far are for the most likely use case, ie basing the font color on the cell background, but if you want to do the reverse (find contrasting background colors for given font colors), you can do this.
Yiq.setBackgroundContrasts({ range})
Basing the background color on the font color
Example
Here’s an screenshot of a collection of contrasting font colors chosen for some random background colors.
Luma, or brightness of the image. Values are in the range [0, 1], where 0 specifies black and 1 specifies white. Colors increase in brightness as Y increases.
I
In-phase, which is approximately the amount of blue or orange tones in the image. I in the range [-0.5959, 0.5959], where negative numbers indicate blue tones and positive numbers indicate orange tones. As the magnitude of I increases, the saturation of the color increases.
Q
Quadrature, which is approximately the amount of green or purple tones in the image. Q in the range [-0.5229, 0.5229], where negative numbers indicate green tones and positive numbers indicate purple tones. As the magnitude of Q increases, the saturation of the color increases.
YIQ meaning
Library code extract
For our purposes, we’re only interested in the Y of Yiq – that’s the luma of the color.
The code for this is in the bmYiq library and looks like this. You’ll find a link to the full code for the library at the end of this article.
/** * turn a number into a #ffffff rgv hex color * @param {number} num turn a number into a #ffffff rgv hex color * @return {string} */ numToColor(num) { return '#' ('000000' (num & 0xFFFFFF).toString(16)).slice(-6) },
// default colors if you want a dark font color get defaultDark() { return '#212121' }, // default colors if you want a light font color get defaultLight() { return '#ffffff' },
// threshold for light vs dark for luma value // 150 appears to be the accepted value get threshold() { return 150 },
/** * get the rgb object * @param {string} color the color * @param {string} p.color color to get the contrast of * @param {string} [p.color=#ffffff] light font color * @param {string} [p.color=#212121] dark font color * @return {object} {r,g,b} */ getRgb(color) { return { r: parseInt(color.substring(1, 3), 16), g: parseInt(color.substring(3, 5), 16), b: parseInt(color.substring(5, 7), 16) } },
/** * get the contrasting font color given a background * @param {object} p oarams * @param {string} p.color color to get the contrast of * @param {string} [p.color=#ffffff] light font color * @param {string} [p.color=#212121] dark font color * @return {string[][]} */ getContrast({ color, light = this.defaultLight, dark = this.defaultDark }) { if (!this.isString(color) || !color.match(/^#[0-9a-f]{6}/i)) throw 'please provide color as a hex string #rrggbb' const { y } = this.getYiq(color) return y > this.threshold ? dark : light },
bruce mcpherson is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Based on a work at http://www.mcpher.com. Permissions beyond the scope of this license may be available at code use guidelines