5

Is it possible to break out/return early of a Sass mixin? I'd like to do something like this:

@mixin foo($bar: false) {

  @if $bar {
    // return early without applying any of the styles below
  }

  color: red;
}

Edit: Please keep in mind that this example is the simplest thing I could come up with that illustrates my problem. In the real world, my code is much more complex and the use case for this is clear.

LandonSchropp
  • 10,084
  • 22
  • 86
  • 149
  • 1
    Why do you want a break out for your mixin?you can use `@if` and `@else` instead, check out this [gist](https://gist.github.com/Aloge/7673360) – Alex Guerrero Nov 27 '13 at 10:10
  • 1
    Breaking out of the mixin would make the code more concise and easier to read. See http://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-nesting – LandonSchropp Dec 01 '13 at 02:49
  • Mixin don't use `@return` to return a value. Mixin display a block of code. Best solution is use `@if` and `@else`: https://gist.github.com/Aloge/7673360 But if you complete your example and describe your scenario I can help you @LandonSchropp – Parhum Dec 01 '13 at 07:37
  • @Parhum That's a bummer. My problem isn't that I can't do it. My problem is that I'm trying to do it in a way that's easy to read and maintain. – LandonSchropp Dec 01 '13 at 20:38
  • If you want use Mixin you have to use @if and do anything in there. Look this mixin: http://web-design-weekly.com/blog/2013/05/12/handy-sass-mixins/ Just using if... But can you use @function? – Parhum Dec 02 '13 at 06:27

3 Answers3

8

Sass doesn't really have the concept of "break", but an @if/@else will get you pretty close:

@mixin foo($bar: false) {

  @if $bar {
    color: green;
  }
  @else {
    color: red;
  }

}

From the Lead Sass developer at https://github.com/nex3/sass/issues/378:

The issue is that the more seldom-used control structures exist in Sass, the harder it is for something with only passing familiarity with the language to read stylesheets that use those control structures. That's why it started out with the bare minimum set of structures needed to do anything: because in many cases it makes sense to skew towards a smaller surface area of the language rather than optimal semantics for writing complex code.

KatieK
  • 13,586
  • 17
  • 76
  • 90
  • Thanks for the reply, especially the comment from the Sass maintainer. IMO, languages should aim for clarity rather than familiarity, but this is clearly the correct answer. – LandonSchropp Dec 07 '13 at 04:16
1

I still thinking that @if/@else statements is the easiest and best solution to deal with your problem in Sass but I've created two different breakout mixins to help you and as a challenge:

Play with this mixin first

Breakout mixin without @includes (link)

@include breakout($styles)

$style should be a list of styles separated by spaces, here are the allowed values:

  • Styles
    Common CSS styles separated by spaces and without colon or semicolons, lists of values should be wrapped by brackets:

      @include breakout(
        color blue // <property> <value>
        width (100 * 20px) // <property> <operation with values>
        border (1px solid #fff) // <property> <list of values>
        box-shadow (0 0 10px 4px #0000FF , 0 0 20px 30px #008000) // <property> <nested list of values>
      )
    
  • Breaks
    Breaks are styles that are compiled if its condition is true, also when the condition is true the mixin ends without returns all styles after the break value

      $foo: true;
      @include breakout(
        break (false color red)  // break (<condition> <property> <value>
        break ((3 < 2) border (1px solid #fff)) // breaks also support list and nested lists
        break ($foo width 10px) // This breaks is compiled because condition is true
        color blue // This style isn't compiled because the $foo break ends the mixin
      )
    

Note that the order of the mixin argument list is important because it determines the compiled and not compiled styles if a break condition is true

Breakout mixin with @includes (link)

This mixin is similar to the above but it introduces mixin values for $styles, break-mixin mixin and @content into the breakout mixin to allow use of @includes.

  • Mixins
    If you want to use other mixins into breakout mixin you need to add some code into $styles and add each mixin into a break-mixin mixin.

      @include breakout(
        mixin foo // mixin <name of the mixin declared into break-mixin arguments>
        mixin bar // mixin name should match break-mixin argument
        mixin foobar
      ) {
        @include break-mixin(foo) { // Here your mixin or mixins for mixin foo }
        @include break-mixin(bar) { @include mixin1; @include mixin2; @include mixin3}
        @include break-mixin(foobar) { @include foobar}
      }
    
  • Mixin breaks
    Now you can also use mixin into breaks. Here the order is still important:

      $foo: true
      @include breakout(
        mixin foobar
        mixin bar
        break ($foo mixin foo) // This breaks is compiled because condition is true
        color blue // This style isn't compiled because the $foo break ends the mixin
      ) {
        @include break-mixin(foo) { // Here your mixin or mixins for mixin foo }
        @include break-mixin(bar) { @include mixin1; @include mixin2; @include mixin3}
        @include break-mixin(foobar) { @include foobar}
      }
    

So for your specific case copy the Breakout mixin without @includes (link) to your scss file or use it as a partial and then add this to your code;

@include breakout(
  break ($bar property value) // The break out statement
  color red // If $bar != false this will be compiled if not it won't
);
Alex Guerrero
  • 2,109
  • 1
  • 19
  • 28
0

I'm surprised that the @error statement has not been mentioned yet. According to the documentation (emphasis mine):

When writing mixins and functions that take arguments, you usually want to ensure that those arguments have the types and formats your API expects. If they aren't, the user needs to be notified and your mixin/function needs to stop running.

That said, @error may not be suitable for every situation, because it will stop the Sass compilation completely. This makes it unfit for mixins where breaking out is an expected end intended scenario.

Example from the Sass documentation:

@mixin reflexive-position($property, $value) {
  @if $property != left and $property != right {
    @error "Property #{$property} must be either left or right.";
  }

  $left-value: if($property == right, initial, $value);
  $right-value: if($property == right, $value, initial);

  left: $left-value;
  right: $right-value;
  [dir=rtl] & {
    left: $right-value;
    right: $left-value;
  }
}

.sidebar {
  @include reflexive-position(top, 12px);
  //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Error: Property top must be either left or right.
}
marcvangend
  • 5,592
  • 4
  • 22
  • 32