222

I am writing a mixin like this:

@mixin box-shadow($top, $left, $blur, $color, $inset:"") {
    -webkit-box-shadow: $top $left $blur $color $inset;
    -moz-box-shadow: $top $left $blur $color $inset;
    box-shadow: $top $left $blur $color $inset;
}

When called what I really want is that if no $inset value is passed, nothing is output, rather than it compiling to something like this:

-webkit-box-shadow: 2px 2px 5px #555555 "";
-moz-box-shadow: 2px 2px 5px #555555 "";
box-shadow: 2px 2px 5px #555555 "";

How do I rewrite the mixin so that if there is no value of $inset passed, nothing is output?

Volker E.
  • 5,911
  • 11
  • 47
  • 64
Rich Bradshaw
  • 71,795
  • 44
  • 182
  • 241
  • 6
    It's a shame that SASS doesn't have a `blank` or `nil` value. – Joshua Pinter Mar 31 '12 at 20:08
  • 1
    Btw, while looking at a different SASS limitation, I came across a nice way to get rid of the quotes in these situations. I added an answer for it. – Joshua Pinter Mar 31 '12 at 22:53
  • 7
    Now in 2015 you can just use `null` in order to skip attr/prop. `ie @include box-shadow($top, $left, $blur, $color, null)` – Drops May 04 '15 at 15:33

13 Answers13

284

A DRY'r Way of Doing It

And, generally, a neat trick to remove the quotes.

@mixin box-shadow($top, $left, $blur, $color, $inset:"") {
  -webkit-box-shadow: $top $left $blur $color #{$inset};
  -moz-box-shadow:    $top $left $blur $color #{$inset};
  box-shadow:         $top $left $blur $color #{$inset};
}

SASS Version 3+, you can use unquote():

@mixin box-shadow($top, $left, $blur, $color, $inset:"") {
  -webkit-box-shadow: $top $left $blur $color unquote($inset);
  -moz-box-shadow:    $top $left $blur $color unquote($inset);
  box-shadow:         $top $left $blur $color unquote($inset);
} 

Picked this up over here: pass a list to a mixin as a single argument with SASS

Community
  • 1
  • 1
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
  • 7
    As Bob Sammers points out, in newer version of SASS you can also use the unquote() method instead of #{} to remove the quotes. Cheers. – Joshua Pinter Jul 18 '13 at 23:05
  • 4
    the most simple way is to use `null` for default value of optional parameters instead of `""`, and they will not be rendered in output! `@mixin box-shadow($top, $left,... , $inset:null) {}` – S.Serpooshan Nov 17 '18 at 09:19
  • @S.Serpooshan I'm curious what version this was added in. This certainly wasn't supported back in 2012. – Joshua Pinter Mar 29 '19 at 00:20
  • @JoshuaPinter even docs for it are harder to find https://sass-lang.com/documentation/values/null but they don't mention any compatibility like e.g. for "Configuring Modules", what I find interesting is it seems to only remove property line when the variable is used directly as the property value (but not when passed along) I created a showcase - https://jsfiddle.net/eqw3oxcv/ – jave.web Jun 24 '21 at 01:14
92

A so much better DRY way

is to pass $args... to the @mixin. That way, no matter how many $args you will pass. In the @input call, you can pass all args needed. Like so:

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

And now you can reuse your box-shadow in every class you want by passing all needed args:

.my-box-shadow {
  @include box-shadow(2px 2px 5px #555, inset 0 0 0);
}
Fabricio
  • 1,168
  • 8
  • 9
  • 17
    That's good to know but often times it's more clear to indicate what arguments a `mixin` is expecting, like `$top`, `$left`, `$blur`, etc. Variable arguments, like you've shown, are great for ultimate flexibility, though. – Joshua Pinter Feb 27 '15 at 22:42
  • 1
    Thanks, dude it helps me a lot – Nilesh Sutar Jul 05 '18 at 09:42
  • This is too generic. Especially if you only want to expose some values of a CSS property. For example: animation: spin $duration $timingFunction infinite. – Youp Bernoulli Jun 18 '21 at 10:41
54

Sass supports @if statements. (See the documentation.)

You could write your mixin like this:

@mixin box-shadow($top, $left, $blur, $color, $inset:"") {
  @if $inset != "" {
    -webkit-box-shadow:$top $left $blur $color $inset;
    -moz-box-shadow:$top $left $blur $color $inset;
    box-shadow:$top $left $blur $color $inset;
  }
}
Andrew Vit
  • 18,961
  • 6
  • 77
  • 84
28

You can put the property with null as a default value and if you don't pass the parameter it will not be interpreted.

@mixin box-shadow($top, $left, $blur, $color, $inset:null) {
  -webkit-box-shadow: $top $left $blur $color $inset;
  -moz-box-shadow:    $top $left $blur $color $inset;
  box-shadow:         $top $left $blur $color $inset;
}

This means you can write the include statement like this.

@include box-shadow($top, $left, $blur, $color);

Instead of writing it like this.

@include box-shadow($top, $left, $blur, $color, null);

As shown in the answer here

Community
  • 1
  • 1
user1551211
  • 537
  • 5
  • 13
  • 1
    This answer offers nothing new over the existing answers (see: http://stackoverflow.com/a/23565388/1652962) – cimmanon Aug 25 '15 at 10:55
  • 9
    @cimmanon On the contrary, this provides extra utility as you can then write the import as `@include box-shadow($top, $left, $blur, $color);` instead of `@include box-shadow($top, $left, $blur, $color, null);`. Therefore this answer is so much more benificial. – Dan Aug 11 '16 at 19:13
  • 1
    @Dan Actually you lose readability doing it that way, so arguably it's *less* beneficial than the answer it's derived from. – TylerH Oct 12 '17 at 13:30
  • @TylerH That is true – Dan Oct 13 '17 at 06:08
14

Old question, I know, but I think this is still relevant. Arguably, a clearer way of doing this is to use the unquote() function (which SASS has had since version 3.0.0):

@mixin box-shadow($top, $left, $blur, $color, $inset:"") {
  -webkit-box-shadow: $top $left $blur $color unquote($inset);
  -moz-box-shadow: $top $left $blur $color unquote($inset);
  box-shadow: $top $left $blur $color unquote($inset);
}

This is roughly equivalent to Josh's answer, but I think the explicitly named function is less obfuscated than the string interpolation syntax.

Bob Sammers
  • 3,080
  • 27
  • 33
14

I know its not exactly the answer you were searching for but you could pass "null" as last argument when @include box-shadow mixin, like this @include box-shadow(12px, 14px, 2px, green, null); Now, if that argument is only one in that property than that property (and its (default) value) won't get compiled. If there are two or more args on that "line" only ones that you nulled won't get compiled (your case).

CSS output is exactly as you wanted it, but you have to write your nulls :)

  @include box-shadow(12px, 14px, 2px, green, null);

  // compiles to ...

  -webkit-box-shadow: 12px 14px 2px green;  
  -moz-box-shadow: 12px 14px 2px green;  
  box-shadow: 12px 14px 2px green;
Drops
  • 2,680
  • 18
  • 16
  • 1
    Definitely my favourite way to do this keeps everything simple to read and understand for when you have to come back to a script. thnx Drops. – Plentybinary Mar 16 '15 at 19:59
8

here is the solution i use, with notes below:

@mixin transition($trans...) {
  @if length($trans) < 1 {
    $trans: all .15s ease-out;
  }
  -moz-transition: $trans;
  -ms-transition: $trans;
  -webkit-transition: $trans;
  transition: $trans;
}

.use-this {
  @include transition;
}

.use-this-2 {
  @include transition(all 1s ease-out, color 2s ease-in);
}
  • prefer passing property values as native css to stay close to "the tongue"
  • allow passing variable number of arguments
  • define a default value for laziness
duggi
  • 565
  • 7
  • 13
  • How does this add anything over the existing answers (namely: http://stackoverflow.com/a/28072864/1652962)? – cimmanon Jun 11 '15 at 18:42
  • He explained how to tell if more than one argument is passed to the function and if so, change the output. The other answer did not. – TetraDev Nov 10 '15 at 22:50
  • sometimes, you want to define a global behaviour. for example, animation transitions where you generally want to have a very uniform experience and don't want to risk "creative developer discretion" when it comes to deciding what to do. this defines a default that can be overridden but often won't be because of the more compact syntax when not passing arguments. you can also change the global behaviour in a single place this way. – duggi Nov 12 '15 at 19:44
6

Even DRYer way!

@mixin box-shadow($args...) {
  @each $pre in -webkit-, -moz-, -ms-, -o- {
    #{$pre + box-shadow}: $args;
  } 
  #{box-shadow}: #{$args};
}

And now you can reuse your box-shadow even smarter:

.myShadow {
  @include box-shadow(2px 2px 5px #555, inset 0 0 0);
}
Ben Kalsky
  • 148
  • 2
  • 16
  • 2
    Just a note - as readable as this is and I do acknowledge that, this is where one should draw the line between DRY code and *readability/maintainability*. – Leo Napoleon Jun 15 '18 at 08:15
  • 1
    I agree, to increase _readability/maintainability_ I would create a "browser list" to reuse in other mixins. This could also pretty easy be assembled to other css properties, that can be executed with a set of dry mixins. [This article](https://alistapart.com/article/dry-ing-out-your-sass-mixins) from 2014 is still great example. – Ben Kalsky Jun 16 '18 at 14:01
  • 1
    a good one, impressive way to handle multi-browser support. – WasiF Oct 27 '21 at 07:24
3

With sass@3.4.9 :

// declare
@mixin button( $bgcolor:blue ){
    background:$bgcolor;
}

and use without value, button will be blue

//use
.my_button{
    @include button();
}

and with value, button will be red

//use
.my_button{
    @include button( red );
}

works with hexa too

gael
  • 769
  • 6
  • 16
2
@mixin box-shadow($left: 0, $top: 0, $blur: 6px, $color: hsla(0,0%,0%,0.25), $inset: false) {
    @if $inset {
        -webkit-box-shadow: inset $left $top $blur $color;
        -moz-box-shadow: inset $left $top $blur $color;
        box-shadow: inset $left $top $blur $color;
    } @else {
        -webkit-box-shadow: $left $top $blur $color;
        -moz-box-shadow: $left $top $blur $color;
        box-shadow: $left $top $blur $color;
    }
}
Adam C
  • 3,881
  • 2
  • 21
  • 19
  • This is arguably worse than all of the other answers, due to its verbosity. Also, its close enough to [one of the other answers](http://stackoverflow.com/a/3535205/1652962) to count as a duplicate. – cimmanon Aug 20 '14 at 12:55
1

Super simple way

Just add a default value of none to $inset - so

@mixin box-shadow($top, $left, $blur, $color, $inset: none) { ....

Now when no $inset is passed nothing will be displayed.

fidev
  • 1,222
  • 2
  • 21
  • 51
1

You can always assign null to your optional arguments. Here is a simple fix

@mixin box-shadow($top, $left, $blur, $color, $inset:null) { //assigning null to inset value makes it optional
    -webkit-box-shadow: $top $left $blur $color $inset;
    -moz-box-shadow: $top $left $blur $color $inset;
    box-shadow: $top $left $blur $color $inset;
}
Benjamin
  • 2,612
  • 2
  • 20
  • 32
-1

I am new to css compilers, hope this helps,

        @mixin positionStyle($params...){

            $temp:nth($params,1);
            @if $temp != null{
            position:$temp;
            }

             $temp:nth($params,2);
            @if $temp != null{
            top:$temp;
            }

             $temp:nth($params,3);
            @if $temp != null{
            right:$temp;
            }

             $temp:nth($params,4);
            @if $temp != null{
            bottom:$temp;
            }

            $temp:nth($params,5);
            @if $temp != null{
            left:$temp;
            }

    .someClass{
    @include positionStyle(absolute,30px,5px,null,null);
    }

//output

.someClass{
position:absolute;
 top: 30px;
 right: 5px;
}
mike tracker
  • 1,023
  • 7
  • 10