petermac-
8/22/2013 - 9:03 AM

Pure SASS script for calculating contrast ratios of colors. MOVED TO: https://github.com/voxpelli/sass-color-helpers

Pure SASS script for calculating contrast ratios of colors. MOVED TO: https://github.com/voxpelli/sass-color-helpers

@function gcd($a, $b) {
  // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript
  @if ($b != 0) {
    @return gcd($b, $a % $b);
  } @else {
    @return abs($a);
  }
}

@function pow($base, $exponent, $prec: 12) {
  // Handles decimal exponents by trying to convert them into a fraction and then use a nthRoot-algorithm for parts of the calculation
  @if (floor($exponent) != $exponent) {
    $prec2 : pow(10, $prec);
    $exponent: round($exponent * $prec2);
    $denominator: gcd($exponent, $prec2);
    @return nthRoot(pow($base, $exponent / $denominator), $prec2 / $denominator, $prec);
  }

  $value: $base;
  @if $exponent > 1 {
    @for $i from 2 through $exponent {
      $value: $value * $base;
    }
  } @else if $exponent < 1 {
    @for $i from 0 through -$exponent {
      $value: $value / $base;
    }
  }

  @return $value;
}

@function nthRoot($num, $n: 2, $prec: 12) {
  // From: http://rosettacode.org/wiki/Nth_root#JavaScript
  $x: 1;

  @for $i from 0 through $prec {
    $x: 1 / $n * (($n - 1) * $x + ($num / pow($x, $n - 1)));
  }

  @return $x;
}
@function color_luminance($color) {
  // Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
  // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
  $rgba: red($color), green($color), blue($color);
  $rgba2: ();

  @for $i from 1 through 3 {
    $rgb: nth($rgba, $i);
    $rgb: $rgb / 255;

    $rgb: if($rgb < .03928, $rgb / 12.92, pow(($rgb + .055) / 1.055, 2.4));

    $rgba2: append($rgba2, $rgb);
  }

  @return .2126 * nth($rgba2, 1) + .7152 * nth($rgba2, 2) + 0.0722 * nth($rgba2, 3);
}

@function color_contrast($color1, $color2) {
  // Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
  // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
  $luminance1: color_luminance($color1) + .05;
  $luminance2: color_luminance($color2) + .05;
  $ratio: $luminance1 / $luminance2;

  @if $luminance2 > $luminance1 {
    $ratio: 1 / $ratio;
  }

  $ratio: round($ratio * 10) / 10;

  @return $ratio;
}

@function pick_best_color($base, $colors, $tolerance: 0) {
  $contrast: color_contrast($base, nth($colors, 1));
  $best: nth($colors, 1);

  @for $i from 2 through length($colors) {
    $current_contrast: color_contrast($base, nth($colors, $i));
    @if ($current_contrast - $contrast > $tolerance) {
      $contrast: color_contrast($base, nth($colors, $i));
      $best: nth($colors, $i);
    }
  }

  @if ($contrast < 3) {
    @warn "Contrast ratio of #{$best} on #{$base} is pretty bad, just #{$contrast}";
  }

  @return $best;
}

Pure SASS-adaption of Lea Verou's contrast-ratio javascript. Can be useful when eg. generating colored buttons from a single supplied color as you can then check which out of a couple of text colors would give the best contrast.

This script currently lacks the support for alpha-transparency that Lea supports in her script though.

In addition to the color-contrast adaption there's also some math methods that were needed to be able to calculate the exponent of a number and especially so when the exponent is a decimal number. A 2.4 exponent is used to calculate the luminance of a color and calculating such a thing is not something that SASS supports out of the box and not something I found a good pure-SASS script for calculating and I much prefer pure-SASS over ruby extensions. The math methods might perhaps be unecessary though if you're running Compass or similar as they may provide compatible math methods themselves.

Normal usage: color: pick_best_color(#f00, (#fff, #ccc, #666));

Bonus feature: Just want to get warned when the contrast becomes unacceptably low? Supply just that one color in the color-pick function: color: pick_best_color(#f00, #fff);

Math methods: pow(10, 3.14) and nthRoot(32, 5)