Pass sass variable inside loop as a selector without value - sass

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

Related

SASS: Add pseudo-class to grandparent ampersand

This SASS code...
#mixin test
{
#at-root #{selector-replace(&, '.class1', '.class1:nth-child(odd)')}
{
color:red;
}
}
.class1
{
.class2
{
#include test;
}
}
...compiles to:
.class1:nth-child(odd) .class2
{
color: red;
}
Is this possible when not using selector-replace (because I don't know how class1 is called)?
I just want to add a nth-child selector to the grandparent.
I am only allowed to change the mixin, not the original code.
Ok, this will do the trick:
#mixin test
{
// Find the first selector
$parent : nth(nth(&, 1), 1);
// Defines a list for the rest of the selectors
$rest : ();
// For each selector of &, starting from the second
#for $i from 2 through length(nth(&, 1)) {
// Adds the selector to the list of the "rest of the selectors"
$rest: append($rest, nth(nth(&, 1), $i));
}
// Adds the selector at root
#at-root #{$parent}:nth-child(odd) #{$rest} {
color: red;
}
}
.class1
{
.class2
{
#include test;
}
}
This compiles to:
.class1:nth-child(odd) .class2 {
color: red;
}
Hope it helps!

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

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

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

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