Calculating Color Contrast in TypeScript using Web Content Accessibility Guidelines (WCAG)

Calculating Color Contrast in TypeScript using Web Content Accessibility Guidelines (WCAG)

When developing applications or websites it is important to use high color contrast between the text elements and their background to ensure they are readable for anyone with low vision impairments and color deficiencies.

There might be cases when you want to calculate and display the color contrast ratio. For example, if you are developing a component library or design system and you want to emphasize color usage guidelines with accessibility in mind.

This article shows how you can calculate the color contrast ratio in TypeScript given two colors in either RGB or hex format.

First, let's explore the concept of color contrast ratio visually. If you inspect the color of a text element in Chrome Developer Tools you can see the contrast ratio for that color and whether the contrast is high enough for the text to be considered legible. In the screenshot, color #2db477 against white has a contrast ratio of 2.66 which is not high enough. The Developer tools also recommend two alternative colors that would meet the high contrast color requirements where the ratios are 4.5.1 and 7.0:1 respectively.

Color Contrast In Developer Tools.jpg

What does this AA: 4.5 and AAA: 7.0 mean?

The Web Content Accessibility Guidelines (WCAG) is a standard that defines three levels of accessibility that should be implemented in the following priority:

  • A: This level is required for assistive technology to read and understand a webpage.
  • AA: This is the ideal support and it is required for government and public websites. Normal text should have a color contrast of at least 4.5 to meet this level. Large text, such as headings, as well as icons, should have a color contrast of at least 3.0.
  • AAA: This level offers specialized support for specific audiences. The text should have a color contrast of at least 7.0.

The WCAG defines a procedure which we can use to calculate color contrast. There are many contrast checkers online such as the Chrome Developer Tools, WebAim, and many others that use the procedure described in the WCAG specification.

According to the procedure we first need to measure the luminance of the foreground color and background color. Then calculate the contrast ratio using this formula (L1 + 0.05) / (L2 + 0.05), where L1 represents the luminance of the lighter color and L2 represents the luminance of the darker color.

function contrast(foregroundColor: RGB, backgroundColor: RGB) {
    const foregroundLumiance = luminance(foregroundColor);
    const backgroundLuminance = luminance(backgroundColor);
    return backgroundLuminance < foregroundLumiance
        ? ((backgroundLuminance + 0.05) / (foregroundLumiance + 0.05))
        : ((foregroundLumiance + 0.05) / (backgroundLuminance + 0.05));
};

You can define the RGB type as an array of three numbers. We will use this type when calculating contrast.

type RGB = [number, number, number];

The relative luminance of each color part (red, green and blue) can be calculated using this function:

function luminance(rgb: RGB) {
    const [r, g, b] = rgb.map((v) => {
        v /= 255;
        return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
    });
    return r * 0.2126 + g * 0.7152 + b * 0.0722;
};

If we use hex color format it will be useful to get the RGB value. We can achieve that using the following function:

function getRgbColorFromHex(hex: string) {
    hex = hex.slice(1);
    const value = parseInt(hex, 16);
    const r = (value >> 16) & 255;
    const g = (value >> 8) & 255;
    const b = value & 255;

    return [r, g, b] as RGB;
};

Finally, we can use our functions to calculate the contrast between two colors (e.g. #2db477 and #ffffff):

const foregroundColor = getRgbColorFromHex("#2db477");
const backgroundColor = getRgbColorFromHex("#ffffff");
const contrastRatio = contrast(foregroundColor, backgroundColor);

Alternatively, if you prefer using the RGB type directly:

const contrastRatio = contrast([45, 180, 119], [255, 255, 255]);

To sum up, we looked at how the Web Content Accessibility Guidelines (WCAG) defines three levels of accessibility and what each level requires in terms of color contrast. We then defined functions in TypeScript that calculate color contrast using the formulas in the WCAG specification, given colors in either hex or RGB format.