Search code examples
variableslessdotless

Resolving dynamic variables in LESS


I am trying to generate a number of classes in a loop based on a number of pre-defined variable snippets.

I have a variables.less document that I am importing at the top of this less file containing my color variables. I then want to generate matching classes for these, but I am unable to get less to compile the variable.

My code:

.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.loop-class(@list, @index: 1) when (isstring(extract(@list, @index))) {
    @status: extract(@list, @index);

    .button-@{status} {
        color: ~'@button-@{status}';
    }
    .loop-class(@list, (@index + 1));
}

Which compiles to:

.button-primary {
  color: @button-primary;
}
.button-success {
  color: @button-success;
}
etc etc

As you can see, I get the variable name to concatenate correctly, but I can not get it to resolve, so I'm guessing that LESS has already done it's variable compilation before getting to this function?

I have already tried moving the variables into this document, as well as wrapping the variables in a mixin and adding that inside the .loop-class, but neither of these seemed to help.

I also tried something like:

@status: extract(@list, @index);
@compileClass: ~'@button-@{status}';

.button-@{status} {
    color: @compileClass;
}

where I am saving the variable in a another one and then referencing that, but it yields the same result.

I looked at less css calling dynamic variables from a loop and tried implementing that as follows:

.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.define(@var) {
    @fallback: ~'@button-@{var}';
}

.loop-class(@list, @index: 1) when (isstring(extract(@list, @index))) {
    @status: extract(@list, @index);

    .button-@{status} {
        .define(@status);
        color: @@fallback;
    }
    .loop-class(@list, (@index + 1));
}

But that gave me the error that @@button-danger (last in the index) is undefined, so it still can't resolve the variable.

Is it obvious to you guys what I'm doing wrong?

Thanks for your help!


Solution

  • Missing Brackets

    You are missing a set of needed brackets to resolve the variable:

    LESS

    //imported from another file
    @button-primary: cyan;
    @button-success: green;
    @button-info: orange;
    @button-warning: yellow;
    @button-danger: red;
    
    //in your mixin file
    .loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
    .loop-class(@list, @index: 1) when (isstring(extract(@list, @index))) {
        @status: extract(@list, @index);
    
        .button-@{status} {
        color: ~'@{button-@{status}}'; /* two more brackets needed */
                  |                |
                here             here
        }
        .loop-class(@list, (@index + 1));
    }
    

    CSS Output

    .button-primary {
      color: #00ffff;
    }
    .button-success {
      color: #008000;
    }
    .button-info {
      color: #ffa500;
    }
    .button-warning {
      color: #ffff00;
    }
    .button-danger {
      color: #ff0000;
    }
    

    Cleaner More Friendly Code

    Also, as a matter of less cluttered and more user friendly code, you can remove your multiple string interpolations needed for the mixing call by changing isstring to iskeyword in your mixin:

    .loop-class(primary, success, info, warning, danger;); /* cleaner code on call */
    .loop-class(@list, @index: 1) when (iskeyword(extract(@list, @index))) {
        @status: extract(@list, @index);
    
        .button-@{status} {
        color: ~'@{button-@{status}}';
        }
        .loop-class(@list, (@index + 1));
    }