Louis Liu

Posted on

How to Choose the Font Color Based on the Background

In the web browser, the font color is set to black by default. Have you ever thought about why? It's not rocket science, just because of the background and the text has high contrast so you can tell apart the text and the background.

When the background is white, you use the black font and when it's black you choose the opposite. But what if the background is not black and white but other colors? (Suppose we only have the black and white font in this world ππ)

The text on these buttons looks harmonious, right? (I take them from the Bootstrap document)

If I reverse the font color:

You still can see the text on most of the buttons but the contrast becomes lower.

Here's another example. On the dark blue background, the white font is easier to read. However, the black font is a better choice on the water blue background.

As a software engineer, I always implement the UI based on the design, I barely think about how the designer chooses the font color until a question comes up in my mind. What if I want to display a text on the background that will change its color dynamically? Like, on Monday it's white but on Tuesday it changes to blue.

How do you choose the font color?

We can choose font color based on the background luminance. According to the contrast ratio formula given by W3C Recommendation:

``````contrast ratio = (L1 + 0.05) / (L2 + 0.05)
``````
• L1 is the relative luminance of the lighter of the colors, and
• L2 is the relative luminance of the darker of the colors
• Lblack is `0`
• Lwhite is `1`

I will use `L` to represent the luminance of the background color. Now, we can say if `(L + 0.05) / (Lblack + 0.05) > (Lwhite + 0.05) / (L + 0.05)` we can use black(#000000) as the font color. Otherwise, use white(#ffffff).

Let's simplify the formula:

```(L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05)

(L + 0.05)2 > (1.05 * 0.05)

(L + 0.05) > β(1.05 * 0.05)

L > β(1.05 * 0.05) - 0.05

L > 0.179
```

We only need to calculate the `L`. The formula to calculate the relative luminance is given by W3C Recommendation.

``````// suppose background color in hexadecimal formart: #ff00ee
function textColorBasedOnBackground(backgroundColor) {
backgroundColor = backgroundColor.substring(1);
const r = parseInt(backgroundColor.substring(0,2), 16); // 0 ~ 255
const g = parseInt(backgroundColor.substring(2,4), 16);
const b = parseInt(backgroundColor.substring(4,6), 16);

const srgb = [r / 255, g / 255, b / 255];
const x = srgb.map((i) => {
if (i <= 0.04045) {
return i / 12.92;
} else {
return Math.pow((i + 0.055) / 1.055, 2.4);
}
});

const L = 0.2126 * x[0] + 0.7152 * x[1] + 0.0722 * x[2];
return L > 0.179 ? "#000" : "#fff";
}
``````

Example

Here's an example. You can observe the font color while changing the background color.

Fabio Giolito • Edited

Soon we'll be able to do this in CSS with `color-contrast`.

``````.text-contrast {
color: color-contrast(var(--bg-color) vs white, black);
}
``````

You can add any number of colors and it will pick the one with better contrast automatically.

developer.mozilla.org/en-US/docs/W...

@fabiogiolito thanks for sharing this! This function is cool, it can compare colors other than black and white.

lionel-rowe

Omg that's incredible news! Hope implementations start cropping up soon.

Nice demo! Here's a version that I think is a bit more self-documenting and also rejects invalid inputs:

``````const HEX_COLOR_MATCHER = /^#(?<r>\p{AHex}{2})(?<g>\p{AHex}{2})(?<b>\p{AHex}{2})\$/u

function relativeLuminance(srgb) {
const [R, G, B] = srgb.map((i) => i <= 0.04045 ? i / 12.92 : ((i + 0.055) / 1.055) ** 2.4)
return 0.2126 * R + 0.7152 * G + 0.0722 * B
}

function hexToSrgb(hex) {
const match = hex.match(HEX_COLOR_MATCHER)
if (!match) throw new Error('Not a valid 6-digit hex color')
const { r, g, b } = match.groups

return [r, g, b].map((x) => parseInt(x, 16) / 255)
}

function contrastingColor(hex) {
return relativeLuminance(hexToSrgb(hex)) > 0.179 ? '#000000' : '#ffffff'
}
``````

`\p{AHex}` in a Unicode-aware regex matches any ASCII hex digit.

Louis Liu

@lionelrowe I was going to create a gist to provide a comprehensive function. Yours is awesome!