SASS loop within a loop variable - css

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

Related

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 - 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!

Is it possible to create a loop to cycle through a map however stop at a certain index in the map ? - SASS

I have a color theme map with five colors and I would like to create a mixin to accept a number argument which would be the stopping point. Then the mixin would execute the loop however stop the map loop when it reaches the mixin argument number.
Tried to read up on this however could not find any answers or solutions to get this to happen.
Code Example:
// color map
$colors: (
"r": red,
"b": blue,
"g": green,
"y": yellow,
"o": orange,
)
Current Situation: The each loop keeps cycling through the map until it reaches the end. Hence it will create 5 selectors with their background-colors as the values in the map.
#mixin color_theme($map) {
#each $col, $color in $map {
&.#{$col} {
background-color: $color;
}
}
}
Goal: To create a mixin with a loop to cycle only to a fixed number of values in the map.
You could use index (related answer: SASS - get map item value by item index) to get the map index number and then use it to stop the loop at a certain number:
// color map
$colors: (
"r": red,
"b": blue,
"g": green,
"y": yellow,
"o": orange
);
#mixin color_theme($map, $stop) {
#each $col, $color in $map {
$i: index($map, $col $color);
#if ($i <= $stop){
&.#{$col} {
background-color: $color;
}
}
}
}
div{
#include color_theme($colors, 4);
}
Your output:
div.r {
background-color: red;
}
div.b {
background-color: blue;
}
div.g {
background-color: green;
}
div.y {
background-color: yellow;
}

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

Sass ampersand and two parents together in mixin?

I'm faced with the task of theming the site. I found a suitable mixin. Everything would work well, if not for the mixin for events. It turns out that I need to do something, so that if the topic's mixin is invoked in the mixin of events, then the class did not go cascade, but substituted for the topic class, the .no-touchevents class on the html tag.
Ideally, that would be so on the output:
.card {
color: #fff;
}
.t-dark .card {
color: #000;
}
.no-touchevents .card:hover {
color: #000;
}
.t-dark.no-touchevents .card:hover {
color: #fff;
}
It's a little hacky (or maybe a lot hacky) but by adding an additional parameter to the themify mixin and building our selector manually, you can achieve the output you're looking for.
$themes: (
dark: (
colorDark: #000,
colorLight: #fff
)
);
#mixin on-event() {
.no-touchevents &:hover {
#content;
}
}
#mixin themify($themes, $flat: ' ') { // Add a parameter to separate by default
#each $theme, $map in $themes {
#at-root .t-#{$theme}#{$flat}#{&} { // Build our selector manually
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
.card {
color: #fff;
#include themify($themes) {
color: themed('colorDark')
}
#include on-event() {
color: #000;
#include themify($themes, '') { // Tell themify not to separate selectors
color: themed('colorLight')
}
}
}

Resources