Sass #if true statement not working function - function

I am trying to make a function which converts a pixel size to ems or rems. The function is as follows:
#function px2em($pixels, $fontSize: 16, $rem: false) {
#if $rem == true {
$unit: 0rem;
} #else {
$unit: 0em;
}
$ratio: 1 / $fontSize;
#return ($pixels * $ratio) + $unit;
}
When I compile this I get the following error:
error style.scss (Line 36 of _functions.scss: Undefined variable: "$unit".)
What am I doing wrong here?

SASS has block scope, variables defined in one block will only be available in that scope. So you want to use $unit outside of the if-else block, so you should declare it like this:
#function px2em($pixels, $fontSize: 16, $rem: false) {
$unit: 0em;
#if $rem == true {
$unit: 0rem;
}
...
}

In this particular instance, you may want to use the if() function, rather than an #if statement
$unit: if($rem, 0rem, 0em);
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#if-instance_method

Related

Cutting down repeating code in Sass functions

I'm working on an HTML boilerplate that uses a lot of Sass to create a maintainable and easy to use code base. Part of that are a few functions:
// Returns the scale value found at $key.
#function get-type-scale($key) {
$value: map-get($type-scale, $key);
#if $value != null {
#return $value;
}
#else {
#warn "Unfortunately, `#{$key}` is not defined in the `$type-scale` map.";
}
}
// Returns the line-height value found at $key.
#function get-line-height($key) {
$value: map-get($line-heights, $key);
#if $value != null {
#return $value;
}
#else {
#warn "Unfortunately, `#{$key}` is not defined in the `$line-heights` map.";
}
}
// etc... I have 2 more functions like this where only the $map changes.
These functions then get called in a few mixins, like this:
// Sets font-size and line-height based on the $level.
#mixin set-type($level: 0) {
font-size: get-type-scale($level);
line-height: get-line-height($level);
}
While this works just fine, I don't like the fact that I'm repeating a lot of code in the functions. I've tried writing a generic function that receives a map name as a parameter, but I cannot use interpolation in map-get().
Is there a way to make the function code more elegant and as DRY as possible?
I appreciate any insights. Cheers!
I've tried writing a generic function that receives a map name as a parameter, but I cannot use interpolation in map-get().
Unfortunately, it is not possible to create variable names from the names of other variables at all (only from its values). Furthermore, a variable only knows its value(s) and not its name which is another issue we are facing when it comes to define the variable warning messages.
I come up with a little improvement that reduces the amount of duplicated code and keeps the ease of use of the high-level function calls. Criticism of having to pass a third variable to the generic function is justified, but I simply couldn't find a clean way to avoid it.
$line-heights: (
0: 1em,
1: 2em
);
$type-scale: (
0: 1em,
1: 2em
);
// Returns the scale value found at $key.
#function get-type-scale($key) {
#return get-value-or-warn($type-scale, $key, 'type-scale');
}
// Returns the line-height value found at $key.
#function get-line-height($key) {
#return get-value-or-warn($line-heights, $key, 'line-heights');
}
#function get-value-or-warn($map, $key, $map-name) {
$value: map-get($map, $key);
#if $value != null {
#return $value;
}
#else {
#warn "Unfortunately, `#{$key}` is not defined in the `$#{$map-name}` map.";
}
}
// Sets font-size and line-height based on the $level.
#mixin set-type($level: 0) {
font-size: get-type-scale($level);
line-height: get-line-height($level);
}
You could create line-height and font-size as key/value pairs in a nested map and use interpolation to print out the property in an #each loop – like:
$map:(
0 : (line-height: 1.3, font-size: 16px),
1 : (line-height: 1.3, font-size: 17px),
2 : (line-height: 1.3, font-size: 18px),
3 : (line-height: 1.4, font-size: 19px),
4 : (line-height: 1.5, font-size: 20px)
);
#mixin set-type($level: 0){
$found: map-get($map, $level);
#if not $found { #warn 'Key `#{$level}` not found in $map!'; }
#else { #each $prop, $value in $found { #{$prop} : $value; } }
}
.class { #include set-type(1); } // line-height: 1.3; font-size: 17px;
.class { #include set-type(5); } // WARN: Key `5` not found in $map!

Random background image for each class instance

I have a sass function which returns a random url from a given set of urls as follows:
#function randomUrl(){
$images: (
"/images/watermarks/area-watermark.png",
"/images/watermarks/bar-watermark.png",
"/images/watermarks/line-watermark.png",
$img: nth($images, random(length($images)));
#return $img;
}
and i am assigning it to a class as follows:
.myClass{
background-image: url(randomUrl());
}
What i want now is to get a random image FOR EACH class instance, i.e, if i have 10 divs with class "myClass" in my HTML, i want the background images of each div to be different. My approach till now just gives me one random image which appears in all the divs everytime i compile.
The random() function does exactly what it sounds like: it generates a random number between 2 specified numbers. There is no guarantee that the numbers will be different each time the function is called because that's not how random works.
What you need is a way to shuffle your list, but there is no such function to do that in the Sass standard library. There are a couple of 3rd party libraries that do:
https://github.com/at-import/SassyLists (sl-shuffle)
https://github.com/mknadler/randomize.scss (shuffle)
The implementation in both libraries is nearly identical (this one was lifted from randomize.scss):
#function shuffle($list) {
$list-length: length($list);
#while($list-length > 0) {
$rand: random($list-length);
$temp: nth($list, $rand);
$list: set-nth($list, $rand, nth($list, $list-length));
$list: set-nth($list, $list-length, $temp);
$list-length: $list-length - 1;
}
#return $list;
}
If you're intentionally avoiding iterating over a list, you could use it like this:
#import "SassyLists";
$last-pos: 0;
$images: sl-shuffle(
"/images/watermarks/area-watermark.png"
"/images/watermarks/bar-watermark.png"
"/images/watermarks/line-watermark.png");
#function randomUrl(){
$last-pos: if($last-pos == length($images), 1, $last-pos + 1) !global;
#return nth($images, $last-pos);
}
.myClass {
background-image: url(randomUrl());
}
.myClass {
background-image: url(randomUrl());
}
.myClass {
background-image: url(randomUrl());
}
Output:
.myClass {
background-image: url("/images/watermarks/line-watermark.png");
}
.myClass {
background-image: url("/images/watermarks/area-watermark.png");
}
.myClass {
background-image: url("/images/watermarks/bar-watermark.png");
}
Though I recommend just using iteration instead and cut out the use of the function all together:
#import "SassyLists";
$images: sl-shuffle(
"/images/watermarks/area-watermark.png"
"/images/watermarks/bar-watermark.png"
"/images/watermarks/line-watermark.png");
#for $i from 1 through length($images) {
.myClass-#{$i} {
background-image: url(nth($images, $i));
}
}
http://sassmeister.com/gist/d0c65d02be52aa31f836

Can I use a variable for a function name?

I'm trying to loop through a list which automates several functions. Unfortunately the function is not evaluated.
For example:
$colors:
red,
blue,
green;
#each $color in $colors{
.color-#{$color} {
value: $color(#F15258);
}
}
(I've simplified my example code to make it easier to illustrate).
Unfortunately this just outputs the value of $key and the color #F15258.
ie:
value: red #F15258;
Can I get SASS to pass in the variable as the function name so it actually evaluates `red(#F15258)?
It should output:
value: 241;
Any thoughts?
As of Sass 3.3 you can do this using the call() function:
$colors:
'red',
'blue',
'green';
#each $color in $colors{
.color-#{$color} {
value: call($color, #F15258);
}
}
Output:
.color-red {
value: 241;
}
.color-blue {
value: 88;
}
.color-green {
value: 82;
}
Note that your variables must be a string: red is a Color while 'red' is a String.
SASS does not allow dynamic names, and that's a good thing.
To use a dynamic name, you'll have to use a template to generate your SASS prior to compiling it. See how Compass does it: https://stackoverflow.com/a/16129685/901944
This increases the complexity of your project greatly and i strongly advise against.
Instead, use a function that accepts the name as a parameter:
#function parse-color($color) {
// Do whatever you want here
}
.color-red {
color: parse-color(red);
}
Note that instead of hardcoding the second color you can have it as an argument with a default value:
#function parse-color($first-color,
$second-color: #F15258) {
// Do whatever you want here
// For example:
#return mix($first-color, $second-color);
}
$colors:
red,
blue,
green;
#each $color in $colors{
.color-#{$color} {
color: parse-color($color);
}
}
See a demo: http://sassbin.com/gist/6193779/
The variable with multiple values are called as lists in sass.
so,
you can make a list like:-
$colors: red blue green; //list of colors
#each $color in $colors{
.color-#{$color} {
color: ($color);
}
}

pass < or > operator into function as parameter?

Inside my function there is an if() statement like this:
if(passedValue < staticValue)
But I need to be able to pass a parameter dictating whether the if expression is like above or is:
if(passedValue > staticValue)
But I cant really pass < or > operator in has a parameter, so I was wondering what is the best way to do this?
Also if the language I am using matters its ActionScript 3.0
Thanks!!
Instead of passing an operator, which is impossible in AS3, why not pass a custom comparison function?
function actualFunction(passedValue:Number, compareFunction:Function) {
/* ... */
if(compareFunction(passedValue, staticValue)) {
/* ... Do something ... */
}
/* ... */
}
Then to use it:
actualFunction(6, function(x:Number, y:Number) {
return x > y;
});
or:
actualFunction(6, function(x:Number, y:Number) {
return x < y;
});
Why not make the function take a bool as an argument and perform the comparison directly when calling the function?
ExampleFunction(arg1, (passedValue > staticValue))
I don't know Actionscript, but can't you make a variable called:
bool greaterThan = true;
and if its true, do >, if its false do < ?
You're right, you can't pass operators. You can pass a variable indicating which operator to use though.
lessThan = true;
func(passedValue, lessThan);

AS3 odd or even (mal)function

please
how to fix this AS3 function ?
thank you
function dispari(numero:int):Boolean;
{
//check if the number is odd or even
if (numero % 2 == 0)
{
returns false;
}
else
{
returns true;
}
}
ERROR:
1071: Syntax error: expected a definition keyword (such as function) after attribute returns, not false.
Why do you have a semi-colon (;) at the end of your function statement? I don't do any AS3 coding but it doesn't look right, and a cursory glance at a few samples on the web don't have it there.
I suspect that may be what's causing your problem. Try this instead:
function dispari(numero:int):Boolean
{
//check if the number is odd or even
if (numero % 2 == 0)
{
return false;
}
else
{
return true;
}
}
I've also changed the return statements to match what every other piece of AS3 does to return values (thanks, #Herms, forgot to mention that :-)
Pax is correct with there answer, but you can simplify it by just returning the result:
function dispari(numero:int):Boolean
{
return (numero % 2 != 0);
}