Last active
August 14, 2019 14:20
-
-
Save haschek/1059983 to your computer and use it in GitHub Desktop.
SCSS/SASS Accessibility Color Methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.