How to Choose the Font Color Based on the Background
Tags: webdev, css, javascript
Published: 2024-06-06 13:12:43


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](https://getbootstrap.com/docs/5.3/components/buttons/))

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g3uut65iruwrvih9we9z.png)

If I reverse the font color:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xyqe3z4nc9jz2z0c88r.png)

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.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0u5lqrtiakyk4v3grh8.png)

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](https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio): ```
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
- L<sub>black</sub> is `0`
- L<sub>white</sub> is `1`

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

Let's simplify the formula:
<pre>
(L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05) 

(L + 0.05)<sup>2</sup> > (1.05 * 0.05)

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

L > √(1.05 * 0.05) - 0.05

L > 0.179
</pre>

We only need to calculate the `L`. The formula to calculate the relative luminance is given by [W3C Recommendation](https://www.w3.org/TR/WCAG21/#dfn-relative-luminance).

```javascript
// 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.

{% codepen https://codepen.io/louis-7/pen/pomPXVe %}
           

References:
[Web Content Accessibility Guidelines (WCAG) 2.1](https://www.w3.org/TR/WCAG21/)
[Determine the best text color for a given background color](https://ux.stackexchange.com/questions/114952/determine-the-best-text-color-for-a-given-background-color#:~:text=In%20your%20case%20you%20simply,on%20the%20highest%20contrast%20ratio.&text='Pleasing%20to%20the%20eyes'%20is%20very%20subjective)
[How to decide font color in white or black depending on background color?](https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color)