SASS & list iteration / delimiter - sass

I've got a pattern where I create a list of lists to iterate over, as an basic example:
$carouselContent : "carousel-content-1" "buying_carousel_image_1.jpg",
"carousel-content-2" "buying_carousel_image_2.jpg";
My iteration (inside of a mixin) then looks like:
#each $carousel in $carouselContent {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
....
}
}
I just came across a page that presently only has 1 item in the carousel. I'd like to keep with the pattern, but I'm not sure how to do so. If I iterate over:
$carouselContent : "carousel-content-1" "growing_carousel_image_1.jpg";
SASS treats that as a 2 item list. I could work around that by adding an empty item to my list, then adding a check against empty string, e.g.
$carouselContent : "carousel-content-1" "growing_carousel_image_1.jpg","" "";
But that seems hacky... so I figured there has to be a way to do this that I'm unaware of.

In Sass 3.3.0, all you need to do is have a trailing comma to signify that what you have is a list with one item in it:
$carouselContent : "carousel-content-1" "buying_carousel_image_1.jpg", ;
#each $carousel in $carouselContent {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
color: red;
}
}
Generates:
.carousel-content-1 {
color: red;
}
Sass 3.3.0 is still undergoing development, but you can play with it now by upgrading to the latest edge version via gem install sass --pre. However, if you're willing to upgrade to 3.3, you may want to look at mappings instead (see: the change log)

You can use an #if directive to check if the first element of your list is also a list with type-of() (and only then use the loop). Something along these lines (I separated the block from inside your loop as a mixin):
#mixin do_car($carousel) {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
/* ... */
}
}
#if (type-of(nth($carouselContent,1)) == list) {
#each $carousel in $carouselContent {
#include do_car($carousel);
}
} #else {
#include do_car($carouselContent);
}
DEMO

If your items are numbered sequentially, you can use a for loop instead:
$carouselImages: 2;
#for $i from 1 through $carouselImages {
.#{carousel-content-#{$i}} {
background: url(buying_carousel_image_#{$i}.jpg);
}
}
Output:
.carousel-content-1 {
background: url(buying_carousel_image_1.jpg);
}
.carousel-content-2 {
background: url(buying_carousel_image_2.jpg);
}
Alternately:
//$carouselContent : "buying_carousel_image_1.jpg", "buying_carousel_image_2.jpg";
$carouselContent : "buying_carousel_image_1.jpg";
#for $i from 1 through length($carouselContent) {
.#{carousel-content-#{$i}} {
background: url(nth($carouselContent, $i));
}
}

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!

Is it possible to pass in variables such that it adds to the actual scss variables?

I have a scss code like below
.hello-station {
&-hello123 {
.site-logo__image {
#extend .logos--hello-123;
margin: 27px 0;
}
}
}
Now you can see that the word "hello" is repeated throughout... So is the number.
I will like to create a mixin or function such that the word and the number can be passed along as variables. Is that possible?
Pretty simple actually, Sass/SCSS offers a concatenation syntax:
$word: 'hello';
$number: 123;
.#{$word}-station {
&-#{$word}#{$number} {
.site-logo__image {
#extend .logos--#{$word}-#{$number};
margin: 27px 0;
}
}
}

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

Updating a SASS variable within an extended class

Is there a way to update a SASS variable within an extended class? For example I have this currently:
.menu-highlight-1 {
$child: 1;
a {
&:nth-child(#{$child}) {
color: red !important;
}
}
}
And I want to update the $child variable to the next element, so in my attempt I've extended the .menu-highlight-1 class and then made a change to the $child variable. However all this does is highlight the same item from the extended class.
.menu-highlight-2 {
#extend .menu-highlight-1;
$child: 2;
}
Is this possible somehow?
The solution for this problem using a SASS mixin. Thanks to #cimmanon and this thread: Updating variables created by lighten/darken functions by modifying the original base color afterwards
#mixin _menuHighlight($child) {
a {
&:nth-child(#{$child}) {
color: red !important;
}
}
}
.menu-highlight-1 {
#include _menuHighlight(1);
}
.menu-highlight-2 {
#include _menuHighlight(2);
}

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