Sass recursion mixin - recursion

I would like to know, if in sass is allowed to write a recursion mixin like this Less example :
.size($val) when ($val < 200 ) {
width: $val;
height: $val;
.size($val + 50px);
}
I'm trying something like this, but i don't have any output
#mixin test($val) {
width: $val;
height: $val;
#for $i from 1 through 5 {
#include test($val);
}
}
.block {
#include test(5);
}

Unlike your LESS example, your Sass mixin does not have a way to break out of the loop.
#mixin test($val) {
width: $val;
height: $val;
#if $val > 0 {
#include test($val - 1);
}
}

In sass, you can create recursive functions, but your example is wrong.
Looking at your code, I see that you are in infinite loop, because $var variable is not used in the #for iterator. You only iterates from 1 to 5 infinitely.
The recursive approach here is the same as another languages, you have to use the parameter to call the function again, and when this variable is the same as 0 (for example), returns a value ... its value is used to the last call of the function to set the new value ... and again the first step.
Here are examples of Sass recursive functions:
https://gist.github.com/paramburu/9613303
http://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/#section-3
Hope it helps.
Regards.

Related

SCSS function with for loop inside not working

I'm trying to write a function that creates a grid layout based on multiple arrangements (as opposed to just the 12 column grid) in Sass (scss syntax). The for loop inside works properly on its own, but when I wrap it in a function it no longer works. I'm new to using Sass functions, so maybe I'm messing up the syntax? Or is this just not possible? I'm just trying to avoid having to write a new for loop for each layout I want to achieve. Thanks in advance.
#function create-grid($num-cols) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: percentage($col / $num-cols);
}
}
}
Sass functions return a value.
If you want to generate css programatically like this, you want to use a mixin.
https://www.sitepoint.com/sass-basics-the-mixin-directive/
#mixin grid($num-cols) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: percentage($col / $num-cols);
}
}
}
#include grid(12)
For anyone who stumbles across this post, the final product is:
#mixin create-grid($num-cols, $col-gutter) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: calc(
((100% - ((#{$num-cols} - 1) * #{$col-gutter})) / #{$num-cols}) *
#{$col} +
((#{$col} - 1) * #{$col-gutter})
);
}
}
}
Thanks a lot to #HorusKol for introducing me to the world of mixins!

SASS | #for and #each loop printing values from a list issues

I'm using sass to print values from a list with a #each and #for loop but when ever i try to print one by one and compile that gathered values in the css it takes all the values in the list even using a nth('', $n) function.
I'm using the following code and gulp as my task runner.
// SASS CODE
$btn-default: darken(#DDD, 5%);
$btn-alert: red;
$btn-class: "btn-default", "btn-alert";
$btn-color: $btn-default, $btn-alert;
#each $btn in $btn-class {
.#{unquote($btn)} {
#for $i from 1 through length($btn-color) {
background-color: nth($btn-color, $i);
}
}
}
/* CSS CODE : OUTPUT */
.btn-default {
background-color: #d0d0d0;
background-color: red; }
.btn-alert {
background-color: #d0d0d0;
background-color: red; }
I'm a little bit stuck in a project i tried from different ways in the sass and when i don't get the values it just return an error instead.
Instead of introducing an inner #for loop that will inevitably render two properties in each ruleset, try using a single loop:
#for $i from 1 through length($btn-class) {
#{nth($btn-class, $i)} {
background-color: nth($btn-color, $i)
}
}
In the long run however, I would recommend using maps instead. This way you have a clear association between class names and colors which will avoid potential mistakes in larger lists of elements:
$buttons: (
btn-default: $btn-default,
btn-alert: $btn-alert
);
#each $class, $color in $buttons {
.#{$class} {
background-color: $color;
}
}

Less to Sass: How to translate less guard when (default()) to sass

I am trying to convert some mixins from less to sass.
I am not very skilled in either of these languages, so I'm not sure at all if I'm doing it right.
The original Less mixins:
.for(#i, #n) {.-each(#i)}
.for(#n) when (isnumber(#n)) {.for(1, #n)}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
.for(#array) when (default()) {.for-impl_(length(#array))}
.for-impl_(#i) when (#i > 1) {.for-impl_((#i - 1))}
.for-impl_(#i) when (#i > 0) {.-each(extract(#array, #i))}
I have identified three main issues:
1) how do I translate guards in sass? Is it correct to gather mixins with the same number of arguments in one single mixin and write conditional blocks inside it?
2) how does when(default()) works? I've been trying to find a good explanation in the documentation but couldn't find any.
3) is there a function in sass which is equivalent to less extract?
Thank you very much!
First thing first, the mixin that you've given in question is one that was created by seven-phases-max to imitate the for and for-each loops because Less doesn't have built-in functions for them. Unlike Less, Sass already has built-in #for and #each directives to perform looping and so I'd recommend you to not spend time on converting these Less mixins to Sass.
Below are Sass samples for a for and a for-each loop:
For:
.column {
#for $i from 1 through 5 { /* execute the loop 5 times */
&-#{$i} { /* create .column-1 to .column-5 using selector interpolation */
width: 20%;
left: (($i - 1) / 5 * 100%);
}
}
}
For Each:
$country-code-list: US, IN, FR, SG; /* list for iteration */
.flag {
#each $country-code in $country-code-list { /* iterate for each value in the list */
&-#{$country-code} { /* create .flag-US, .flag-IN etc using selector interpolation */
background-image: url(http://yoursite.com/#{$country-code}.png);
}
}
}
You can try out the above samples # SassMeister.com and see the compiled output.
Now coming to your questions,
1) how do I translate guards in sass? Is it correct to gather mixins with the same number of arguments in one single mixin and write conditional blocks inside it?
The Less guards would translate to #if (conditional statements) in Sass. Below is an example:
#mixin list-style($val) {
#if ($val == i) { list-style-type: lower-roman; }
#else if ($val == I) { list-style-type: upper-roman; }
#else { list-style-type: decimal };
}
#demo { #include list-style(2); }
The second part of your question is probably opinion based as each one would have their own way of doing things. I'd personally prefer grouping them inside one mixin and writing conditional blocks.
2) how does when(default()) works?
The default() guard in Less is like the typical else or default: (in switch-case) statements. The mixin with this as guard gets called only when no other mixin with the same name and the same no. of arguments is matched.
For example, have a look at the below code. Here when the value passed as argument to the mixin is anything other than i or I neither of the first two mixin guards are matched and so those mixins do not get executed. In such a case, the third one (the default()) will get executed and set the list style type as decimal.
.mixin-list-style(#val) when (#val = i) { list-style-type: lower-roman; }
.mixin-list-style(#val) when (#val = I) { list-style-type: upper-roman; }
.mixin-list-style(#val) when (default()) { list-style-type: decimal; }
#demo {
.mixin-list-style(1); /* this will result in list-style-type: decimal */
}
3) is there a function in sass which is equivalent to less extract?
Yes, there is a nth() function in Sass which is equivalent to the Less extract() function. Below is a sample:
$country-code-list: US, IN, FR, SG;
.flag-FR {
$country-code: nth($country-code-list, 3); /* extract 3rd item from country-code-list */
background-image: url(http://yoursite.com/#{$country-code}.png);
}
Note: You don't need this with #each loop because Sass automatically extracts the item and assigns to the variable for each iteration.

How to solve and compensate the sub-pixel rounding issue?

I try to iron out the sub-pixel rounding error for quite a while now, but so far i've failed miserably again and again. I try to accomplish that endeavour with the help of Sass and Susy. The mixin i've used in my last try i got from the Susy issue tracker on Github (i've used space, columns as well as push on the margin-left/right property like suggested there):
#mixin isolate($location, $context: $columns, $dir: 'ltr') {
#if $dir == 'ltr' {
margin-right: -100%;
margin-left: space($location, $context);
}
#else if $dir == 'rtl' {
margin-left: -100%;
margin-right: space($location, $context);
}
}
My Scss looks like the following:
.imggrid{
#include with-grid-settings($gutter: 0.1%){
$y:2;
li{
#include span-columns(2,12);
#for $x from 1 through 30
{
&:nth-child(#{$x}){
#include isolate($y,12);
$y: $y + 2;
#if $y > 12{
$y: 2;
}
}
}
#include nth-omega(6n);
}
}
}
First i've created a custom gutter for the image grid. Then i've defined a variable y to iterate up in the steps of two to be able to call the isolate mixin (isolate(2,12) isolate (4,12) etc). For values larger than 12 the value gets reset to two within the for loop in the end. Then i span a column for each li walking through the 30 images. Each time calling the iterating isolate mixin. After the for loop i've appended the #include nth-omega(6n); to get a new line after each sixth element.
But somehow it doesn't work at all. The first four rows are missing the first image and on the last row the first five elements are missing. Any ideas and suggestions are appreciated. Thanks Ralf
UPDATE: I adjusted these mixins to be 1-indexed rather than 0-indexed. I think that is the more common.
You're close, but the math isn't quite right. It get's a bit complex to keep everything straight, so I wrote up a mixin to take care of it for you. I also adjusted the isolate mixin so that it uses the existing Susy $from-direction variable:
#mixin isolate($location, $context: $total-columns, $from: $from-direction) {
$to: opposite-position($from);
margin-#{$to}: -100%;
margin-#{$from}: space($location - 1, $context);
}
#mixin isolate-list($columns, $context: $total-columns, $from: $from-direction) {
$position: 1;
$line: floor($context / $columns);
#include span-columns($columns, $context, $from: $from);
#for $item from 1 through $line {
$nth: '#{$line}n + #{$item}';
&:nth-child(#{$nth}) {
#include isolate($position, $context, $from);
#if $position == 1 { clear: $from; }
$position: $position + $columns;
#if $position > $context { $position: 1; }
}
}
}
Use it just like span-columns, width & context. That's all there is too it:
.imggrid{
li{
#include isolate-list(4,12);
}
}
That should work with any width, in any context, for any number of list items. Cheers!

How to combine 2 SASS functions as they do almost the same thing just using a different calculation?

I was wondering if someonce could advise me on how I could combine the SASS functions below, basically they do the same thing but one is used specifically for IE so having 2 is a waste. I've tried adding if..else statements with an additional parameter like if ( $property == 'ie' ) but with no success.
Functions:
// To use width, margin, padding...
#function percentageWidth( $elementWidth, $containerWidth ){
#return $elementWidth/$containerWidth * 100%;
}
// To use *width ie6/7 workarounds
#function iePercentageWidth( $elementWidth, $containerWidth ){
#return $elementWidth/$containerWidth - 0.5/$containerWidth * 100%;
}
Current CSS:
width: percentageWidth( 214,945 );
*width: iePercentageWidth( 214,945 );
I would also like to use this function on things like:
margin: percentageWidth( 23,945 );
Here's a general mixin you can use to create a pair of properties:
#mixin ieprop($name, $elementWidth, $containerWidth) {
#{$name}: percentageWidth($elementWidth, $containerWidth);
*#{$name}: iePercentageWidth($elementWidth, $containerWidth);
}
You pass the property name as the first parameter, like so:
.cls {
#include ieprop('width', 214, 945);
#include ieprop('margin', 23, 945);
}
Is this what you need?
#mixin twoWidths($x, $y){
width: percentageWidth($x, $y);
*width: iePercentageWidth($x, $y);
}
Then in your stylesheet you'd call it like:
#include twoWidths(500,700);

Resources