John Ohara John Ohara - 3 months ago 14
Less Question

Less Guard not working as expected

I have a mixin that has a guard clause.

I've followed the guide and believe the syntax below to be correct.

Essentially the guide should ensure that @palette is one of a range of custom colors, and @color a number from a set.

This works - it compiles and produces the correct output.

However, if I change the @palette variable to cause a fault, Less doesn't compile - is this the expected behaviour?

.AccentPalette(@palette; @color:500) when
(@palette = amber), (@palette = blue), (@palette = blueGrey), (@palette = cyan), (@palette = deepOrange),
(@palette = deepPurple), (@palette = green), (@palette = grey), (@palette = indigo), (@palette = lightBlue),
(@palette = lightGreen), (@palette = lime), (@palette = orange), (@palette = pink), (@palette = purple),
(@palette = red), (@palette = teal), (@palette = yellow) and
(@color = 50), (@color = 100), (@color = 200), (@color = 300), (@color = 400),
(@color = 500), (@color = 600), (@color = 700), (@color = 800), (@color = 900) {
.Swatch(@palette);

@accentColor:"@{@{color}}";

@accent50: @50;
@accent100: @100;
@accent200: @200;
@accent300: @300;
@accent400: @400;
@accent500: @500;
@accent600: @600;
@accent700: @700;
@accent800: @800;
@accent900: @900;
}


Called like this:

.AccentPalette(indigo);


A swatch example - there are a number of them, one for each color.

.Swatch(amber)
{
@50: #fff8e1;
@100:#ffecb3;
@200:#ffe082;
@300:#ffd54f;
@400:#ffca28;
@500:#ffc107;
@600:#ffb300;
@700:#ffa000;
@800:#ff8f00;
@900:#ff6f00;
}

Answer

Contrary to what I said in my earlier comment, the problem is actually with the .AccentPalette mixin guard. It seems like the Less compiler evaluates the and before the , (or). Because of it, the guard always gets matched when you don't provide any value for the @color variable as the guard @color = 500 is always true.

Consider the below simplified example:

@500: dummy;
.AccentPalette(@palette; @color:500) when
(@palette = amber), (@palette = blue) and
(@color = 50), (@color = 500), (@color = 900) {
  .Swatch(@palette); 
  accentColor:"@{@{color}}";
}

.Swatch(amber){}
.Swatch(blue){}

#demo {
  .AccentPalette(amber;1000);
}

The compiler seems to evaluate it as follows: (note the extra pair of braces)

(@palette = amber), ((@palette = blue) and (@color = 50)), (@color = 500), (@color = 900)

This evaluates to (false, (false and false), true, false) or (false, false, true, false), so the mixin is always matched as there is one true.


The proper fix would've been to write the mixin guard as follows:

((@palette = amber),(@palette = blue)) and ((@color = 50),(@color = 500),(@color = 900))

but the Less compiler doesn't seem to like the extra pair of braces and gives a compiler error. So, the only option seems to be to split the guards into two levels like in the below example.

@500: dummy;

.AccentPalette(@palette; @color:500) when (@palette = amber), (@palette = blue) {
  & when (@color = 50), (@color = 500), (@color = 900) {
    .Swatch(@palette); 
    accentColor:"@{@{color}}";
  }
}

.Swatch(amber){}
.Swatch(blue){}

#demo {
  .AccentPalette(red);
}