SASS : compare variable name within #each loop with a string - sass

I'm trying to find a way of comparing the variable name e.g. $topLeft within the #each loop with a string which would be for instance 'topLeft' - an example would be:
#mixin getCorner($topLeft:false, $topRight:false, $bottomRight:false, $bottomLeft:false) {
#each $corner in $topLeft, $topRight, $bottomRight, $bottomLeft {
#if #{$corner} == topLeft {
border-top-left-radius: $corner;
}
}
}
The above obviously doesn't work, but is there a way of doing it in Sass?

If you use the name top-left instead of topLeft, you can reduce the amount of code you have to write.
Here I have a list which does not do EXACTLY what you want, but you can easily use this to go ahead and do the comparison you want to do.
$corners: (top-left, top-right, bottom-left, bottom-right);
#mixin getCorner($cornerName, $cornerVal) {
$max: length($corners);
#for $i from 1 through $max {
$temp: nth($corners, $i);
#if ($temp == $cornerName) {
border-#{$temp}-radius: $cornerVal;
}
}
}
body {
#include getCorner(top-left, 2px);
}

When you assign a variable, all the interpreter knows is the value it contains, not what its name is. So when you're looping over your values, $corner is getting set to one of the values in the list. It will never be topLeft unless you pass that as the value for the $topLeft argument, which is why your #if statement never evaluates to true.
If you use a default value of null instead of false, you can simplify a lot:
#mixin getCorner($topLeft: null, $topRight: null, $bottomRight: null, $bottomLeft: null) {
border-top-left-radius: $topLeft;
border-top-right-radius: $topRight;
border-bottom-right-radius: $bottomRight;
border-bottom-left-radius: $bottomLeft;
}
.foo {
#include getCorner($topLeft: 50%, $bottomRight: 50%);
}
Output:
.foo {
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
}

Related

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

Using a sass variable in an #each loop

I'm trying to create a little overview for all the colors we use in our corporate identity. All our colors have been defined in _settings-colors.scss, and the only reason I need this bit of css is for the library, where the colors need to be listed.
What I have now is as follows:
$colors-brand: color-brand, color-brand-40, color-brand-60, color-brand-70;
.prfx-color {
display: block;
height: 5rem;
width: 100%;
#each $color in $colors-brand {
&--#{$color} {
background-color: #{'$'+$color};
&::after {
content: '$'+$color;
}
}
}
}
These color-brand variables are set in another file which I'm including in this scss file.
The code above outputs this:
.prfx-color {
display: block;
height: 5rem;
width: 100%;
}
.prfx-color--color-brand {
background: $color-brand;
}
.prfx-color--color-brand::after {
content: "$color-brand";
} [...etc]
What I'm after however, is this:
.prfx-color--color-brand {
background: #00ff11; // don't worry, brand is not actually this color
}
The problem I'm having is that the $color-brand variable isn't interpreted as a sass variable anymore, but is a literal value. I need the #hheexx that this variable refers to!
All the solutions I've found so far consist of using two lists, or a key-value pair. In my situation these variables have already been set once, and I want a solution where I don't want to have to manually edit the library if the colors change.
Is this at all possibe, or am I too greedy here?
And I realized I overcomplicated it. You don't need any extra functions because the #each is designed to work with maps and iterating over multiple values.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $key, $val in $colors {
&--#{$key} {
background-color: $val;
&::after { content: "$#{$key}"; }
}
}
}
You could use a map.
Here's a sassmeister playground for you.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $color in map-keys($colors) {
&--#{$color} {
background-color: map-get($colors, $color);
&::after { content: "$#{$color}"; }
}
}
}

Avoid repeat the same mixin in Sass

I have this Mixin for padding utility:
Sass code:
$padding: (
top: "top",
right: "right",
bottom: "bottom",
left: "left",
all: "all"
);
#mixin no-padding($map) {
#each $padding-side, $side in $map {
#if $side == 'all' {
& {
padding: 0 !important;
}
} #else {
&-#{$side} {
padding-#{$side}: 0 !important;
}
}
}
}
Use of it:
.u-noPadding {
#include no-padding($padding);
}
I want to use the same Mixin but now for margin, is there any solution to avoid repeating the same mixin and make a good use of best practices?
#mixin no($type,$sides:null) {
$i:0 !important;
#if $sides == null {
#{$type}:$i;
} #else {
#each $side in $sides {
#{$type}-#{$side}:$i;
}
}
}
.u-noPadding {
#include no(padding, top left etc...); // choose any side separated with a space
}
.u-noMargin {
#include no(margin); // instead of 'all', type nothing
}
Like this? Your $sides will be stored in a temporary map automatically if your second parameter is set, no need extra map for this.
About the second parameter: If you want no sides, let it empty and all sides will have 0. Similiar to your 'all' idea.. it's shorter.

Conditionally output a part of SCSS rule selector

I would like to specify an additional default shortcut class to a set of classes, similarly to that
#each $pos, $some-css-rules in ("left": ..., "right": ..., ...) {
#if $pos == "left" {
.block,
}
.block-#($pos) {
...
}
}
that would be outputted as
.block,
.block-left {
...
}
.block-right {
...
}
However, it will stumble over .block, syntax error.
.block-left cannot be replaced here with .block.left because $pos will collide with existing classes (.left, etc).
I would prefer to avoid .block { #extend .block-left } if possible, there is a considerable amount of similar rules that will gain a lot of WET code this way.
Is there a way to conditionally output a part of rule selector? How can both SCSS and CSS be kept DRY in a pattern like that?
I'm not sure if I understand the question but I achieve the output CSS based on your code. I put the #if directive inside the selector to compare with $pos variable. Here is my code:
SASS
#each $pos, $some-css-rules in ("left": red, "right": blue) {
.block-#{$pos} {
#if $pos == "left" {
#at-root .block, &{
color:$some-css-rules;
}
}
#else{
color:$some-css-rules;
}
}
}
Output
.block, .block-left {
color: red;
}
.block-right {
color: blue;
}

How do I combine a range of sass classes that are created in a loop

I am using SASS to construct a range of classes from variables passed into a mixin.
#mixin classes($key, $num) {
#for $i from 1 through $num {
[class*=#{$key}-#{$i}] {
#content
}
}
}
#include classes(grid, 8) {
width:100px;
}
It currently makes the classes like I want, but all as 8 separate classes (which are identical in #contents. Is there a way to merge them all together so I get:
[class*=grid-1],
[class*=grid-2],
....
[class*=grid-8],
{
width:100px;
}
I'm not sure if it's even possible to do this? Any pointers would be greatly appreciated.
Thanks,
Carl
I found the answer eventually
$classes: ();
#for $i from 1 through $cols {
$classes: join($classes, unquote("#{$prefix}#{$i} "), comma);
}
#{$classes} {
float: left;
margin-right: $gutterPercent;
width: $columnWidth;
}

Resources