Giovanni Venturelli Giovanni Venturelli - 3 months ago 18
Sass (Sass) Question

Less to Sass: How to translate less guard when (default()) to sass

I am trying to convert some mixins from less to sass.
I am not very skilled in either of these languages, so I'm not sure at all if I'm doing it right.
The original Less mixins:

.for(@i, @n) {.-each(@i)}
.for(@n) when (isnumber(@n)) {.for(1, @n)}
.for(@i, @n) when not (@i = @n) {
.for((@i + (@n - @i) / abs(@n - @i)), @n);
}


.for(@array) when (default()) {.for-impl_(length(@array))}
.for-impl_(@i) when (@i > 1) {.for-impl_((@i - 1))}
.for-impl_(@i) when (@i > 0) {.-each(extract(@array, @i))}


I have identified three main issues:
1) how do I translate guards in sass? Is it correct to gather mixins with the same number of arguments in one single mixin and write conditional blocks inside it?

2) how does when(default()) works? I've been trying to find a good explanation in the documentation but couldn't find any.

3) is there a function in sass which is equivalent to less extract?

Thank you very much!

Answer

First thing first, the mixin that you've given in question is one that was created by seven-phases-max to imitate the for and for-each loops because Less doesn't have built-in functions for them. Unlike Less, Sass already has built-in @for and @each directives to perform looping and so I'd recommend you to not spend time on converting these Less mixins to Sass.

Below are Sass samples for a for and a for-each loop:

For:

.column {
  @for $i from 1 through 5 { /* execute the loop 5 times */
    &-#{$i} { /* create .column-1 to .column-5 using selector interpolation */
      width: 20%;
      left: (($i - 1) / 5 * 100%);
    }
  }
}

For Each:

$country-code-list: US, IN, FR, SG; /* list for iteration */
.flag {
  @each $country-code in $country-code-list { /* iterate for each value in the list */
    &-#{$country-code} { /* create .flag-US, .flag-IN etc using selector interpolation */
      background-image: url(http://yoursite.com/#{$country-code}.png);
    }
  }
}

You can try out the above samples @ SassMeister.com and see the compiled output.


Now coming to your questions,

1) how do I translate guards in sass? Is it correct to gather mixins with the same number of arguments in one single mixin and write conditional blocks inside it?

The Less guards would translate to @if (conditional statements) in Sass. Below is an example:

@mixin list-style($val) {
  @if ($val == i) { list-style-type: lower-roman; }
  @else if ($val == I) { list-style-type: upper-roman; }
  @else { list-style-type: decimal };
}

#demo { @include list-style(2); }

The second part of your question is probably opinion based as each one would have their own way of doing things. I'd personally prefer grouping them inside one mixin and writing conditional blocks.


2) how does when(default()) works?

The default() guard in Less is like the typical else or default: (in switch-case) statements. The mixin with this as guard gets called only when no other mixin with the same name and the same no. of arguments is matched.

For example, have a look at the below code. Here when the value passed as argument to the mixin is anything other than i or I neither of the first two mixin guards are matched and so those mixins do not get executed. In such a case, the third one (the default()) will get executed and set the list style type as decimal.

.mixin-list-style(@val) when (@val = i) { list-style-type: lower-roman; }
.mixin-list-style(@val) when (@val = I) { list-style-type: upper-roman; }
.mixin-list-style(@val) when (default()) { list-style-type: decimal; }
#demo {
  .mixin-list-style(1); /* this will result in list-style-type: decimal */
}

3) is there a function in sass which is equivalent to less extract?

Yes, there is a nth() function in Sass which is equivalent to the Less extract() function. Below is a sample:

$country-code-list: US, IN, FR, SG;
.flag-FR {
  $country-code: nth($country-code-list, 3); /* extract 3rd item from country-code-list */
  background-image: url(http://yoursite.com/#{$country-code}.png);
}

Note: You don't need this with @each loop because Sass automatically extracts the item and assigns to the variable for each iteration.