3

In the following scenario I need to support as many browsers as possible.

I am building a SCSS @mixin that prefixes background-image with vendor prefixes, but also listens to see if a linear-gradient is declared, and if it is, then prefix that as well.

My code looks like this:

@function str-coerce($string) {
  @return inspect($string);
}

@function str-replace($string, $find, $replace: "") {
  $index: str-index($string, $find);

  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($find)), $find, $replace);
  }

  @return $string;
}

@mixin background-image($values...) {
  $vendors: (-webkit-, -moz-, -o-);

  @each $vendor in $vendors {
    $string: str-coerce($values);

    @if (str-index($string, "linear-gradient")) {
    $string: str-replace($string, "linear-gradient", #{$vendor + "linear-gradient"});

    @if (str-index($vendor, "-o-")) {
      $vendor: str-replace($vendor, "-o-");
    }

    #{$vendor + "background-image"}: $string;
    } @else {

      @if not (str-index($vendor, "-o-")) {
        #{$vendor + "background-image"}: $values;
      }
    }
  }

  background-image: $values;
}

Usage and output looks like this:

// usage
.foo {
  @include background-image(url("../example.svg"));
}
.bar {
  @include background-image(linear-gradient(45deg, red, blue));
}

// output
.foo {
  -webkit-background-image: url("../example.svg");
  -moz-background-image: url("../example.svg");
  background-image: url("../example.svg");
}
.bar {
  -webkit-background-image: (-webkit-linear-gradient(45deg, red, blue),);
  -moz-background-image: (-moz-linear-gradient(45deg, red, blue),);
  background-image: (-o-linear-gradient(45deg, red, blue),);
  background-image: linear-gradient(45deg, red, blue);
}

My question is, what is going wrong in my type coercion that is causing my linear-gradient vendor prefixes to be wrapped in brackets and followed with a comma? e.g. (-moz-linear-gradient(45deg, red, blue),).

DanMad
  • 1,643
  • 4
  • 23
  • 58

1 Answers1

2

While I'm not entirely sure why your values are being wrapped in (), you can use the str-replace function to remove them.

To remove the leading ( update your str-replace function to:

@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);

  @if $index {
    @return str-slice($string, 1, $index - 2) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }

  @return $string;
}

Then update this line to remove the trailing ),

  #{$vendor + "background-image"}:  str-slice($string, 1, -3);
sol
  • 22,311
  • 6
  • 42
  • 59
  • I obviously had not thought of this. A handy solution. I will point out though, that because strings aren't `0` indexed in Sass, your string slice would actually have to read: `str-slice($string, 2, -3);` to remove the opening `(`. Thank you. Will mark as answers shortly. Though would love it if someone was able to further explain the issue to me. – DanMad May 16 '18 at 00:27
  • Also, something looks odd with your output. Firstly, I'm not sure you need to prefix the property AND the value (the value should be enough). Secondly, in terms of `linear-gradient`, I'm not sure it needs prefixing at all: http://shouldiprefix.com/#gradients and https://caniuse.com/#search=linear-gradient -- I could be wrong, please correct if so! – sol May 16 '18 at 00:42
  • 1
    shouldiprefix says you don't need to for modern browser support, but I am trying to support as many browsers as possible. caniuse indicates that vendor-prefixing does support a wider range of browsers (even if they are ancient): https://caniuse.com/#search=linear-gradient – DanMad May 16 '18 at 00:45
  • That makes sense so. – sol May 16 '18 at 00:51