Defining Variable Variables Using Less CSS

Defining Variable Variables using LESS CSS

Use interpolation and escaping, parentheses in the selector and parametric mixins to get the desired effect:

  • Dynamic variables by interpolation: In a string, "@{variable}" is replaced with the value of the variable. They can also be nested: Given @{@{var}-foo} and @var: bar;, the result is "barfoo".

    The resulting value is quoted. To remove these quotes, prefix ~.
  • Dynamic selectors by Selector interpolation: body.@{var} turns into body.bar.

Example:

@red-md:   #232;
@red-dk: #343;

.setColor(@color) {
body.@{color} { background-color: ~"@{@{color}-dk}";
#container { background-color: ~"@{@{color}-md}";
p { color: ~"@{@{color}-md}"; }
}
}
}
.setColor(~"red"); // Escape to prevent "red" turning "#FF0000"
//.setColor(~"blue"); etc..

Turns into:

body.red {
background-color: #334433;
}
body.red #container {
background-color: #223322;
}
body.red #container p {
color: #223322;
}

Note: When the answer was originally written, selector interpolation did not exist. See the previous revision for the solution if you're working with an old LESS compiler (before LESS 1.3.1a). Support for the old method will be dropped in LESS 1.4.0.

Using CSS variables in LESS

LESS allows you to use normal CSS code, so use one option could be just use the variable as CSS:

@import "../variables.css";
.header {
color: var(--header-color);
}

Also, you can save the css var to a LESS var:

@import "../variables.css";
@header-color: var(--header-color);

.header {
color: @header-color;
}

How to use variable variables in LESS with colors stored in named variables in loops and mixins?

It's easy just use @@

I've been struggling with this myself for some time now. The solution is simple. Just use @@ instead of @ for the color. The color will then get parsed properly, and become an color object. For this to work I store the variable name 'color_cat' in a variable called @color first. The I use the variable variables technique @@ to resolve the variable.

In your case this code works:

@color_dog: red;
@color_cat: yellow;

.animal-border(@animal){
@color: "color_@{animal}";

.@{animal}.border{
border-color: @@color;
}
}
.animal-border(dog);
.animal-border(cat);

Results:

.dog.border {
border-color: #ff0000;
}
.cat.border {
border-color: #ffff00;
}

Some errors associated with this problem. This one occurs when using the darken or lighten methods:

error evaluating function darken: Object # has no method 'toHSL'

Or this occurs when trying to supply the string value "#FF0000" to the color method:

error evaluating function color: argument must be a color keyword or 3/6 digit hex e.g. #FFF


Some related posts on SO:

  • Define variable name with variable in LESS operation
  • less undefined method error
  • Lighten color from parent in Less
  • Defining Variable Variables using LESS CSS

LESS CSS define variables when another variable is true

You can use Guarded Mixins like this :

@neutral: false;
@warm: true;

.color() when (@neutral) {
@green: #91C95B;
}
.color() when (@warm) {
@green: #91AD3C;
}
.color();


h1 {
color:@green;
}

Dynamically define a variable in LESS CSS

This Cannot Be Done

What you desire to do is not currently possible in LESS. I can think of two possible "workarounds" if you know ahead of time what variable names you want to allow to be used (in other words, not fully dynamic). Then something like one of the following could be done:

Idea #1 (Variable Variables)

.define(@var) {
@fooBar: 0;
@fooTwo: 2;
@fooYep: 4;

@fooSet: 'foo@{var}';
}

.define(Two);
.test {
.define(Bar);
prop: @@fooSet;
}
.test2 {
prop: @@fooSet;
}

Idea #2 (Parametric Mixins)

LESS

.define(@var) {
.foo() when (@var = Bar) {
@fooBar: 0;
}
.foo() when (@var = Two) {
@fooTwo: 2;
}
.foo() when (@var = Yep) {
@fooYep: 4;
}
.foo();
}

.define(Two);
.test {
.define(Bar);
prop: @fooBar;
}
.test2 {
prop: @fooTwo;
}

CSS Output (for both ideas)

.test {
prop: 0;
}
.test2 {
prop: 2;
}

Conclusion

But I'm not sure how useful either would really be, nor do I know if it could have any real application in your actual use case (since you mention the above is not the real use case). If you want a fully dynamic variable in LESS, then it cannot be done through LESS itself.

Is it possible to manipulate css variables using LESS?

As mentioned in my comment, my understanding of CSS variables is that the variable is resolved into its actual value by the UA. This happens after the Less compiler compiles the file and thus it wouldn't be aware of what is the actual value contained by the CSS variable.

To the compiler, the value of @length is only var(--length). Since this is not a number, an error is thrown during compilation indicating that the math operation is being done on an invalid type.

OperationError: Operation on an invalid type on line 4, column 3:

One way to fix this would be to make the Less compiler output the variable name as it is and have the multiplier appended to it (like string concatenation). This would then leave the control to the UA.

But since all CSS math operations have to be given within calc() function, the entire thing has to be wrapped within it. So, the below code would work fine.

h1 {
--length: 40px;
@length: var(--length);
@length2: ~"calc(@{length} * 2)";
line-height: @length;
padding: @length2;
border: 5px solid tomato;
}

Or, even the below would be enough if --strict-math is enabled during compilation:

h1 {
--length: 40px;
@length: var(--length);
@length2: calc(@length * 2);
line-height: @length;
padding: @length2;
border: 5px solid tomato;
}

Above code when compiled produces an output similar to the one in Example 11 of the specs and so it should be a reasonably good way of doing this :)

... Note, though, that calc() can be used to validly achieve the same thing, like so:

.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px);
}

var() functions are substituted at computed-value time...

Define variables in one LESS file

You should be compiling your .less files into a single .css file and including it once on every page (i.e. styles.less compiled to styles.css). That way the browser doesn't have the overhead of recompiling the CSS every page load. Also the .css file can be cached.

Instead of adding:

<link href="/css/colours.less" />
<link href="/css/styles.less" />
<link href="/css/forms.less" />
<link href="/css/widgets.less" />
...etc...

It should be:

<link href="/css/styles.css" />

And in styles.less you should have:

@import 'colours';
@import 'forms';
@import 'widgets';
...etc...

Otherwise, if you want to reuse colours.less in multiple .less stylesheets, you'll need to @import them in each stylesheet.


For development purposes, I recommend using a single, primary .less file that contains only variable declarations and @import statements. That way it's easy to find where additional scripts are added. LESS makes it very easy to add or remove stylesheets to keep the code organized.

For example, style.less might look something like:

// import statements
@import 'core';
@import 'mixins';
@import 'lists';
@import 'buttons';

@import 'forms/form';
@import 'forms/search';
@import 'forms/contact-us';

@import 'modules/module';
@import 'modules/archive';
@import 'modules/events';

// variables
@image-path: '/assets/images/';

@font: Helvetica, Arial, sans-serif;

@black: #000;
@dark-grey: #333;
@grey: #888;
@light-grey: #CCC;
@white: #FFF;
@red: #F00;

This structure makes it easy to add a new stylesheet, such as when adding a new module:

...
@import 'modules/events';
@import 'modules/foo'; //add another module
...

It also makes it very easy to remove styles if they're no longer being used. If the foo module was to be removed, it's easy to remove all the foo styles simply by removing the @import 'modules/foo'; statement.

How to declare a variable as !important with LESS?

I think your declaration of @fontColor: #000000 !important; indeed make no sense, what do you assign? a string, a list of something?
Most reasonable it a string, which should quoted and indeed escaped when used:

For that reason the following code seems correct (for me)

@fontColor: "#000000 !important";

p {
color: ~"@{fontColor}";
}

I also found that the following code:

@wrong: red noncolor;
selector {
property: @wrong;
}

@wrongtoo: red !important;
selector {
propertytoo: @wrongtoo;
}

output:

selector {
property: red noncolor;
}
selector {
propertytoo: red;
}

The compiler (Less v2) does not throw a error in both cases. But in the second case !important is not compiled in the CSS code. Also red !noncolor compiles into the CSS code. Probably !important is some kind of keyword, but for me it seems an inconsequence in the compiler for now.

Also notice that the docs describe how to use !important with mixins at http://lesscss.org/features/#mixins-feature-the-important-keyword

Calculations on Less variable variables

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);
}


Related Topics



Leave a reply



Submit