Problem with Loop through nested sass maps - css

I have a simple sass map with colors. I loop through it and generate css vars. Works great so far.
$colors: (
primary: red,
secondary: blue,
)
#each $color, $value in $colors {
--#{$color}: #{$value};
}
Then I extended this map with themes. There I loop through again per theme and create the css vars. This also works great.
$colors2: (
theme-1: (
primary: red,
secondary: blue,
),
theme-2: (
primary: yellow,
secondary: pink,
)
)
#each $theme, $colors in $colors2 {
&[data-theme="#{$theme}"] {
#each $color, $value in $colors {
--#{$color}: #{$value};
}
}
}
But now I need to find a way to combine the two. Unfortunately I haven't found a way so far to loop through the following list, create the individual css vars and the data-attributes + css vars.
$colors3: (
primary: green,
secondary: purple,
theme-1: (
primary: red,
secondary: blue,
),
theme-2: (
primary: yellow,
secondary: pink,
)
)
Does anyone of you maybe have an idea how I can do this? In the end something like this should come out:
:root {
--color-primary: green;
--color-secondary: purple;
&[data-theme="theme-1"] {
--color-primary: red;
--color-secondary: blue;
}
&[data-theme="theme-2"] {
--color-primary: yellow;
--color-secondary: pink;
}
}
Thank you very much for your help!
Kind regards
Marco

i found a way to get it to work :)
here is my solution just in case, someone else needs it.
:root {
#each $key, $value in $colors {
#if type-of($value) == map {
&[data-theme="#{$key}"] {
#each $theme-key, $theme-value in $value {
--color-#{$theme-key}: #{$theme-value};
}
}
} #else {
--color-#{$key}: #{$value};
}
}
}

Related

SCSS - How to loop nested maps within map-merge?

I have a nicely working SCSS function with which I can call any color like so:
color: clr(milk);
Now I would like to make some kind of loop within the map-merge so that when, for example I add a new nested map called 'tertiary' with some other colors, the colors automatically become available without having to add
map-get(colors, 'tertiary')
to the map-merge. Does anyone know how to do this? Below is my current function:
$colors: (
primary: (
milk: #fff,
cola: #000,
mine-shaft: #232323,
),
secondary: (
pampas: #f4f1ef,
pearl-brush: #e9e2dd,
alto: #ddd,
),
);
// Color generation
#function clr($color) {
$color: map-get(map-merge(map-get($colors, 'primary'), map-get($colors, 'secondary')), $color);
#return $color;
}
Nice project! map-merge() can be indeed used to achieve what you want, however it adds an unnecessary step. I'd recommend to simply use a nested loop coupled with an #if statement.
#function clr($find) {
#each $colorCategoryName, $colorCategory in $colors {
#each $colorName, $color in $colorCategory {
#if $find == $colorName {
#return $color;
}
}
}
}
For example: This...
$colors: (
"primary": (
"milk": #fff,
"cola": #000,
"mine-shaft": #232323,
),
"secondary": (
"pampas": #f4f1ef,
"pearl-brush": #e9e2dd,
"alto": #ddd,
),
);
#function clr($find) {
#each $colorCategoryName, $colorCategory in $colors {
#each $colorName, $color in $colorCategory {
#if $find == $colorName {
#return $color;
}
}
}
}
body {
color: clr(cola);
background-color: clr(pampas);
}
Will output:
body {
color: #000;
background-color: #f4f1ef;
}
Let me know if this isn't what you were looking for!

How to override two maps in scss

SCSS
$colors: (
primary: red,
secondary: blue,
accent: #ddd,
) !default;
$colors: (
primary: green,
secondary: purple,
black: #000,
white: #fff,
);
#each $color, $value in $colors {
.alert-#{$color} {
color: $value;
}
}
Result
.alert-primary {
color: green;
}
.alert-secondary {
color: purple;
}
.alert-black {
color: #000;
}
.alert-white {
color: #fff;
}
I wanted to create a SASS framework something like bootstrap. Wanted to override theme colors. How can i merge these maps to get something like this? I want a simple solution.
Expected result
.alert-accent {
color: #ddd;
}
.alert-primary {
color: green;
}
.alert-secondary {
color: purple;
}
.alert-black {
color: #000;
}
.alert-white {
color: #fff;
}
You can use map-merge:
$colors: map-merge($colors, (
primary: green,
secondary: purple,
black: #000,
white: #fff
));
From the documentation, this function:
Returns a new map with all the keys and values from both $map1 and $map2.
This can also be used to add a new value or overrwrite a value in $map1, by passing a single key/value pair as $map2.
If both $map1 and $map2 have the same key, $map2’s value takes precedence.

scss interpolation help for passing the values

I need to create dynamic classes for which I am creating a scss code to create the classes for all the possible values. Below is my code:-
$colors: (
"black": "0,0,0",
"white": "255,255,255",
"red" : "255,0,0"
);
$opacity:9;
#for $i from 0 through $opacity {
$j:$i/10;
#each $color, $rgb in $colors {
$rgba: "#{$rgb},#{$j}";
.background-#{$color}-#{$i} {
background: #{$rgba};
}
}
}
I want it to give out put as :-
.background-black-0 {
background: rgba(0,0,0,0);
}
.background-white-0 {
background: rgba(255,255,255,0);
}
.background-red-0 {
background: rgba(255,0,0,0);
}
.background-black-1 {
background: rgba(0,0,0,0.1);
}
.background-white-1 {
background: rgba(255,255,255,0.1);
}
.background-red-1 {
background: rgba(255,0,0,0.1);
}
struggling with the interpolation for rgba(). Otherwise its getting the exact values I want. If you check my code in https://www.sassmeister.com/ you will see it.
You could use directly your colors as rgb color in your map and then add opacity in your #for loop:
$colors: (
"black": rgb(0,0,0),
"white": rgb(255,255,255),
"red": rgb(255,0,0)
);
$opacity:9;
#for $i from 0 through $opacity {
$j:$i/10;
#each $color, $rgb in $colors {
.background-#{$color}-#{$i} {
background: rgba($rgb, $j);
}
}
}

SASS loop within a loop variable

I'm trying to loop with a loop, the first loop is a list and the second is a sass map. The items in the list represent sass maps. The issue is, in the second loop, an error is thrown if I simply add $ in front of #{$event_type}. Should I be approaching this differently?
The list:
$event_list: 'summit', 'awards', 'awards-europe', 'other';
Example map:
$awards: (
content-marketing: #F47521,
digiday: #FFDD00,
publishing: #89C63F,
signal: #33AADB,
video: $pink
);
Broken Loop:
#each $event_type in $event_list {
#each $event, $color in #{$event_type} {
&.#{$event_type}-#{$event} {
section h2, h3 {
color: #{$color};
border-color: #{$color};
}
// More CSS
}
}
}
It may be better to use a multi-map structure:
$event_list: (
'summit': (
key1: #000,
key2: #fff
),
'awards': (
content-marketing: #F47521,
digiday: #FFDD00,
publishing: #89C63F,
signal: #33AADB,
video: $pink
)
);
The loop will be about the same:
// Note the added $map variable.
#each $event_type, $map in $event_list {
#each $event, $color in $map {
&.#{$event_type}-#{$event} {
section h2, h3 {
color: #{$color};
border-color: #{$color};
}
// More CSS
}
}
}
SassMeister Demo

Using SCSS variable inside a variable

I want to use a SCSS loop as below:
#each $var in dark, purple, green, cyan, silver, white {
.text-#{$var} {
color: nth($color-, $var);
}
.btn-#{$var} {
background-color: nth($color-, $var);
}
}
in order to use the following variables:
$color-dark: #0D0E1E;
$color-purple: #333366;
$color-green: #33cc99;
$color-cyan: #00cccc;
$color-silver: #ccc;
$color-white: #fff;
but it is not working.
$color-#{$var} was not working as well. Can I do this?
nth gets an item in a list. The first argument is the list, the 2nd is an index in the list. Also SASS thinks anything with a $ is a variable, so $color- is a variable. You haven't defined $color- as a variable, and that's not your intended use.
DOCS.
But you can get your desired result with a map...
DEMO
$color-dark: #0D0E1E;
$color-purple: #333366;
$color-green: #33cc99;
$color-cyan: #00cccc;
$color-silver: #ccc;
$color-white: #fff;
$colors: (
dark: $color-dark,
purple: $color-purple,
green: $color-green,
cyan: $color-cyan,
silver: $color-silver,
white: $color-white
);
#each $name, $val in $colors {
.text-#{$name} {
color: $val;
}
.btn-#{$name} {
background-color: $val;
}
}

Resources