Sass {Contrast} Functions

@gianablantin

Sass Tools for
Contrasting Colors

check-contrast(#6caf93, #9d5937, AALG)
=> false

fix-color(#b4e7ff, #fff, AAA)
=> #465c67

fix-contrast(#f476a6, #b6b6b6, 4.5, 75)
=> #833d58, #c8c8c8

best-contrast(#8ce2d4, #fff, #000, 4.5, 4.5)
=> #4c7f76
A very colorful illustrated leaving painted (and color-corrected) footprints

Illustration by Mango

A collection of functions to make it easy to test the contrast between colors and tweak them until they pass your specified requirements. Formulas are based on the WCAG 2.0 guidelines.

See the randomized test pen for a quick visual demo with the full source code. View this project's source for an example setup and to download the relevant file.

Note: This is not a dynamic, front-end tool! These are Sass functions that take input colors and, at the time of compilation, return corrected colors. This demo assumes you're comfortable with Sass.

Check the contrast between two colors

// Function format
check-contrast($color1, $color2, $min-ratio, $return-ratio)
=> boolean (default) or number

// Example returning true/false & using a number as $min-ratio
check-contrast(#2aaabf, #2b2f31, 3)
=> true

// Example returning ratio & using a keyword as $min-ratio
check-contrast(#ffbb9b, #91966d, AAALG, true)
=> 1.8908

// Example usage
.image-caption {
  // If contrast passes, use $caption-color, otherwise use #fff
  color: if( check-contrast($caption-color, $caption-background, AAA), $caption-color, #fff );
}

Pass any two colors as the first two arguments; order is unimportant. If $color2 isn't passed, it defaults to #fff. Any color format (hex, rgb, hsl, hex value) is supported.

Pass a target ratio as the third argument ($min-ratio); if ommitted, defaults to AA, which is 4.5. Accepted values for this ratio argument are AA and AAALG (4.5:1), AALG (3:1) and AAA (7:1) or a number between 1 and 21.

Setting the fourth argument ($return-ratio) to true returns the current ratio of your two colors. Useful if you want to check how screwed you are.

Sample image caption

Fix a color

// Function format. Only $color1 will be fixed
fix-color($color1, $color2, $min-ratio, $iterations)
=> color

// Example using a number a $min-ratio
fix-color(#00bdd1, #005da3, 3.75)
=> #00d4ea

// Example using a keyword as $min-ratio
fix-color(#3f464a, #678d7e, AAA)
=> #000

// Example usage
.image-caption {
  color: fix-color($caption-color, $caption-background);
}

This function modifies the first color's luminance (saturation and lightness) to properly contrast with the second color. Pass the color to fix as the first argument, and the color to compare as your second argument.

The third argument is the luminance ratio required between the two numbers. It takes a number between 1–21 or the previously mentioned keywords.

The fourth argument can usually be ignored. You'll only want to increase this number (from a default of 5) if the fixed color you're getting isn't quite right. What it does is scale the lightness by 0.1% in a loop by the iterations you specify or until the contrast is correct.

Sample image caption

Fix both colors

// Function format. Both colors will be modified by percentage specified with $balance
fix-contrast($color1, $color2, $min-ratio, $balance)
=> list

// Example favoring $color1 change over $color2
fix-contrast(#aaf901, #bbffc8, 6, 70%)
=> #460, #cfffd9

// Example splitting the difference between both colors equally
fix-contrast(#e5eae1, #9dffff, 3)
=> #8b9089, #ceffff

// Example usage
.image-caption {
  // This function returns a list of two colors. You'll need to use nth() to get a single color
  $colors: fix-contrast($caption-color, $caption-background, 5, 60%);
  background: nth($colors, 2);
  color: nth($colors, 1);
}

This is same as the fix-color() function, except it allows you to modify both colors at once. Pass your colors as the first two arguments, and your target ratio as the third.

The fourth argument accepts a number between 0 and 100 that lets you control how the colors are scaled to satisfy the contrast requirements. For example, set the balance to 0 to leave the first color unaffected and only correct the second color. Set the balance to 100 to correct only the second color and leave the first unaffected. Set the balance to 50 to correct both colors equally.

Note that in some cases, there's simply nothing that can be done with your settings to satisfy the contrast requirements. If, for example, you ask pale grey to be scaled lighter, it will hit white before passing the contrast requirement. In this case, the function will fix it anyway even if it breaks your balance, and you'll receive a @warn message when you compile.

Sample image caption

Get the best contrast between two colors

// Function format. $color will be modified to contrast most with both $color1 and $color2
best-contrast($color, $color1, $color2, $min-ratio1, $min-ratio2)
=> color

// Example using recommended ratio for links
best-contrast(blue, white, black, 4.5, 3)
=> #4d4dff

// Example usage
.image-caption a {
  color: best-contrast($link-color, $caption-color, $caption-background);
}

Pass the color to change as the first argument, and the two colors to contrast with as the next two arguments. The last two arguments are the required ratios between your first color and the next two colors respectively.

This only really works when there's a strong contrast between the comparing colors, eg black and white. It's best usage is to set link colors: that is, find a color that contrasts both with the background and with the surrounding text.

Sample image caption

Other less useful functions

// Returns the luminance of the color
luminance($color)
=> number

// Changes a color to have the required luminance (0–1)
scale-luminance($color, $target-luminance)
=> color

// Changes the first color's lightness value by $iterations using lighten() or darken() as specified in $operation
scale-light($color1, $color2, $min-ratio, $operation, $iterations)
=> color

Returns the luminance of a color according to the specified WCAG formula.

Changes the luminance (saturation and lightness) of a color to match $target-luminance. Takes a color and a number from 0 to 1.

Tries to fix the first color's luminance purely by tweaking its lightness (not saturation). Since this is a supportive function, it's not terribly useful unless you pass a very high number of $iterations.

Final notes

Using this tool locally requires some sort of pow() function with support for decimals. Options include Compass (deprecated), Eyeglass Math, Math Sass, SassPow, and googling.

If you find any issues, please let me know.

Human input and testing with real users is the best. Proper color contrast is only one aspect of accessibility. Please do your own research and use your own judgement.

See the Pen Sass contrast test page by Giana (@giana) on CodePen.