47
votes

I like to make mixins with SASS that help me make good cross-browser compatibility. I want to make a mixin that looks like this:

@mixin box-shadow($value) {
    box-shadow: $value;
    -webkit-box-shadow: $value; 
    -moz-box-shadow: $value; 
}

to which I can pass something like this:

@include box-shadow(inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5), inset 0px 0px 0px 1px #ff800f);

with the result being something like this:

box-shadow: inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5),inset 0px 0px 0px 1px #ff800f;
-moz-box-shadow: inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5),inset 0px 0px 0px 1px #ff800f;
-webkit-box-shadow: inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5),inset 0px 0px 0px 1px #ff800f; 

However, This doesn't work because the complier thinks I am trying to pass the mixin 3 arguments. box-shadow takes a variable number of comma separated bits, so I can't just define a mixin like box-shadow($1,$2,$3).

I tried passing

@include box-shadow("inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5), inset 0px 0px 0px 1px #ff800f");

and it compiled, but didn't actually render the styles.

Any tips on how to resolve this?

6
Like you, I think it's intuitive to wrap the parameter in a string when there are commas. Putting the #{} in the mixin itself allows you to do this. I added an answer to show this as well. - Joshua Pinter
Just a note on the use of rgba mostly for others starting to use Sass (since I know everyone else here already knows this :]). Instead of having to type rgba(0,0,0,0.5); you can type rgba(black,.5). Same goes with white, red, etc. and all other CSS colors, that way you don't have to type the actual RGB value itself. You can also use hex values: rgba(#258,.5);. Or color variables: rgba($colorVariable,.5). Hope that helps. - Ricardo Zea
4 years on I notice that the order of the box-shadow expressions is the wrong way around. How embarrassing. - Oliver

6 Answers

74
votes

Variable Arguments

Sometimes it makes sense for a mixin to take an unknown number of arguments. For example, a mixin for creating box shadows might take any number of shadows as arguments. For these situations, Sass supports “variable arguments,” which are arguments at the end of a mixin declaration that take all leftover arguments and package them up as a list. These arguments look just like normal arguments, but are followed by .... For example:

@mixin box-shadow($shadows...) {
  -moz-box-shadow: $shadows;
  -webkit-box-shadow: $shadows;
  box-shadow: $shadows;
}

.shadows {
  @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}

via: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#variable_arguments

37
votes

SASS 3.1 or less

Note: If you're using SASS 3.2+ then use the Variable Arguments feature as rzar suggests.

Just use string interpolation in the mixin itself, like so:

@mixin box-shadow($value) {
  -webkit-box-shadow: #{$value};               // #{} removes the quotation marks that
  -moz-box-shadow: #{$value};                  // cause the CSS to render incorrectly.
  box-shadow: #{$value};
}

// ... calling it with quotations works great ...
@include box-shadow("inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5), inset 0px 0px 0px 1px #ff800f");

Thanks for the tip Ryan.

14
votes

Use string interpolation

@include box-shadow(#{"inset -2px -2px 2px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.5), inset 0px 0px 0px 1px #ff800f"});
4
votes

There's many ways to do this, the best way is using a mixin like so:

@mixin box-shadow($value...) {
  -webkit-box-shadow: $value;               
  -moz-box-shadow: $value;                  
  box-shadow: $value;
}

And include it like this:

@include box-shadow(inset 0 1px 0 #FFD265, 0 0 0 1px #912E01, 0 0 0 7px rgba(0, 0, 0, .1), 0 1px 4px rgba(0, 0, 0, .6));

or

@mixin box-shadow($value) {
      -webkit-box-shadow: #{$value};               
      -moz-box-shadow: #{$value};          
      box-shadow: #{$value};
}

And include it like this:

@include box-shadow("inset 0 1px 0 #FFD265, 0 0 0 1px #912E01, 0 0 0 7px rgba(0, 0, 0, .1), 0 1px 4px rgba(0, 0, 0, .6)");

or:

@mixin box-shadow($value) {
      $value: unquote($value);
      -webkit-box-shadow: $value;               
      -moz-box-shadow: $value;          
      box-shadow: $value;
}

or:

@mixin box-shadow($value) {
  -webkit-box-shadow: $value;               
  -moz-box-shadow: $value;                  
  box-shadow: $value;
}

And include it like this:

@include box-shadow((inset 0 1px 0 #FFD265, 0 0 0 1px #912E01, 0 0 0 7px rgba(0, 0, 0, .1), 0 1px 4px rgba(0, 0, 0, .6)));

Sass is very powerful :)

2
votes

i want to point out that you can also pass a value using the argument name as you call the mixin:

@mixin shadow($shadow: 0 0 2px #000) {
    box-shadow: $shadow;
    -webkit-box-shadow: $shadow; 
    -moz-box-shadow: $shadow; 
}

.my-shadow {
  @include shadow($shadow: 0 0 5px #900, 0 2px 2px #000);
}

note that scss is scoped so $shadow will still retain its mixin value if used again later. less i believe, suffers from reassignment in this scenario

0
votes

This doesn't compile:

+box-shadow(inset 0 1px 0 #FFD265, 0 0 0 1px #912E01, 0 0 0 7px rgba(0, 0, 0, .1), 0 1px 4px rgba(0, 0, 0, .6))

this compiles:

+box-shadow((inset 0 1px 0 #FFD265, 0 0 0 1px #912E01, 0 0 0 7px rgba(0, 0, 0, .1), 0 1px 4px rgba(0, 0, 0, .6)))

That is, add a parenthesis around the comma-separated list of shadows and it should work:

+box-shadow( (myshadow1, myshadow2, ...) )