Contrast Matching Tool

06 August 2023
Try Contrast matching tool | Github Repository

Colors are tricky. If you have experience building accessible color ramps in your design system, you will know this as the cold hard truth. For those unaware, our goal here is to create colors in each theme as pairs matching color contrast. This creates confidence for designers to create a design in one theme and know that the other themes will work just the same with little to no testing.

There are many discussions online about how to build color systems with color contrast and accessibility in mind, but when it comes to multiple product themes the challenge increases. With color ramp generators, we can still only get the primary color theme, however for secondary themes we can't just use the generator again, since the colors won't match. For example a nice blue color contrast won't match with a violet color.

Color HSL1 Contrast HSL2 Contrast
Background 208, 22, 15
001 200, 100, 85 11.21 260, 100, 85 8.20
002 200, 100, 70 8.39 260, 100, 70 4.11
003 200, 100, 55 6.38 260, 100, 55 2.27
004 200, 100, 35 3.08 260, 100, 35 1.31
005 200, 100, 15 1.14 260, 100, 15 1.20

As you can see from the table above, only changing the Hue in HSL (or HSB) will not solve our problems. The current method to create such sub-level theme is a very manual process. Currently we have to go through our primary ramp color by color, using each as a foundation, to then find a new color to fit the secondary or tertiary theme slots, all while trying to balance saturation and lightness and contrast. If you're like me, you probably discovered early on that HSB/HSL makes this process a bit easier.

Others have tried a process whereby they attempt to match the gray value in each pair. This is not an efficient method to pairing up colors since matching the gray value doesn't guarantee a matching contrast ratio.

Color HSL Contrast Grayscale Contrast
Background 208, 22, 15 - - -
Primary 60, 100, 50 14.14 60, 0, 50 3.84
Secondary 256, 84, 98 14.14 256, 0, 98 14.51

We can see from the example that even if the color contrasts match, the grayscale can have a vastly different results. Therefore, we cannot rely on matching colors through their gray values as a 'fast' matching method.

Calculated vs Perceived contrasts

Did you know there are two types of contrast ratios to calculate? I only just learned about this while researching to create this tool. The calculated contrast is what we are familiar with see in tools, however, I recently learned there is a way to calculate the perceived contrast; which is what most people will actually see as matching contrasts

Of course, what we actually see may not be reliable; which is why it is so difficult to make the themes. However, using the perceived contrast for decorative colors could be a great use case, whereby legibility is not necessary. The differences can be subtle, but they are definitely there.

Perceived and Calculated contrast display
Color HSL Contrast
Background 0, 100, 100 -
Primary 144, 100, 70 1.28
Calculated 300, 100, 91 1.33
Perceived 300, 97, 84 1.48

Contrast matching system

Now you can see why it can be so troublesome to find colors, especially manually. That's why I thought there has to be a better way. I created this tool in python that takes in the HSL values for a foreground, background and the new hue value for a secondary theme.

Please enter the foreground color HSL values (0, 0, 0): 144,100,70 
Please enter the background color HSL values (0, 0, 0): 208,22,15  
|         First pair results         |
Contrast calculated is: 11.858
Contrast perceived is:  10.437
Please enter the new foreground Hue to match (0 - 259): 300

This tool will then output two new colors, one for calculated and one for perceived contrast. The tool also prioritizes saturation to try to get a color as close to the saturation as possible from the original.

Calculated contrast match:
H:300, S:100, L:92
Contrast ratio: 11.812
Contrast difference from origin: 0.046
Perceived contrast match:
H:300, S:97, L:85
Contrast ratio: 10.537
Contrast difference from origin: 0.1

How does it work?

The program takes the new Hue and the saturation of the original color as a starting point. It then checks each lightness value, if none match within the threshold, it widens its search across the saturation range, and keeps iterating until a match is found. If a match is found, it will stop immediately, giving you the result nearest the saturation and lightness of the original color.

A visual representation of the search algorithm

It is entirely possible that within the range given, a color matching cannot be found, in that case you may want to adjust the threshold. I have found the sweet spot is .01, but you can narrow or widen this threshold. This basically says that the contrast of your new color must be within ±.01 of the primary color it's being paired with.

Another point to look out for is if the saturation or lightness has a large margin from your original color. Usually this indicates the threshold is too small, and you may want to allow for more wiggle room.

Let's now fill in the diagram from the start and see what this program will produce as matches.

Color HSL1 Contrast HSL2 Contrast
Background 208, 22, 15 Threshold: ±0.01
001 200, 100, 85 11.289 260, 89, 92 11.280+.09
002 200, 100, 70 8.421 260, 92, 85 8.429+.08
003 200, 100, 55 6.432 260, 94, 79 6.429-.03
004 200, 100, 35 3.099 260, 93, 63 3.097+.02
005 200, 100, 15 1.142 260, 100, 18 1.142+.00
Filled color ramps for both primary and secondary theme

Not half bad if I do say so myself! So what is the future of this tool you ask? If there is interest, I may add some functionality to bulk find colors, fine tune tweaking, and of course fix any obvious issues users find! There are definitely some optimizations that can be made, but for now it's a good tool to play around with.

Contrast Matching Tool

Welcome to the color contrast matching tool. This will find a color to match the same contrast as your existing foreground color, given the hue of the new color to be generated.

Foreground color:
Background color:
New Hue:
If results are not as expected, it's recommended to set the threshold t one of these values: .01, .005 or .001


Primary color
Secondary Calculated
Secondary Perceived