Increment a Variable in Less CSS

Increment a variable in LESS css

Not Strictly Possible

See the documentation on LESS variables. Essentially, LESS variables are constants in the scope of their creation. They are lazy loaded, and cannot be "changed" in that way. The very last definition will be the one used for all in that scope. In your case an error will occur, because variables cannot reference themselves.

Consider this example:

@counter: 1;
.someSelector("nameOfClass", @counter);
@counter: 2;
.someSelector("nameOfClass1", @counter);

.someSelector(@name; @count) {
@className: ~"@{name}";
.@{className} {
test: @count;
}
}

The output will be 2 for both:

.nameOfClass {
test: 2;
}
.nameOfClass1 {
test: 2;
}

This is because LESS defines the @counter with the last definition of the variable in that scope. It does not pay attention to the order of the calls using @counter, but rather acts much like CSS and takes the "cascade" of the variable into consideration.

For further discussion of this in LESS, you might track discussion that occurs on this LESS feature request.

Solution is in Recursive Call Setter for the Local Variable

Seven-phases-max linked to what he believes to be a bug in LESS, but I don't think it is. Rather, it appears to me to be a creative use of recursive resetting of the counter to get the effect desired. This allows for you to achieve what you desire like so (using my example code):

// counter

.init() {
.inc-impl(1); // set initial value
} .init();

.inc-impl(@new) {
.redefine() {
@counter: @new;
}
}

.someSelector(@name) {
.redefine(); // this sets the value of counter for this call only
.inc-impl((@counter + 1)); // this sets the value of counter for the next call
@className: ~"@{name}";
.@{className} {
test: @counter;
}
}

.someSelector("nameOfClass");
.someSelector("nameOfClass1");

Here is the CSS output:

.nameOfClass {
test: 1;
}
.nameOfClass1 {
test: 2;
}

NOTE: I believe you are not strictly changing a global value here, but rather setting a new local value with each call to .someSelector. Whether this is based on buggy behavior or not is questionable, but if so, this solution may disappear in the future.
For further comments of the limitations of this method, see the discussion here.

Increase a variable in Less

Simply speaking, it is not possible to increment the same variable without using a loop (mixin) in Less. This is because Less does lazy loading of variables and so the multiple increments result in recursive definition error. The following snippet:

@i: 0;
.one { @i: @i + 1; border: @i; }
.two { @i: @i + 1; border: @i; }
.three { @i: @i + 1; border: @i; }

when compiled would give:

NameError: Recursive variable definition for @i on line 4, column 7:

Using a mixin like in question (.increase()) would still result in the same error as provided above.


The best way to increment would be to make use of mixin loops. For the modified sample provided in the question, the loop should be like below:

@icon_width: 32px;
@social-networks: facebook, twitter, googlep, pinterest, linkedin; /* an array with list of networks */
.social{
background: url('snippet_with_n_images');
.loop-social(1); /* call loop with initial value as 1 */
}
.loop-social(@index) when (@index < length(@social-networks)){ /* iterate till index is less than array length */
@social-network: extract(@social-networks, @index); /* extract value corresponding to index from array */
.@{social-network}{ /* use extracted social network value as selector */
background-position-x: -@icon_width*@index; /* assign calculated value, the index would incremented for each iteration */
}
.loop-social(@index + 1); /* call next iteration with incremented value */
}

The above Less code when compiled would produce the following CSS:

.social {
background: url('snippet_with_n_images');
}
.social .facebook {
background-position-x: -32px;
}
.social .twitter {
background-position-x: -64px;
}
.social .googlep {
background-position-x: -96px;
}
.social .pinterest {
background-position-x: -128px;
}

Sprites LESS CSS Variable increment issue

Strictly Speaking You Cannot

Variables in LESS are essentially constants once defined in a particular scope, and so cannot be changed (including incremented). So your @counter: @index + 1; is not incrementing the global variable at all, but rather creating a new value for a local scope @counter variable inside that particular call of .myIconX(). See the documentation on how variables work in LESS.

Emulated by Recursive Local Variable Setting

This works, based off information deemed a bug here, but which I do not believe is strictly speaking a bug. At any rate, it can be utilized to meet your needs like so (I just implemented an @row: 1 and tweaked some code to show the calculation working):

@row: 1;

.init() {
.inc-impl(1);
} .init();

.inc-impl(@new) {
.redefine() {
@counter: @new;
}
}

#my-icon-bundle {
.my-icons () {
#my-icon-bundle .myIconX("classX1", @counter);
#my-icon-bundle .myIconX("classYY1", @counter);
}

.myIconX(@name) {
.redefine();
.inc-impl((@counter + 1));
@nameText: ~".my-icon-@{name}";
@{nameText} { #my-icon-bundle .myIcon(@row); }
}

.myIcon(@row) {
@x: @row * @counter;
@y: @row * @counter;
background-position: -@x -@y;
}
}

#my-icon-bundle .myIconX("classX1");
#my-icon-bundle .myIconX("classX1");
#my-icon-bundle .myIconX("classYY1");

Output CSS is:

.my-icon-classX1 {
background-position: -1 -1;
}
.my-icon-classX1 {
background-position: -2 -2;
}
.my-icon-classYY1 {
background-position: -3 -3;
}

This demonstrates that with each call of the .myIconX() mixin, it is setting the counter by +1 for the next call.

Warning: Whether this solution is based on buggy behavior or not is questionable, but if it is a bug, this solution may disappear in the future. For further comments on the limitations of this method, see the discussion here.

How should I reset a less variable using its own value

The above code would result in the following error being thrown on compilation:

NameError: Recursive variable definition for @navbar-default-bg

Recursive variable definitions won't work in Less because of the way Less does lazy loading of the variables. This would mean that the last definition for that variable (within the same scope) will be the one that is used. In your example it would result in an error because the variable cannot reference itself (to get its originally declared value).

Quoting Less Website:

When defining a variable twice, the last definition of the variable is used, searching from the current scope upwards. This is similar to css itself where the last property inside a definition is used to determine the value.


The best way to create a rgba color value from a given rgb color is to use the built-in fade function (like shown below). But note that, the value cannot be assigned back to the same variable.

@navbar-default-bg: rgb(255, 0, 0);

#sample{
color: fade(@navbar-default-bg, 90%);
}

The above Less code when compiled would produce the following CSS output:

#sample {
color: rgba(255, 0, 0, 0.9);
}

Of-course, you could do something like mentioned in this answer to sort of achieve a reset effect but my personal opinion is that it is way too much complexity and effort for something that can probably be achieved in a different way.

Here is a sample implementation of the method mentioned in that answer adapted to suit this question. (Code is added below just in-case the link becomes inactive.)

.init() {
.inc-impl(rgb(255, 0, 0), 0.1); // set initial value
}
.init();
.inc-impl(@new, @i) {
.redefine() {
@color: @new;
@alpha: @i;
}
}
.someSelector(@name) {
.redefine(); // this sets the value of counter for this call only
.inc-impl(rgba(red(@color), green(@color), blue(@color), @alpha), (@alpha + 0.1)); // this sets the value of counter for the next call
@className: ~"@{name}";
.@{className}
{
color: @color;
}
}
.someSelector("nameOfClass");
.someSelector("nameOfClass1");
.someSelector("nameOfClass2");

Is there any way to increment a CSS value depending on nth-child variable?

The simple answer, given the limitations you've provided (no SASS or other creepy stuff), is no, it's not possible with CSS.

Nesting and looping with less incremental

See Loops, e.g.:

.make-nested-lists(5);

.make-nested-lists(@n, @i: 0) when (@i < @n) {
ul li {
padding-left: (30px + 15 * @i);
.make-nested-lists(@n, (@i + 1));
}
}

Append string in LESS variable

As explained in comments, your problem is the recursive variable definition in the below line. Less does not support this as explained in this answer and this one.

@classes: "@{classes}, @{newClass}";

Based on your requirement explanation in comments (that there would be some extra padding etc when the colors are different), you could use one of the below methods.

Option 1: (will add the padding to every class and so repeated code)

.some-mixin(@newClass,@color,@color2){
.@{newClass}{
color: @color;
& when not (@color = @color2){
padding: 4px;
}
}
}

.some-mixin(abc,#ffffff,#000000);
.some-mixin(xyz,#ffffff,#ffffff);
.some-mixin(jkl,#ff00f0,#ff00ff);

The above Less would compile into below CSS:

.abc { 
color: #ffffff;
padding: 4px; /* colors were different */
}
.xyz {
color: #ffffff;
}
.jkl {
color: #ff00f0;
padding: 4px; /* colors were different */
}

Option 2: (uses a dummy class + extend and so lesser code)

This option is probably what you are looking for as it avoids code repetition. We cannot extend a mixin and hence we use a dummy class. This should not be a big concern because it just adds one extra line to output CSS.

.common-padding-diff-color{ /* all styles that are needed when colors are different */
padding: 4px;
}
.some-mixin(@newClass,@color,@color2){
.@{newClass}{
color: @color;
& when not (@color = @color2){
&:extend(.common-padding-diff-color);
}
}
}

.some-mixin(abc,#ffffff,#000000);
.some-mixin(xyz,#ffffff,#ffffff);
.some-mixin(jkl,#ff00f0,#ff00ff);

This would compile into

.common-padding-diff-color,
.abc,
.jkl {
padding: 4px; /* style applied for all cases where colors are not same */
}
.abc {
color: #ffffff;
}
.xyz {
color: #ffffff;
}
.jkl {
color: #ff00f0;
}

Sass loop incrementing by 100

$max: 26;
$step: 100;
$j: 150;

@for $i from 1 through $max {
$k: $i * $step;
$j: $j + 50;

// Use $k and $j in the styles instead of $i

Instead of adding $step to $i, you need to multiply * it with the $step. Also, change the variable name from $i to something else since it will override the loop variable $i.

Edit: Just realized you will also need to change your start and end of the loop.

IMPROVED ANSWER:
My old answer works but, I believe it works at the expense of readability. I would suggest you initialize $k with an initial value just like you did with $j and add $step to $k each time independent of $i in each iteration:

$max: 26;
$j: 150;
$k: 100;
$stepK: 100;
$stepJ: 50;

@for $i from 10 through $max {
$k: $k + $stepK;
$j: $j + $stepJ;

// Use $k and $j in the styles instead of $i


Related Topics



Leave a reply



Submit