-
-
Save haschek/1059983 to your computer and use it in GitHub Desktop.
/* | |
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; | |
} |
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.
I guess there is an issue with the function. I have passed both the colors white
$i: color_adjust_contrast_AERT(white,white);
.b{
color:$i;
}
and this returned the white color?
I found that I was able to establish a 4.5:1 contrast ratio (WCAG 2.0-AA Normal Text, WCAG 2.0-AAA Large Text) by setting the $difference_min
value to 320. I was able to establish almost 7:1 (WCAG 2.0-AAA Normal Text) by setting $difference_min
value around 355. The actual value in the second case would hover between 6.7:1 and 7:1 depending on the background color. As you approach 400, the color washes out. Since my site's base-font is in the Large Text range, I settled on a score of 340.
I left the $brightness_min
value stock.
To test this, I used WebAIM's color contrast tool. For background colors, I used the Bootstrap 4.1 alert SCSS, using a bootswap gunmetal swatch.
Hi @haschek thanks for clarifying. I commented a bit hastily before appreciating the true nature of your mixin :)