SASS loop to simplify repetitive code - css

I have this set of repetitive classes (variables names and overall code simplified for demonstration purposes):
&-category01 { background: linear-gradient((nth($colors-category01, 1), nth($colors-category01, 2)); }
&-category02 { background: linear-gradient((nth($colors-category02, 1), nth($colors-category02, 2)); }
&-category03 { background: linear-gradient((nth($colors-category03, 1), nth($colors-category03, 2)); }
&-category04 { background: linear-gradient((nth($colors-category04, 1), nth($colors-category04, 2)); }
The $colors-... bit comes from a map:
$colors-category01: (#ab1b99, #8f017d);
$colors-category02 (#d2544b, #87352f);
How can I simplify this code with a SASS loop?
Remember that variable interpolation is not valid inside a loop or mixin. Also, my real code is more complex than this practical situation.
My goal is to get this as output:
.category01 { linear-gradient(#ab1b99, #8f017d); }
.category02 { linear-gradient(#d2544b, #87352f); }
(etc...)

If all your color category variables have the same number of colors in them, you can create a new variable that is just a list of all the variables with actual values in them and then loop through that new variable with an #each function.
See #each loop with index for more on that.
$colors-category01: (#ab1b99, #8f017d);
$colors-category02: (#d2544b, #87352f);
$colors-category03: (#ffffff, #000000);
$colors-category04: (#f0f0f0, #333333);
$color-categories: $colors-category01, $colors-category02, $colors-category03, $colors-category04;
#each $category in $color-categories {
$i: index($color-categories, $category);
.category0#{$i} {
background-image: linear-gradient(nth($category, 1), nth($category, 2));
}
}
Results in:
.category01 {
background-image: linear-gradient(#ab1b99, #8f017d);
}
.category02 {
background-image: linear-gradient(#d2544b, #87352f);
}
.category03 {
background-image: linear-gradient(#ffffff, #000000);
}
.category04 {
background-image: linear-gradient(#f0f0f0, #333333);
}
Unfortunately if the reality is that your different color categories actually have different numbers of colors in them, then things get a lot more complicated. I can't personally figure out a way you could loop through an indefinite number of values in those maps and chain together the background-image value. (Which is not to say for sure that it can't be done, but it may well end up taking you longer to figure out than the time you'd be saving by using a loop.)
But you could possibly do a bunch of #if statements to cover a reasonable number of possibilities, if you really wanted to.
$colors-category01: (#ab1b99, #8f017d);
$colors-category02: (#d2544b, #87352f);
$colors-category03: (#ffffff, #000000, #2d2d77);
$colors-category04: (#f0f0f0, #333333, #00ff5e, #ffa900);
$color-categories: $colors-category01, $colors-category02, $colors-category03, $colors-category04;
#each $category in $color-categories {
$i: index($color-categories, $category);
.category0#{$i} {
#if length($category) == 2 {
background-image: linear-gradient(nth($category, 1), nth($category, 2));
}
#if length($category) == 3 {
background-image: linear-gradient(nth($category, 1), nth($category, 2), nth($category, 3));
}
#if length($category) == 4 {
background-image: linear-gradient(nth($category, 1), nth($category, 2), nth($category, 3), nth($category, 4));
}
//et cetera
}
}
Outputs:
.category01 {
background-image: linear-gradient(#ab1b99, #8f017d);
}
.category02 {
background-image: linear-gradient(#d2544b, #87352f);
}
.category03 {
background-image: linear-gradient(#ffffff, #000000, #2d2d77);
}
.category04 {
background-image: linear-gradient(#f0f0f0, #333333, #00ff5e, #ffa900);
}

Related

Gaps in sass css output

I'm writing some sass to generate a set of icons based on a series of parameters. I have a function that analyses a set of variables and then returns a 'scenario' variable which in turn is used to filter the information taken from the nested map where everything is stored.
The code which retreives the information from the nested map is as follows:
#each $key-lv0, $lv0 in $icon-config {
#if($key-lv0 == $scenario) {
.icon{
#each $key-lv1, $lv1 in $lv0 {
#if type-of($lv1) != "map" {
#{$key-lv1}: $lv1;
}
#each $key-lv2, $lv2 in $lv1 {
#if type-of($lv2) != "map" {
.#{$key-lv1} {
#{$key-lv2}: $lv2;
}
}
#each $key-lv3, $lv3 in $lv2 {
#if($key-lv2 == "hover") {
.#{$key-lv1}:#{$key-lv2} {
#{$key-lv3}: $lv3;
}
} #else {
.#{$key-lv1} #{$key-lv2} {
#{$key-lv3}: $lv3;
}
}
}
}
}
}
}
}
... and this produces something along these lines:
.icon .icon-header {
background-color: #00a9f0;
}
.icon .icon-header:hover {
border-color: #040100;
}
... etc ...
... which is fine - repeated statements aside whicvh I'll deal with later.
The problem is the cap between ".icon" and ".icon-header". These classes will all be used in a single element and for the css to be interpretted correctly it needs to generate something like this:
.icon.icon-header {
background-color: #00a9f0;
}
.icon.icon-header:hover {
border-color: #040100;
}
I've tried bringing ".icon" down like so:
#if type-of($lv1) != "map" {
.icon#{$key-lv1}: $lv1;
}
and removing it from the top but sass rejects this with the following error:
Error: Properties are only allowed within rules, directives, mixin includes, or other properties.
It seems such a minor thing but it's nagging me and I can't seem to find an answer.

Pass mixin as mixin param in LESS

I have the the following mixin:
.image-ui-wave-pos (#num, #selected:0) when (#selected = 0) {
background-position: -(#image-ui-wave-width * #num) -28px;
}
I'd like to pass it to the following mixin as parameter:
.small-button (#pos-macro, #buttons, #i) when (#i > 0) {
#button: extract(#buttons, #i);
&.#{button} {
.pos-macro (#i);
}
.small-button(#pos-macro, #buttons, #i - 1);
}
as the parameter #pos-macro calling it like this:
.small-button (.image-ui-wave-pos, #wave-buttons);
But it doesn't compile. How to do so?

LESS - Create ID by looping through two variables

I am trying to create a loop that outputs all of the possible combinations between coordinates -2,-2 to 2,2. Is there any way to do this without creating multiple loops?
Desired Output
#p1x0,#p2x0,#p-1x0,#p-2x0,#p1x1,#p-1x-1,#p-1x1,#p1x-1,#p2x2,#p-2x-2,p2x-2,p-2x2,#p2x1,#p2x-1,#p1x2,#p1x-2,#p-2x1,#p-2x-1,#p-1x2,#p-1x-2,#p0x-1,#p0x-2,#p0x0,#p0x1,#p0x2{}
Current Attempt
#cube-side {
border:red;
}
.create-cubes(#n, #i: -2, #z: -2, #side-sum:#i + #z) when (#side-sum =< #n) {
& when (#i < #z) {
.create-cubes(#n, #i+1);
}
& when (#z < #i) {
.create-cubes(#n, #z+1);
}
#p#{i}x#{z}:extend(#cube-side) {}
}
.create-cubes(4);
Output
#cube-side,
#p-2x-2 {
border: red;
}
There's way to do this w/o any loops at all:
#cube-side {
border: red;
}
-2, -1, 0, 1, 2 {
#p&x&:extend(#cube-side) {}
}
Though for an arbitrary list of values just a nested loop is the simplest solution of course (see for example), e.g. (in "pure Less") something like:
.create-cubes(-2, 2);
.create-cubes(#min, #max) {
.i; .i(#i: #min) when (#i <= #max) {
.j; .i(#i + 1);
}
.j(#j: #min) when (#j <= #max) {
#p#{i}x#{j}:extend(#cube-side) {}
.j(#j + 1);
}
}

Generating classes with a loop

I have a list of colors and I want to generate classes using these colors:
CSS
#color1: #b37974;
#color2: #ffa385;
#color3: #ff5500;
#color4: #b2682e;
This is the code i'm using:
Less
.loopingClass(#index) when (#index > 0) {
#ctype: "color#{index}";
.setClass(#color,#cindex) {
.btn-color-#{cindex} {
background-color:#{color} ;
}
}
.setClass(e(##ctype),#index);
.loopingClass(#index - 1);
};
.loopingClass(2);
When I try to compile the code with gulp, I receive "Unrecognised input" error. When I remove background-color: #{color} the error goes away. What is my mistake in this code?
Update:
The correct code is:
.loopingClass(#index) when (#index > 0) {
#ctype: "color#{index}";
.setClass(#color,#cindex) {
.btn-color-#{cindex} {
background-color:#color ;
}
}
.setClass(##ctype,#index);
.loopingClass(#index - 1);
};
.loopingClass(2);
As I already mentioned in comments above the error there is in e function (which does not make any sense there). The correct code would look like this:
#color1: #b37974;
#color2: #ffa385;
#color3: #ff5500;
#color4: #b2682e;
.loopingClass(#index) when (#index > 0) {
#ctype: "color#{index}";
.setClass(#color, #cindex) {
.btn-color-#{cindex} {
background-color: #color;
}
}
.setClass(##ctype, #index);
.loopingClass(#index - 1);
}
.loopingClass(2);
In fact all this can be simplified to just:
#color1: #b37974;
#color2: #ffa385;
#color3: #ff5500;
#color4: #b2682e;
.loopingClass(#index) when (#index > 0) {
.btn-color-#{index} {
#color: "color#{index}";
background-color: ##color;
}
.loopingClass(#index - 1);
}
.loopingClass(2);
More over the whole thing could be even more simple since you don't need to emulate arrays via "indexed variable names" because you can use array directly (unless you need to refer to those vars separately elsewhere):
#colors:
#b37974,
#ffa385,
#ff5500,
#b2682e;
.loopingClass(2);
.loopingClass(#index) when (#index > 0) {
.loopingClass(#index - 1);
.btn-color-#{index} {
background-color: extract(#colors, #index);
}
}
And finally (since I entered "optimizations never end" mode anyway), same thing with a bit of syntactic sugar:
#import "for";
#colors:
#b37974
#ffa385
#ff5500
#b2682e;
.btn-color- {
.for(#colors); .-each(#color) {
&#{i} {background-color: #color}
}
}
where imported for is thefor.

SASS list variable default value

I have the following list in SASS and some classes:
$list: 10px, 15px, 20px
.class1: nth($list, 1)
.class2: nth($list, 2)
.class3: nth($list, 3)
Is there any to make a default value for the list, so if I don't show 3 different values, the nth($list, 3) doesn't return an error and assigns a default value instead?
index() wasn't working for me cause it needs the value as second parameter.
I would recommend using a function as getter for the list values like:
$list: 10px, 15px, 20px;
#function getListEntry ($list, $index) {
#if length($list) >= $index {
#return nth($list, $index);
};
#return 0px;
}
.class1{
margin: getListEntry($list, 1)
}
DEMO
You can use Index and if functions of sass to check if the index exists in the list.
For example, if you set the default value with 2:
$value: index($list,4);
#if $value == null {
$value: 2;
} #else {
$value : nth($list, $value);
}
Hope it helps.
Regards.

Resources