Create a recursion mixin scss - css

I am trying to create a recursion #mixin. The mixin has the task to append a selector from a given list and apply a CSS rules. The result of this has to look like this:
[data-tag]:not([conref]) {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid] {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid][draggable] {
border: dashed 2px #2999d1;
}
[data-tag]:not([conref])[nodeid][draggable][class] {
border: dashed 2px #2999d1;
}
The #mixin which I have created looks like this:
#mixin set-border-to-selectors($list, $item){
#if false == (index($list, $item)){
#error "Fail: #{$item} not in list: #{$list}";
}
#else{
$index: index($list, $item);
#debug "index: #{$index}";
$item: nth($list, $index);
&[#{$item}]{
#include set-data-tag-border;
#if($index + 1 <= length($list)){
$item: nth($list, $index + 1);
#include set-border-to-selectors($list, $item);
}
}
}
}
The first time I use the function it works pretty well.
#mixin show-data-tag-border{
$data-tag-list: (#{$id}, draggable, class);
$data-tag-list-two: (#{$id}, #{$conref}, class);
&[data-tag]:not([#{$conref}]){
#include set-data-tag-border;
#include set-border-to-selectors($data-tag-list, #{$id});
}
&[data-tag]{
#include set-border-to-selectors($data-tag-list-two, #{$id});
}
But the second call of the set-border-to-selector function throws an error, because the $index in the recusion #mixin does not contain a number. It is empty.
I call the function from p{#include show-data-tag-border;} and the border will be set in #mixin set-data-tag-border{border: solid 1px black}. And this two variables:
$id: id;
$conref: conref;
Is there something wrong what I do not see or is this approach wrong to create an recursion in SCSS? I won't use this function only one time and write one or more similar function below to accomplish this.

I'm not gonna try to repair your code, as it's too big. Recursion should work just fine like this:
#mixin set-borders ($list, $index: 1) {
#if $index <= length($list) {
#if $index == 1 {
&#{nth($list, $index)} {
#include set-borders($list, $index + 1);
}
} #else {
&,
&#{nth($list, $index)} {
#include set-borders($list, $index + 1);
}
}
} #else {
// ... (set the required properties)
}
}

The problem was the declaration of the list elements. I have to make tick marks.
So instead of this:
$data-tag-list: (#{$id}, draggable, class);
$data-tag-list-two: (#{$id}, #{$conref}, class);
I have to enclose every element in the list with tick marks:
$data-tag-list: ('#{$id}', 'draggable', 'class');
$data-tag-list-two: ('#{$id}', '#{$conref}', 'class');
and it works!
Thank you hon2a for your help!
Here is the full code for check up:
$id: id;
$conref: conref;
#mixin set-data-tag-border{border: solid 1px black}
#mixin set-border-to-selectors($list, $item){
#if false == (index($list, $item)){
#error "Fail: #{$item} not in list: #{$list}";
}
#else{
$index: index($list, $item);
#debug "index: #{$index}";
$item: nth($list, $index);
&[#{$item}]{
#include set-data-tag-border;
#if($index + 1 <= length($list)){
$item: nth($list, $index + 1);
#include set-border-to-selectors($list, $item);
}
}
}
}
#mixin show-data-tag-border{
$data-tag-list: ('#{$id}', 'draggable', 'class');
$data-tag-list-two: ('#{$id}', '#{$conref}', 'class');
&[data-tag]:not([#{$conref}]){
#include set-data-tag-border;
#include set-border-to-selectors($data-tag-list, #{$id});
}
&[data-tag]{
#include set-border-to-selectors($data-tag-list-two, #{$id});
}
}
p{#include show-data-tag-border;}

Related

How to insert an ampersand conditionally in a SASS mixin?

Below is some sass Im trying out, The css generated is correct, Im just trying to reduce my mixin duplication. The two mixins genProps and genPropsAmp are exactly the same except that the Amp mixin uses the & to prefix the rule with &. I tried to just add another var to the original mixin $amp: false and prefix my rule with an if so it became #if $amp {&}.#{$class}-... but that didnt work nor did trying to have it as an interpolation. Is there away of conditionaly placing the ampersand so I can just reuse the one mixin?
SASS:
$max_size: 0;
$sub_suffixes: '', 'q', 'h', 't';
$sub_divs: '', '.25', '.5', '.75';
#mixin genProps($class, $props, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
#mixin genPropsAmp($class, $props, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
&.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
.root{
#include genProps(f, font-size, a);
#include genPropsAmp(m, margin);
}
CSS:
/* without amp */
.root .f-0 {font-size: var(--a-0);}
.root .f-0q {font-size: var(--a-0q);}
/*...*/
/* with amp */
.root.m-0 {margin: var(--r-0);}
.root.m-0q {margin: var(--r-0q);}
/*...*/
You can use a ternary operator: if($amp, "&", "")
#mixin genProps($class, $props, $amp, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
#{if($amp, "&", "")}.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
#mixin genProps($class, $props, $amp: false, $val_prefix: r, $max_size: $max_size, $sub_suffixes: $sub_suffixes) {
#for $i from 0 through $max_size {
#each $sub_suffix in $sub_suffixes {
#if $amp {
&.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
} #else {
.#{$class}-#{$i}#{$sub_suffix} {
#each $prop in $props {
#{$prop}: var(--#{$val_prefix}-#{$i}#{$sub_suffix});
}
}
}
}
}
}
this is the best Iv got so far, not great as theres still a lot of duplication in the if elses but at least I only have one mixin now

#mixin with $variables as params inside a #for loop SASS SCSS

I have a very challenging code for SASS SCSS lovers.
my code and loop challenge:
#for $i from 1 through 8 {
&.-overlay-#{$i} {
#include box-shadow($brand-color-#{$i});
}
}
The mixin ( what inside doesn't matters )
#mixin box-shadow($color: $black) {
box-shadow: inset 194px -7px 200px 0 rgba($color, .42);
}
The list of colors ( I do have 8 colors )
$brand-color-1: red;
$brand-color-2: blue;
$brand-color-3: green; ... so on
The $brand-color-XX variable is passed to the #mixin
What I want to do is, create with #for loop these variations:
.-overlay-1
.-overlay-2
.-overlay-3 ...so on
But that #for is not working at the moment, what do am I missing here?
Regards
M
The #for was working, but you were trying to use variables in a way they're not meant to be used. You can't combine 2 SASS/SCSS variables to make one. You don't have a variable called $brand-color- so the compiler didn't know what to do with that. But you can get the result you want with a list.
DEMO
$brand-color-1: red;
$brand-color-2: blue;
$brand-color-3: green;
$colors: $brand-color-1, $brand-color-2, $brand-color-3;
#mixin box-shadow($color: $black) {
box-shadow: inset 194px -7px 200px 0 rgba($color, .42);
}
#for $i from 1 through length($colors) {
.-overlay-#{$i} {
#include box-shadow(nth($colors, $i));
}
}
You said the result you wanted was, .-overlay-1 .-overlay-2 .-overlay-3 ...so on, so I'm not sure what you were doing with the &. The & is used to refer to the parent class. If you want to nest your created classes, you can do that too...
.cool {
#for $i from 1 through length($colors) {
.-overlay-#{$i} {
#include box-shadow(nth($colors, $i));
}
}
}
...or put them at the same level as the parent...
.cool {
#for $i from 1 through length($colors) {
&.-overlay-#{$i} {
#include box-shadow(nth($colors, $i));
}
}
}
...or create a different class
.cool {
#for $i from 1 through length($colors) {
&-overlay-#{$i} {
#include box-shadow(nth($colors, $i));
}
}
}

SASS and data attribute multiple

I have a problem with the nesting of SAS to make multiple selections, nose much about it, I hope you can help me and understand (because I do not write very good English).
SASS mixin:
#mixin data($x) {
$sel: &;
$collector: ();
#for $i from 1 through length($sel) {
$s: nth($sel, $i);
$last: nth($s, -1);
#if str-slice($last, -1) == "]" {
// if is just the bare attribute with no value, $offset will be -1, otherwise it will be -2
$offset: -1;
$current-x: $x;
#if str-slice($last, -2) == '"]' {
// this attribute already has a value, so we need to adjust the offset
$offset: -2;
} #else {
// no attribute value, so add the equals and quotes
$current-x: '="' + $x + '"';
}
$last: str-slice($last, 1, $offset - 1) + $current-x + str-slice($last, $offset);
$collector: append($collector, set-nth($s, -1, $last), comma);
} #else {
// following line will append $x to your non-attribute selector
$collector: append($collector, selector-append($s, $x), comma);
// the following line will not change your non-attribute selector at all
//$collector: append($collector, $s, comma);
}
}
#at-root #{$collector} {
#content;
}
}
SASS:
[data-content] {
#include data("content") {
background: black;
}
}
Output:
[data-content="content"] {
background: black;
}
The problem is I can not nest more than one item, for example does not work:
[data-content] {
#include data("content", "menu") {
background: black;
}
}
Output:
[data-content="content"],
[data-content="menu"] {
background: black;
}
Any way to solve?
You can always do something like this if you don't mind having to specify your selectors instead of passing them through as variables.
[data-content="content"], [data-content="menu"]{
#include data() {
background: black;
}
}

Sass: alternative variable in less [duplicate]

I'm trying to loop through a list of values in Sass and use interpolation of the current key to dynamically output class names that utilize #include and #extend, respectively.
Here is a pen showing the problem, simplified. http://codepen.io/ghepting/pen/vBmLy
As you can see in the markup, I have tried including the "_" inside of the interpolated string as well as outside of it. Is there something I'm missing to work around this limitation of how Sass supports interpolation?
(Note: the OP's pen has disappeared. This is not the original code found in the pen, but a rough approximation of the problem)
$error-light: red;
$error-dark: darken(red, 10%);
$success-light: green;
$success-dark: darken(green, 10%);
$dialogs: error, success;
#each $d in $dialogs {
.#{$d} {
background: $#{$d}-light;
}
}
Interpolation doesn't work on mixins or variables at this point in time. You'll have to come up with a different way to achieve your goal.
As of Sass 3.3, you can use mappings for this purpose for variables:
$dialogs:
( error:
( light: red
, dark: darken(red, 10%)
)
, success:
( light: green
, dark: darken(green, 10%)
)
);
#each $name, $colors in $dialogs {
.#{$name} {
color: map-get($colors, dark);
}
}
And for functions:
#function green() {
#return lighten(green, 10%);
}
#function red() {
#return lighten(red, 10%);
}
#mixin my-bg($function-name) {
background: call($function-name);
}
.foo {
#include my-bg('red');
}
Alternative workaround (for a particular use case):
https://sass-lang.com/documentation/at-rules/mixin#passing-arbitrary-arguments
💡 Fun fact:
Because an argument list keeps track of both positional and keyword arguments, you use it to pass both at once to another mixin. That makes it super easy to define an alias for a mixin!
If you are interested in mixin interpolation because you have a group of mixins, like this:
//_mixins.scss
#mixin text-style-1($args...){ //sass here }
#mixin text-style-2($args...){ //sass here }
#mixin text-style-3($args...){ //sass here }
//_text.scss
.text-style-1 {
#include text-style-1;
}
.text-style-1-contrast {
#include text-style-1($contrast: true);
}
.text-style-2 {
#include text-style-2;
}
.text-style-2-contrast {
#include text-style-2($contrast: true);
}
We can take advantage of passing arbitrary arguments and use an alias for the group:
//_mixins.scss
#mixin text-style-1($args...){ //sass here }
#mixin text-style-2($args...){ //sass here }
#mixin text-style-3($args...){ //sass here }
#mixin text($mixin, $args...) {
#if $mixin == 'style-1' { #include text-style-1($args...); }
#else if $mixin == 'style-2' { #include text-style-2($args...); }
#else if $mixin == 'style-3' { #include text-style-3($args...); }
}
//_text.scss
$text-styles: 'style-1', 'style-2', 'style-3';
#each $style in $text-styles {
.text-#{$style} {
#include text($style);
}
.text-#{$style}-contrast {
#include text($style, $contrast: true);
}
}
Ran into this issue of trying to include an interpolated variable inside a mixin and was able to resolve it with placeholders:
%color-scheme-dark-bg-1 { background-color: #4e5163; }
%color-scheme-dark-color-1 { color: #4e5163 !important; }
%color-scheme-light-bg-1 { background-color: #c7c8ce; }
%color-scheme-dark-bg-2 { background-color: #fd6839; }
%color-scheme-dark-color-2 { color: #fd6839 !important; }
%color-scheme-light-bg-2 { background-color: #fecfc1; }
.card_color {
#mixin CardColorScheme($arg: 1) {
.borderPercent {
#extend %color-scheme-dark-bg-#{$arg};
}
.border {
#extend %color-scheme-light-bg-#{$arg};
}
ul li:before {
#extend %color-scheme-dark-color-#{$arg};
}
.percent {
#extend %color-scheme-dark-color-#{$arg};
}
.heading {
#extend %color-scheme-dark-color-#{$arg};
}
}
&--scheme {
&-1 {
#include CardColorScheme(1);
}
&-2 {
#include CardColorScheme(2);
}
}
}
Hat tip to: https://krasimirtsonev.com/blog/article/SASS-interpolation-in-a-name-of-variable-nest-variables-within-variables

Is it possible to take the variable's name in SASS?

I tried to make the following code look fancier with an #each or a #for loop (without any usable result).
.btn[data-btn-color="black"] {
#include colored-btn($black);
}
.btn[data-btn-color="blue"] {
#include colored-btn($blue);
}
.btn[data-btn-color="red"] {
#include colored-btn($red);
}
// ... and more colors ...
My current approach is to take value from the variable to use it as the value for the data-btn-color attribute and put that snippet into an #each loop.
Something like
#each $color in ($black, $blue) {
#include colored-btn($color);
}
which compiles into:
.btn[data-btn-color="black"] {
background-color: #000; // $black
}
.btn[data-btn-color="blue"] {
background-color: #00f; // $blue
}
Is there any function, which allows me to do such a thing?
You were so close! You don't want the () around what you want to go through in #each. I think Sass would just see what you have as one list item with a two item list inside.
Here is what I think you're trying to do:
$red: #f00;
$blue: #00f;
$black: #000;
$colors: red $red, blue $blue, black $black;
#mixin colored-button($background-color: #000){
background-color: $background-color;
}
#each $color in $colors {
$name: nth($color, 1);
$hex: nth($color, 2);
.btn[data-btn-color="#{$name}"]{
#include colored-button($hex);
}
}
Which would result in:
.btn[data-btn-color="red"] {
background-color: red; }
.btn[data-btn-color="blue"] {
background-color: blue; }
.btn[data-btn-color="black"] {
background-color: black; }

Resources