Get a variable name as a string - sass

I have a class with the name .text-black, I also have a map with some values.
$black: #000;
map:
dark: $black;
I want to loop through this map and create a new class with the $key and then extend they new class with using the value text-black.
There are 2 problems I have. The first I think I have solved, if I can get the $value as $black instead of #000. Then I can remove the $ using string replacement.
The second challenge however is proving a headache for me. I need to get $black in stead of #000.
Here is my code showing my approach so far.
// String Replacement to remove '$' from varaible name.
#function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
#if $index {
#return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace)
}
#return $string;
}
// get color from map
#function text-color($key: "weekly-digest") {
#return map-get($text-colors, $key);
}
$black: #000000;
// map text-colors
$text-colors: () !default;
$text-colors: map-merge(
(
"news": $black,
),
$text-colors
);
// Extendable classes.
.text-black{
color: $black;
}
// Loop function
#each $name, $value in $text-colors {
&--#{$name} {
background-color: $value;
#extend .text-#{$value} // This should return '.text-black' not '.text-#000000'
}
}

I try to give you 3 different solutions. In all these solutions I used 2 colors (black & red) only to see if they could work in combination:
1. Using a function str-split() (maybe the most intricated, but use your code)
I found a magical function that splits a string in 2 elements How to split a string into two lists of numbers in SASS?.
So my idea is to use that function (thanks to #dayenite: if you like this solution, please upvote him ;)) in a string using a character (in my example "_") to split your maps in 3 different value (something like 2 keys and 1 value):
1. "news"
2. "black"
3. "#000"
Your map could become something like this:
$text-colors: map-merge(
(
"news_black":$black,
"info_red": $red
),
$text-colors
);
This is all the code in action:
#function str-split($string, $separator) {
// empty array/list
$split-arr: ();
// first index of separator in string
$index : str-index($string, $separator);
// loop through string
#while $index != null {
// get the substring from the first character to the separator
$item: str-slice($string, 1, $index - 1);
// push item to array
$split-arr: append($split-arr, $item);
// remove item and separator from string
$string: str-slice($string, $index + 1);
// find new index of separator
$index : str-index($string, $separator);
}
// add the remaining string to list (the last item)
$split-arr: append($split-arr, $string);
#return $split-arr;
}
/* Example with 2 colors */
$black: #000000;
$red: #ff0000;
$text-colors: () !default;
$text-colors: map-merge(
(
"news_black":$black,
"info_red": $red //my add
),
$text-colors
);
// Extendable classes.
.text-black{
color: $black;
}
.text-red{
color: $red;
}
// Loop function
.my-div{
#each $name, $value in $text-colors {
$list: (); // Create every time an empty list with my 2 argoments, for example "news" and "black"
$split-values: str-split($name, "_"); //use the function to split the string
#each $valore in $split-values {
$list: append($list, str-split($valore, " "));
}
//set here the first part of the string (i.e. news/info)
&--#{nth($list, 1)} {
background-color: $value;
#extend .text-#{nth($list, 2)} // set here the second part: black/red
}
}
}
https://www.sassmeister.com/gist/08f699dba4436d3bae6a4d8b666e815b
2. Using a nested list
This time I created a simple nested list with 3 value ("news", "black", $black), the result is the same.
$black: #000000;
$red: #ff0000;
// list text-colors
$text-colors: (
( "news", "black", $black ),
( "info", "red", $red )
);
// Extendable classes.
.text-black{
color: $black;
}
.text-red{
color: $red;
}
.mydiv{
// Loop function
#each $item in $text-colors {
&--#{nth($item, 1)} {
background-color: nth($item, 3);
#extend .text-#{nth($item, 2)}
}
}
}
https://www.sassmeister.com/gist/59adf5ee60ea46dd7a24e94d7db91d85
3. Using a nested map
Here I use nested maps, but the structure is different from yours and I don't know if it's ok for you.
$black: #000000;
$red: #ff0000;
// map text-colors
$text-colors: (
news:(black: $black),
info:(red: $red)
);
.text-black{
color: $black;
}
.text-red{
color: $red;
}
.mydiv{
// Loop function
#each $name, $value in $text-colors {
&--#{$name} {
#each $name-color, $value-color in $value {
background-color: $value-color;
#extend .text-#{$name-color}
}
}
}
}
https://www.sassmeister.com/gist/8ddec08755befc84f6e4846fbc625130
Well, I haven't another ideas. I hope at least one way could help you to solve your problem.
Cheers :)

Related

Assign multiple values from variable in SCSS

I've variable named classes where each key has style values
$classes : (
class1: (
font-size: 15px,
color: blue
),
class2: (
font-size: 20px,
font-weight: 600,
background-color: red
)
)
I'm writing #each rule in scss like below.
#each $key, $value in $classes {
.#{$label} {
// How to assign values here
}
}
I'm able to get class name but unable to add all values here. Any idea how to do it.
I found the solution of above problem.
We can't assign key - value pair directly in scss.
Here we need to apply #each loop till the end
#each $key, $value in $classes {
.#{$label} {
#each $key1, $value1 in $value {
.#{$key1}: $value1
}
}
}

Using sass #function inside #for

I have some sass function to create same margin, and it is inside loop like this
$short-margins: ( top: 'mt', left: 'ml', bottom: 'mb', right: 'mr' );
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: #{$i}px;
}
}
}
This will create margin classes like this mr-0 and so on until mr-200, the problem is in line
margin-#{$position}: #{$i}px;
There I create px in loop, but i need that to be in rem? I ahve some function like this
$base-font-size: 14px;
// Remove units from the given value.
#function strip-unit($number) {
#if type-of($number) == 'number' and not unitless($number) {
#return $number / ($number * 0 + 1);
}
#return $number;
}
// convert only single px value to rem
#function single-px-to-rem($value) {
$unitless-root-font-size: strip-unit($base-font-size);
#return $value / $unitless-root-font-size * 1rem;
}
But when i want to use function inside loop like this
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: single-px-to-rem(#{$i});
}
}
}
It does not compile throw me error, does any body knows how ti use sass #function inside #for?
You are doing it right, but you need to send the $i value without interpolation:
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: single-px-to-rem($i);
}
}
}

global var update from scss function

I have a scss function
#function darken($color,$percent) {
...
#return $calculated-color;
}
This function overrides the darken() function, but adds some extra features.
Now, I wonder if it is possible to somehow store all calls to this function in some map and then after all function calls has been made run that map trough a mixin such as:
$calc-colors:() !global;
$calc-colors:map-merge(('thisvaluewillbeexported':1),$calc-colors);
#function test($color,$percent) {
$col: darken($color,$percent);
// $calc-colors: append($calc-colors,'--'$color); --not working
// $calc-colors:map-merge(('--'+$color:$col),$calc-colors); --not working
#return $col;
}
.test {
color:test(pink,24%);
}
.test2 {
color:test(red,24%);
}
:export{
#each $bp, $value in $calc-colors {
#{$bp}: #{$value};
}
}
//gives only thisvaluewillbeexported:1
My goal would to somehow get all calls to my test function recorded into the :export{} attribute in order to be able to fetch the values from javascript.
// My preferred output would be:
{
'thisvaluewillbeexported':1,
'--pink':'#ff4666',
'--red':'#850000'
}
You should set variable !global inside function.
Sassmeister demo.
Article about variable scope in Sass.
#function set-color($color-name, $darken-ration) {
$darken-color: darken($color-name, $darken-ration);
$calc-colors: map-merge($calc-colors, ('--' + $color-name: $darken-color)) !global;
#return $darken-color;
}
$calc-colors: map-merge(('thisvaluewillbeexported': 1), ());
a {
color: set-color(green, 10%);
}
b {
color: set-color(red, 10%);
}
c {
#each $name, $value in $calc-colors {
#{$name}: #{$value};
}
}
Css output:
a {
color: #004d00;
}
b {
color: #cc0000;
}
c {
thisvaluewillbeexported: 1;
--green: #004d00;
--red: #cc0000;
}

Pass sass variable inside loop as a selector without value

I want to use a mixin to give me the option to pass additional selectors as parameters as needed, in addition to the selectors already established. But I’m getting an error here. Not sure what I'm missing, unless you just can't use variables inside of a loop without passing a value to each item and I'm going about this all wrong.
#mixin light-section-text( $selectors: null ) {
#if ( $selectors ) {
#for $i from 1 through length( $selectors ) {
#{nth( $selectors, $i )},
}
}
p,
address,
li {
color: $white;
}
}
The desired output of #include light-section-text( "body", "strong", "strong a" ); in this case would be:
body,
strong,
strong a,
p,
address,
li {
color: #fff; }
First, you can't directly pass the selectors list to the mixin function as it would cause $selectors to be the first string. So you have to first declare a list variable and then pass that variable to the function.
Second, you should simply use the Placeholders functionality offered by Sass which makes use of #extend and the % character.
%color{
color: white;
}
#mixin light-section-text( $selectors: null ) {
#if ( $selectors ) {
#for $i from 1 through length( $selectors ) {
#{nth( $selectors, $i )}{
#extend %color;
}
}
}
p,
address,
li {
#extend %color;
}
}
$list-variable: "body", "strong", "strong a";
#include light-section-text($list-variable);
Alternate Method
You don't even need to use the mixin function as this task can be handled by sass placeholder alone.
$white : white;
%color{
color: $white;
}
$list-variable: "body", "strong", "strong a", "p", "address", "li";
#each $item in $list-variable {
#{$item} {
#extend %color;
}
}

Sass mixin with multiple variables attached to the same style

I have a Sass list of class name and color:
$txt-colors:
"music" $color-orange,
"video" $color-green,
"photo" $color-blue;
What I'm currently doing is looping through each line and applying that style to that color
#each $color in $txt-colors {
".#{nth($color, 1)}" {
color: nth($color, 2);
}
}
I now want the ability to be able to pass multiple class names that corresponds to that color that would output something like this:
.music, .instrument {
color: $color-orange;
}
Where the list would now look like:
$txt-colors:
"music" "instrument" $color-orange,
"video" $color-green,
"photo" $color-blue;
I think it would much cleaner if I do it with a mixin that accepts multiple parameters, but how do I do it?
Thanks!
Use another list.
$txt-colors:
("music" "instrument") orange,
"video" green,
"photo" blue;
#each $classes, $color in $txt-colors {
$sel: ();
#each $c in $classes {
$sel: append($sel, '.#{$c}', comma);
}
#{$sel} {
color: $color;
}
}
Alternately, just have the first element in your list be the selector:
$txt-colors:
".music, .instrument" orange,
".video" green,
".photo" blue;
#each $sel, $color in $txt-colors {
#{$sel} {
color: $color;
}
}

Resources