Which Colors Look Good on Black and White?
Tags: a11y, css, design, productivity
Published: 2019-03-21 19:05:00

---
title: Which Colors Look Good on Black and White?
published: true
description: A list of colors that have 4.5:1 contrast ratio against both black and white, and the journey to find them.
tags: a11y, css, design, productivity
cover_image: https://thepracticaldev.s3.amazonaws.com/i/b8syvcqddp9g7ldp8ucs.png
---

Imagine you have to find a color that looks good both on black and white. By "looking good" I mean conforming at least [WCAG AA Contrast (Minimum)](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html), which is a ratio of **4.5:1**.

**tl;dr**: *Scroll to the end to get the list of colors*

# So how is color contrast ratio calculated? 

The [WCAG contrast ratio definition](https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio) says:

> **contrast ratio**
>(L1 + 0.05) / (L2 + 0.05), where
>
> * L1 is the relative luminance of the lighter of the colors, and
> * L2 is the relative luminance of the darker of the colors.
>
> NOTE
> Contrast ratios can range from 1 to 21 (commonly written 1:1 to 21:1).

Simple math, right? You take two values and divide them.

# But how is the relative luminance calculated??

The [WCAG relative luminance definition](https://www.w3.org/TR/WCAG21/#dfn-relative-luminance) says:

> **relative luminance**
> the relative brightness of any point in a colorspace, normalized to 0 for darkest black and 1 for lightest white
>
> NOTE
> For the sRGB colorspace, the relative luminance of a color is defined as L = 0.2126 * **R** + 0.7152 * **G** + 0.0722 * **B** where **R**, **G** and **B** are defined as:
>
> * if R<sub>sRGB</sub> <= 0.03928 then **R** = R<sub>sRGB</sub>/12.92 else **R** = ((R<sub>sRGB</sub>+0.055)/1.055) ^ 2.4
> * if G<sub>sRGB</sub> <= 0.03928 then **G** = G<sub>sRGB</sub>/12.92 else **G** = ((G<sub>sRGB</sub>+0.055)/1.055) ^ 2.4
> * if B<sub>sRGB</sub> <= 0.03928 then **B** = B<sub>sRGB</sub>/12.92 else **B** = ((B<sub>sRGB</sub>+0.055)/1.055) ^ 2.4
>
> and R<sub>sRGB</sub>, G<sub>sRGB</sub>, and B<sub>sRGB</sub> are defined as:
> 
> * R<sub>sRGB</sub> = R<sub>8bit</sub>/255
> * G<sub>sRGB</sub> = G<sub>8bit</sub>/255
> * B<sub>sRGB</sub> = B<sub>8bit</sub>/255

Don't get bamboozled by the equations, it's still simple math! The last three lines are just about normalizing the color channels by dividing them with **255**, here are some examples: 

| Color  | R<sub>8bit</sub> | G<sub>8bit</sub> | B<sub>8bit</sub> | -> | R<sub>sRGB</sub> | G<sub>sRGB</sub> | B<sub>sRGB</sub> |
|--------|---:|---:|---:|---|---:|---:|---:|
| Black  |  0 |  0 |  0 |   |  0 |  0 |  0 |
| White  |255 |255 |255 |   |  1 |  1 |  1 |
| Red    |255 |  0 |  0 |   |  1 |  0 |  0 |
| Purple |128 |  0 |128 |   |0.502 |  0 |0.502 |
| Orange |255 |165 |  0 |   |  1 |0.647 |  0 |

Then getting the **R**, **G**, and **B** values is done by a simple division (divide by 12.92) if the normalized value is less than or equal to 0.03928, or applying the exponential formula above if the normalized value is greater than 0.03928. The final step is summing these three values with different weights.

# Let's calculate!

It's not hard to see that the relative luminance of black is 0, and white is 1. Calculating their contrast ratio gives (1 + 0.05) / (0 + 0.05) = 21. We can calculate any other color's contrast ratio against black and white by the following (where "c" is the color's relative luminance):

* against black: (c + 0.05) / (0 + 0.05)
* against white: (1 + 0.05) / (c + 0.05)

We have a requirement of a contrast ratio of at least 4.5, therefore:

* against black: (c + 0.05) / (0 + 0.05) >= 4.5
* against white: (1 + 0.05) / (c + 0.05) >= 4.5

Solving the two inequalities give:

* against black: c >= 0.175
* against white: 0.1833 >= c

In the end we get the relative luminance requirement of:

**0.175 <= c <= 0.1833**

# The list of colors

Finally, I created a pen which iterates through the RGB color space, from black to white with increments of 0x11 = 17 per channel. Iterating through **all** of the colors would take some time and CPU, and I'm sure listing ~300k colors wouldn't be practical. So the pen iterates through the colors which can be described with 3-digit hexa notation, and lists 76 colors that matches our requirements. [Use them wisely ;)](https://codepen.io/finnhvman/full/bZQLgR)
{% codepen https://codepen.io/finnhvman/pen/bZQLgR %}