Search code examples
csssasscolorsrgbsrgb

Alpha channels with CSS Color Module Level 4's color() function


I am experimenting with a Sass @mixin that provides support for the display-p3 color-space and gracefully degrades back to sRGB when there is no support for display-p3. Currently, only Safari supports this spec.

@mixin color-spaces($property, $color) {
  $red: red($color);
  $green: green($color);
  $blue: blue($color);
  $alpha: alpha($color);

  @if $alpha == 1 {
    #{$property}: rgb($red, $green, $blue);
    #{$property}: color(display-p3 ($red / 255) ($green / 255) ($blue / 255));
  } @else {
    #{$property}: rgba($red, $green, $blue, $alpha);
    #{$property}:
      rgba(
        color(display-p3 ($red / 255) ($green / 255) ($blue / 255)),
        $alpha
      );
  }
}

In the above @mixin I made the assumption that I'd be able to pass a color() function as an argument to an rgba() function, should the color require an alpha-channel. Some example usages are below:

// Declarations
.foo {
  @include color-spaces(background-color, #ff0);
  @include color-spaces(color, rgb(255, 0, 100));
}

.bar {
  @include color-spaces(border-color, #ffff);
  @include color-spaces(color, rgba(0, 80, 160, 0.5));
}

// Results
.foo {
  background-color: #ff0;
  background-color: color(display-p3 1 1 0);
  color: rgb(255, 0, 100);
  color: color(display-p3 1 0 0.39216);
}
.bar {
  border-color: #ffff;
  border-color: rgba(color(display-p3 1 1 1), 1);
  color: rgba(0, 80, 160, 0.5);
  color: rgba(color(display-p3 0 0.31373 0.62745), 0.5);
}

My assumption was incorrect. Passing a color() function as an argument to an rgba() argument isn't valid. I also cannot pass a fourth number argument to the color() function. It only accepts three.

Using the opacity property as an alternative isn't a reasonable solution in this instance, either. Passing an opacity value to the selector in question will then impact the opacity of all child elements.

I appreciate that this is fairly new and experimental. However, does anyone know of a method where I can pass an alpha-channel in conjunction with a color() function to achieve transparency with the display-p3 color space???


Solution

  • I have found an answer to this question. I'll post the answer in case it can help someone in the future as this spec becomes more commonplace.

    It seems there is conflicting information out there... The color() function is capable of taking a fourth argument (for an alpha-channel). This helpful CSS Tricks article by Ollie Williams explains it very well.

    The correct sytanx is as follows:

    color(display-p3 1 0 0 / 50%);
    

    Though these are different color spaces, this is roughly equivalent to:

    rgba(255, 0, 0, 0.5);
    

    This means I can update my @mixin by removing the second rgba() function call altogether, and just rely on the color() function to pass an alpha-channel when the context requires it:

    @mixin color-spaces($property, $color) {
      $red: red($color);
      $green: green($color);
      $blue: blue($color);
      $alpha: alpha($color);
    
      @if $alpha == 1 {
        #{$property}: rgb($red, $green, $blue);
        #{$property}: color(display-p3 ($red / 255) ($green / 255) ($blue / 255));
      } @else {
        #{$property}: rgba($red, $green, $blue, $alpha);
        #{$property}: color(display-p3 ($red / 255) ($green / 255) ($blue / 255) unquote('/') ($alpha * 100%));
      }
    }