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

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!

Related

Problem with Loop through nested sass maps

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

How to change sass import file when body class is changing

Is any way to change scss file with color variables when class in body is changing?
I mean when I have in html:
<body class="custom"> ... </body>
I want to load in style.scss
#import 'custom';
and when I have
<body class="dark-mode"> ... </body>
I want to load in style.scss
#import 'dark-mode';
You can not make an #import depending on a condition, but there is a ton of possible other approaches to this. Here is a small framework I wrote back then.
#function keyFilter($iKey, $fKey, $rKey) {
#if ($iKey == $fKey) {
#return $rKey;
}
#return $iKey;
}
#function v($key) {
#return var(--#{$key});
}
//
$modes: (
"&": (
"color": #000,
),
"dark": (
"color": #fff,
),
);
//
#each $key, $map in $modes {
body#{keyFilter("[#{$key}]", "[&]", null)} {
#each $key, $value in $map {
--#{$key}: #{$value};
}
}
}
To "register" a new mode just nest another map in the $modes-map, you can add as many modes as you want. Keep in mind the "&"-mode represents the default-mode.
$modes: (
"&": (
//...
),
"dark": (
//...
),
//...
);
To register a new mode-depending variable just simply enter key and value to the respective mode.
$modes: (
"&": (
"color": #000,
"bgc": #fff,
"bgc-contrast": #eee,
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
To call a variable just use the v($key) function.
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
This compiling to:
body {
--color: #000;
--bgc: #fff;
--bgc-contrast: #eee;
}
body[dark] {
--color: #fff;
--bgc: #000;
--bgc-contrast: #424242;
}
body {
color: var(--color);
background-color: var(--bgc);
}
div.contrasted {
background-color: var(--bgc-contrast);
}
Note: you do not need to declare each variable for each mode. If a variable wasn't found for the current mode, this won't throw an error.For Example: This...
$modes: (
"&": (
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
//...
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
... is totally fine.

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/SCSS #each multiple arrays

I am trying to write a sass mixing using the values from two arrays to output my button classes. Not sure if what I am trying to do is possible at all.
So I have two arrays:
$buttonNames: ('black', 'primary', 'red', 'green', 'orange');
$buttonColors:(black, blue, red, green, orange);
and then my mixin is:
#mixin underlineButton($class, $name, $size, $color: black) {
.#{$class}-underline-#{$name} {
background-color: transparent;
border-bottom: $size + px solid $color;
border-radius: 0;
font-size: .75rem;
}
}
and then I do an #each loop for the names, and attempted to nest another loop inside this to get the colors. Obviously this isn't working! Just wondering if it is even possible.
#each $name in $buttonNames {
#each $color in $buttonColors {
#include underlineButton('btn', $name, 3, $color)
}
}
The desired output would be something like:
.btn-underline-black {
background-color: transparent;
border-bottom: 3px solid black;
border-radius: 0;
font-size: .75rem;
}
// .btn-underline-* for the rest of the matching keys and colors
Here's a DEMO
If you need to keep your values separate, in 2 lists, then you can...
// loop over $buttonNames
#each $buttonName in $buttonNames {
// Find the current index of $buttonNames...
$index: index($buttonNames, $buttonName);
// ... to pull the right from $buttonColors
#include underlineButton('btn', $buttonName, 3, nth($buttonColors, $index));
}
However, using a map is a little easier.
$buttons: (
'black': black,
'primary': blue,
'red': red,
'green': green
);
#each $buttonName, $color in $buttons {
#include underlineButton('btn', $buttonName, 3, $color)
}
$buttons: ('black', $black),
('primary', $primary),
('red', $red),
('green', $green);
#each $buttonName, $color in $buttons {
#include underlineButton('btn', $buttonName, 3, $color)
}

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

Resources