Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
SCSS/SASS Accessibility Color Methods
/*
SCSS Color Methods for Accessibility
================================================================================
Adjust given colors to ensure that those color combination provide sufficient
contrast.
@version 0.1
@link http://eye48.com/go/scsscontrast
@license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License (LGPL)
@author Michael Haschke, http://michael.haschke.biz/
Usage
--------------------------------------------------------------------------------
Import contrast.scss first, then use method ``color_adjust_contrast_AERT``.
```(scss)
@import 'libs/contrast';
.my-element {
background-color: #eee;
color: color_adjust_contrast_AERT(#ccc, #eee); // get grey with good contrast on #eee
}
```
How it works
--------------------------------------------------------------------------------
It tests for sufficent difference on brightness and color of two values. If it
is not enough contrats regarding test formulas of accessible guidelines then one
color is adjusted on lightness and saturation.
Methods using calculations from:
* "Techniques For Accessibility Evaluation And Repair Tools (AERT)" working draft,
@see http://www.w3.org/TR/AERT#color-contrast
* TODO: add methods to test with WCAG2 formulas
*/
// -- AERT ----------------------------------------------------------------
@function color_adjust_contrast_AERT(
$color_adjust,
$color_keep,
$lightness_change_value:1,
$saturation_change_value:1,
$brightness_min: 75,
$difference_min: 500)
{
@if (color_test_contrast_AERT($color_adjust, $color_keep, $brightness_min, $difference_min) == true)
{
@return $color_adjust;
}
$color_save: $color_adjust;
$S: saturation($color_adjust);
$L: lightness($color_adjust);
$foundresult: false;
@while(($foundresult == false) and ($S > 0 or ($L > 0 and $L < 100)))
{
$color_adjust: desaturate($color_adjust, $saturation_change_value);
$S: $S - $saturation_change_value;
@if (lightness($color_keep) > lightness($color_adjust))
{
$color_adjust: darken($color_adjust, $lightness_change_value);
$L: $L - $lightness_change_value;
}
@else
{
$color_adjust: lighten($color_adjust, $lightness_change_value);
$L: $L + $lightness_change_value;
}
$foundresult: color_test_contrast_AERT($color_adjust, $color_keep, $brightness_min, $difference_min);
}
@if ($foundresult == false)
{
@debug $color_save + " was adjusted to " + $color_adjust + " but this is not enough contrast to " + $color_keep + " (AERT).";
}
@return $color_adjust;
}
@function color_test_contrast_AERT($color_1, $color_2, $brightness_min: 75, $difference_min: 500)
{
$difference_brightness: math_absolute(color_brightness_AERT($color_1) - color_brightness_AERT($color_2));
$difference_color: color_difference_AERT($color_1, $color_2);
@if (($difference_brightness < $brightness_min) or ($difference_color < $difference_min))
{
@return false;
}
@else
{
@return true;
}
}
@function color_brightness_AERT($color)
{
$r: red($color);
$g: green($color);
$b: blue($color);
@return (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
}
@function color_difference_AERT($color1, $color2)
{
$r1: red($color1);
$g1: green($color1);
$b1: blue($color1);
$r2: red($color2);
$g2: green($color2);
$b2: blue($color2);
@return (math_max($r1, $r2) - math_min($r1, $r2)) + (math_max($g1, $g2) - math_min($g1, $g2)) + (math_max($b1, $b2) - math_min($b1, $b2));
}
// -- math stuff ----------------------------------------------------------
@function math_max($value1, $value2)
{
@if ($value1 > $value2) { @return $value1; }
@return $value2;
}
@function math_min($value1, $value2)
{
@if ($value1 < $value2) { @return $value1; }
@return $value2;
}
@function math_absolute($value)
{
@if ($value > 0) { @return $value; }
@return $value * -1;
}
Owner

haschek commented Jul 4, 2011

Hi Aaron, thx for the link. The compass methods are not really similar, because they only give you back a predefined light/dark color, based on the threshold. So it depends only on your inputs if the color combination has sufficient contrast to be accessible, even for visually impaired people.

Just take the the compass example [1], #333333 text on #C82523 background. This combination got a brightness difference of 35, and a color difference of 179 [2]. To be accessible, the brightness difference should be near 125 or above, the color difference around 500 and above (some guidelines say 400). Just use the example combination and transform it to grayscale, you will see it ...

So, what will the methods above do:

color_adjust_contrast_AERT(#333333, #C82523) => black
=> DEBUG: #333333 was adjusted to black but this is not enough contrast to #c82523 (AERT).
color_adjust_contrast_AERT(#C82523, #333333) => #e8d5d4

This helps to get color combinations what are really accessible/readable, for most people. With the test methods, you even can chain that methods, this example is the short one without any conditionals:

color_adjust_contrast_AERT(#C82523, color_adjust_contrast_AERT(#333333, #C82523)) => #d6908f

So, an inaccessible input of #333/#C82523 will lead to two accessible results: #333/#e8d5d4 and black/#d6908f Try it yourself [2], and yes, the colors really look different now, but in this case it's a feature :)

cheers,
Haschek

[1] http://compass-style.org/examples/compass/utilities/contrast/
[2] http://www.snook.ca/technical/colour_contrast/colour.html

Hi @haschek thanks for clarifying. I commented a bit hastily before appreciating the true nature of your mixin :)

tony commented Aug 28, 2017 edited

I don't advise using this code unless it's licensed MIT/Apache/BSD/ISC/etc. LGPL/GPL isn't clear about how it handles scripting languages and copy-pasting, so there are various hidden traps (albeit likely not intended by the author) that could create a derivative if you use the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment