Building a Color Contrast Checker in the Terminal

What if I could double-check that color combinations are passing the AA and/or AAA standards without leaving my code? This bash script will allow us to quickly pass in two colors and determine if they are good to go, or need to be tweaked.

Building a Color Contrast Checker in the Terminal

Color contrast is one of the easiest accessibility features that you can make sure works on your website. People with low vision can have a hard time reading your text if the text color doesn't have enough contrast compared to the background color behind it.

I always have my terminal open while I'm working on templates, and so I want a quick and easy way to double-check the color combos I'm using right where I already am. Let's build our own color contrast checker using the API provided (for free!) by WebAIM.

Watch the video walkthrough or continue reading below!

Getting the Colors from Input

We know that we need to compare two colors, so let's grab them right from the input:

#!/bin/bash

FOREGROUND="${1//#/}"
BACKGROUND="${2//#/}"

The syntax here: "${1//#/}" is a way of sanitizing our input, meaning that we can put any hex code regardless of whether it has the "#" and the beginning. This is using a pattern replacement: whatever is after the first two // is what we're looking for, then whatever is after the single / is what it will be replacing.

For example, let's say we have a string, this, and we want to change it to that instead. We can use the pattern replacement like this:

$mystr="this"
echo ${mystr//is/at}

Here's a quick demo:
https://youtube.com/shorts/Jgp7Paxciec?si=gckvrPitrjQLNbO8

Make Your Script Executable for Testing

Before we continue, let's make sure we can run our script and test that everything is working. In your terminal, go to the directory where your script is. Then use the command:

chmod +x checkcontrast.sh

Now that we can run our script, let's add a quick test to our script, just below our FOREGROUND and BACKGROUND assignments:

echo "FG: ${FOREGROUND}"
echo "BG: ${BACKGROUND}"

Now, back to your terminal, let's try running the script. I'm going to use two different hex codes, one will have the # and one will not:

./checkcontrast #123123 ffffff

Hit enter and you should see the following output:

FG: 123123
BG: ffffff

Now that we know the script is working and it is correctly formatting our input (removing the #), we can comment out the echo statements to keep things clean.

The API

We'll use the free API provided by WebAIM. We'll put the endpoint into our script in the variable API_URL, and insert the FOREGROUND and BACKGROUND colors that we just got from the input.

API_URL="https://webaim.org/resources/contrastchecker/?fcolor=$FOREGROUND&bcolor=$BACKGROUND&api"

Now we can call that URL and collect the result by running a curl command and assigning the response to a new variable:

RESULT=$(curl "$API_URL")

Now we can echo out that RESULT

echo $RESULT

...and you should see something like this if you run the script:

{"ratio":"8.59","AA":"pass","AALarge":"pass","AAA":"pass","AAALarge":"pass"}

Formatting the Output

We have successfully checked the contrast between two colors, but it's not very human-readable, so let's have our script format it so it's really easy to tell where we've passed or failed.

We're going to modify our echo $RESULT statement to do this. First, let's get rid of any characters that we won't need: {}". Those three characters are for JSON syntax, but they don't provide any significance in terms of readability, so we can get rid of them to start cleaning up the output.

The tr utility's -d will help us quickly delete those three characters:

echo $RESULT | tr -d '{}"'

Running our script as-is will now give us this:

ratio:14.1,AA:pass,AALarge:pass,AAA:pass,AAALarge:pass

Next, we'll want to put each category of standards on its own line. We can use the sed utility to look for any commas (,) and replace them with a new line (\n):

echo $RESULT | tr -d '{}"' | sed 's/,/\n/g'

Now our output looks like this:

ratio:14.1
AA:pass
AALarge:pass
AAA:pass
AAALarge:pass

Finally, we can split each of our categories into two columns, using the colon character (:) as the place to make the split. We'll use awk to help us do this:

echo $RESULT | tr -d '{}"' | sed 's/,/\n/g' | awk -F: '{printf "%-10s %s\n", $1, $2}'

Which will result in two columns, the first having 10 characters of width, followed by the second column:

ratio      14.1
AA         pass
AALarge    pass
AAA        pass
AAALarge   pass

Breaking Down the awk Call

We started with the -F: flag, which tells awk to look through each of our category sets and find the colon (:) to make the split. For example, the first line goes from this:

ratio:14.1

...to this:
ratio (group one, or $1)
14.1 (group two, or $2)

Then we use the print formatting statement (printf) to help us format our groups. We start by assigning our two groups to two string placeholders (we'll have to add the newline character \n back, so we'll put that there in advance):

{printf "%s %s\n", $1, $2}

The %s acts as a placeholder, and we tell it what it should be replaced with outside of the "..." in the order that it should be replaced. In this case, we have two string placeholders inside the quotes, and we'll replace the first one with our group one ($1) and the second placeholder gets replaced by our group 2 ($2).

To make the first group feel more like a column, we'll modify its placeholder to have a max-width of 10 characters (just a little more than our longest value). We also want it to be left-aligned, so we us the hyphen (-) in front of the character width (10) like this:

"%-10s %s\n"

Final Touches: Color

Our script is working perfectly now, and it's giving us readable output. We can make one more change to make this a pro-level script: add color to our pass or fail values.

By adding conditional logic to our awk statement, we can check if our second group/column has the word "pass" in it, and set the color to green, or if it has the word "fail," set it to red.

Focusing just on the awk statement, we'll add and if/else chain. Starting with if the second group contains "pass," we'll use this expression, where the tilde (~) means "contains": if ($2 ~ /pass/). Then set a variable called color to the ANSI Escape Code color for green.

We'll do the same for the word "fail" and if neither pass/fail is in the group, we set the color to an empty string.

The reset variable will be needed to change the color back to the default color at the end of the second column, so that the color doesn't leak onto all of the characters after it.

Finally, we update our printf statement to surround the second column with our color, the actual content of the column $2, and then the reset.

... awk -F: '
{
    if ($2 ~ /pass/) {
        color="\x1b[32m"; # Green for pass
    } else if ($2 ~ /fail/) {
        color="\x1b[31m"; # Red for pass
    } else {
        color=""; 
    }
    reset="\x1b[0m"; # Reset color
    printf "%-10s %s%s%s\n", $1, color, $2, reset;
}
'

Now our output should give us colored pass/fail values for each accessibility standard, making it really easy to determine whether our color is accessible enough.

Screenshot of terminal output with colored pass/fail values

The Final Script

#!/bin/bash

FOREGROUND="${1//#/}"
BACKGROUND="${2//#/}"

# echo "FG: ${FOREGROUND}"
# echo "BG: ${BACKGROUND}"

API_URL="https://webaim.org/resources/contrastchecker/?fcolor=$FOREGROUND&bcolor=$BACKGROUND&api"

RESULT=$(curl -s "$API_URL")

echo $RESULT | tr -d '{}"' | sed 's/,/\n/g' | awk -F: '
{
    if ($2 ~ /pass/) {
        color="\x1b[32m"; # Green for pass
    } else if ($2 ~ /fail/) {
        color="\x1b[31m"; # Red for pass
    } else {
        color=""; 
    }
    reset="\x1b[0m"; # Reset color
    printf "%-10s %s%s%s\n", $1, color, $2, reset;
}
'