Euphoria Euphoria - 1 month ago 15
Sass (Sass) Question

SCSS Contrast (from Compass) output different to LESS's contrast

Trying to use the .contrast-color function in SCSS from the compass library is generating a different color to that of the .contrast which belongs in LESS even though the specification in LESS states otherwise.


This function works the same way as the contrast function in Compass for SASS.
http://lesscss.org/functions/


Here is an example of me using the SCSS version of contrast-color and the outputted css.

SCSS gist

and here is the LESS version.

LESS version

Is there a like for like LESS contrast function in SCSS? If not, how would one go about converting it. Looking at the source there seems to be a fair few dependenices within LESS for the contrast to work.

Answer

Reason:

No, the two functions are not the same. In Less, the contrast function compares the colors based on the gamma corrected luma value. Below is the extract from the Less documentation:

In accordance with WCAG 2.0, colors are compared using their gamma-corrected luma value, not their lightness.

Whereas in Sass, they are comparing the colors based on brightness of the colors. This function is giving the luminance value as the output and not the luma value. So they are giving different outputs.


Solution:

While Less has a luma function to directly get the gamma corrected luma value also, Sass doesn't seem to have any built-in functions to provide this value. So, we have to write custom functions based on the WCAG 2.0 specifications. The calculation logic is provided there and below is the extract:

For the sRGB colorspace, the relative luminance of a color is defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4

if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4

if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4 and RsRGB, GsRGB, and BsRGB are defined as:

RsRGB = R8bit/255

GsRGB = G8bit/255

BsRGB = B8bit/255

I'm not a Sass expert to write this custom function by myself, so I took some help from this article by Toni Pinel (well, almost everything bar his re-gamma function). If you use the below contrast-color function, it would provide the same output as Less.

@function de-gamma($n) { @if $n <= 0.03928 { @return $n / 12.92; } @else { @return pow((($n + 0.055)/1.055),2.4); } }

// sRGB BT-709 BRIGHTNESS
@function brightness($c) {
    $rlin: de-gamma(red($c)/255);
    $glin: de-gamma(green($c)/255);
    $blin: de-gamma(blue($c)/255);
    @return (0.2126 * $rlin + 0.7152 * $glin + 0.0722 * $blin) * 100;
}

// Compares contrast of a given color to the light/dark arguments and returns whichever is most "contrasty"
@function contrast-color($color, $dark: #000000, $light: #FFFFFF) {
    @if $color == null {
        @return null;
    }

    @else {
        $color-brightness: brightness($color);
        $light-text-brightness: brightness($light);
        $dark-text-brightness: brightness($dark);

        @return if(abs($color-brightness - $light-text-brightness) > abs($color-brightness - $dark-text-brightness), $light, $dark);
    }
}

Below is the code that Less is using for the luma function and it is very similar to the custom function given above. This function returns the same output as the Sass custom function given above.

Color.prototype.luma = function () {
    var r = this.rgb[0] / 255,
        g = this.rgb[1] / 255,
        b = this.rgb[2] / 255;

    r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
    g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
    b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);

    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};

Note: If we use luma(darken(@bg,10%)), Less compiler is giving 23.64695145 as the output. This is slightly different from the Sass custom function's output, which is 23.83975738. But luma(#868686) gives the same output as the Sass custom function and so I don't think the custom function is wrong.

Comments