Search code examples
lessless-mixins

Calculations on Less variable variables


My grid has different gutters for different breakpoints and I would have liked to generate my .row classes dynamically by passing to it predefined variables such as:

@grid-gutter-xs: 10px;
@grid-gutter-sm: 20px;
...

Then I have some mixins within which one that generates the row negative margins by dividing the gutter size in half:

.grid-row(@class) {
  .row {
    margin-right: -( @{grid-gutter-@{class}} / 2);
    margin-left: -( @{grid-gutter-@{class}} / 2);
  }
}

Which is later called like

@media (min-width: @screen-sm-min) {
    .grid-row(sm);
}

This is a code excerpt and I can simply do this another way and escape the headache but I just curious if it's simply not working like this (and why) or I am missing something in the syntax?


Solution

  • The idea you have is correct and there is nothing wrong with that. Less mixins can handle cases like this. But the implementation has a few problems in the below line of code:

    margin-right: -( @{grid-gutter-@{class}} / 2);
    
    • First of all, you are concatenating the value of @class variable with the string grid-gutter and syntax for string concatenation with a variable is "string-concatenated-with-@{var}" (note the quotes). Then the variable whose name is same as the concatenated string should be evaluated.

    • But even if we put the entire thing within quotes like "@{grid-gutter-@{class}}", the math won't work because of the problem described in this answer.

    • Because the math cannot be performed, the output of "@{grid-gutter-@{class}}" / 2 will just be something like 20px / 2 (string concatenation). Since the output of this entire thing is just a string, the negation -(...) will also fail and produce an error (the error message for this step is confusing, but that is a different problem).

    The correct way to do this would be the following:

    • Concatenate @{class} variable's value with grid-gutter and store in a temporary variable.
    • Use the temporary variable like @@var (double referencing - @var is to get the temp variable's value and the other @ fetches the value of the variable whose name is same as the evaluated string) in the property's value calculation. It is required because in such cases Less makes the output value a number and thereby support the math operations to follow.
    • After this, you can do -(@@margin / 2) or -1 * @@margin/2 to get the negated value. There is no big benefit of either of these syntaxes and its more of a personal preference on which to use.

    Code:

    @grid-gutter-xs: 10px;
    @grid-gutter-sm: 20px;
    .grid-row(@class) {
      .row {
        @margin: "grid-gutter-@{class}";
        margin-right: -1 * @@margin / 2; /* or -(@@margin / 2) */
        margin-left:  -1 * @@margin / 2;
      }
    }
    @media (min-width: @screen-sm-min) {
        .grid-row(sm);
    }
    @media (min-width: @screen-xs-min) {
        .grid-row(xs);
    }