Can you use a loop on null arguments in a sass mixin? - validation

so I've been trying to figure this out but maybe I'm just beating my head against the wall. I'm trying to make a sass mixin that uses null arguments so that the argument will not be included unless a value is specifically assigned to the output like so:
// main mixin
#mixin fontSettings(
$font-size: null,
$line-height: null,
)
{
// if null, get the default value
#if not $font-size {
$font-size: -get-font-defaults(font-size);
}
// if null get, default value
#if not $line-height {
$line-height: -get-font-defaults(line-height);
}
// calculate font stuff
$font-size: $font-size * 1rem;
$line-height: $line-height * 1rem;
//output
font-size: $font-size;
line-height: $line-height;
}
// map merge mixin
#mixin -set-font-defaults() {
$-font-defaults: map-merge($-font-defaults, keywords($defaults)) !global;
}
// function to get map values
#function -get-font-defaults($key){
#return map-get($-font-defaults, $key);
}
// default settings config map
$-font-defaults: (
'font-size': 1.2,
'line-height': 2,
);
and usage would be
p {
//output sets fontsize to 12px and line height to 20px
#include fontSettings;
}
my question is - is there any way to shorten this without having to do an #if validation for each argument? like using a #each loop to loop through the arguments? I keep getting invalid null operations unless I do a validation for each argument separately. if there's no way to shorten this with a loop, then I'll accept that since I'm a noob but if there is a way to shorten this I would appreciate some advise on how to do so. I plan on adding more arguments to the mixing, this is just a test with the two. thanks in advance for any help.

There is an easier way, you need to leverage the optional parameters of the mixin in your favor. By using optional parameters, not defining them allows the default values set in the declaration to kick in like the example below.
#mixin fontSettings($font-size: 1.2, $line-height: 2) {
font-size: $font-size * 1rem;
line-height: $line-height * 1rem;
}

Related

SASS/SCSS, how to access a property/method in a dynamic way from a partial file?

Let's say for instance we have the next sass partial file:
//_colors.scss
$foo: red;
And we "use" it on another file:
//test.scss
#use './colors'
.test{
color: colors.$foo;
}
All good, but what if I would like to use/get the value in a dynamic way within a mixin? something like:
//test.scss
#use './colors'
#mixin getColor($type){
color: colors[$type]; //JavaScript example, * don't actually work *.
or
color: #{colors.{$type}; * don't work neither *
//The above returns `color: colors.foo` instead of `color: red` on compilation.
or
color: colors.#{$type}; * doesn't work neither *
}
.test{
#include getColor(foo);
}
Is it possible? thanks for the help!
For a color, I really much prefer a function so it can be used on any property (color, background-color, border, box-shadow...)
I usually declare a string equivalent to variable names, then define them inside a map. Finally this map is accessible via a dedicated function.
Something like
//_colors.scss
#use 'sass:map';
$favoriteRed: "favoriteRed";
$favoriteYellow: "favoriteYellow";
$favoriteBlue: "favoriteBlue";
$MyColors: (
$favoriteRed: #c00,
favoriteYellow: #fc0,
$favoriteBlue: #0cf
);
#function my-color($tone: $favoriteRed) {
#if not map.has-key($MyColors, $tone) {
#error "unknown `#{$tone}` in MyColors.";
}
#else {
#return map.get($MyColors, $tone);
}
}
This _colors.scss generates no code at all, it can be imported anywhere at no cost.
Then, in a specific style file:
//test.scss
#use './colors' as *;
//inside a mixin
#mixin special-hue-component($tone){
div.foo {
span.bar {
border-color: my-color($tone);
}
}
}
//or directly
.foobartest {
color: my-color($favoriteBlue);
}

Pass variable name as parameter in SCSS [duplicate]

I have a very simple mixin which looks like this:
#mixin global( $variable-name ) {
font-size: #{$variable-name}-font-size;
}
I have previously defined variable $input-font-size and pass it into the mixin in the following format
#include global( input );
Problem is that the sass is not converting it and browser returns :
font-size:input-font-size
How should I write my mixin to actually return the value from $input-font-size please?
Thank you for your advice in advance!
You can't create a dynamic variables in sass.
'#{}' means it will convert whatever attribute to its plain css form, it won't be treated as a variable it will be treated as a text.
What you can do is create a map for the list of properties and call them inside the mixin.
$input-font-size: 16px;
$textarea-font-size: 14px;
$var-map: (
input: $input-font-size,
textarea: $textarea-font-size,
);
#mixin global( $variable-name ) {
font-size: map-get($var-map, $variable-name);
}
body {
#include global( input );
}
or if you dont want to create the map then you can simply pass the variable name in the mixin
#mixin sec( $variable-name ) {
font-size: $variable-name;
}
.text-area {
#include sec( $textarea-font-size );
}
Sample pen
https://codepen.io/srajagop/pen/aWedNM

#function v/s #mixin in Sass-lang. Which one to use?

After searching a lot in difference between #function and #mixin I ended up here.
Is there any advantage of using #mixin over #funcion or vice versa. In what context they'll be different, how to use them interchangeably, please come up with examples.
Functions are useful specifically because they return values. Mixins are nothing like functions--they usually just provide valuable blocks of code.
Usually, there are cases where you might have to use both.
For example, if I wanted to create a long-shadow with SASS, I would call a function like so:
#function makelongshadow($color) {
$val: 0px 0px $color;
#for $i from 1 through 200 {
$val: #{$val}, #{$i}px #{$i}px #{$color};
}
#return $val;
}
Which would then be called with this mixin:
#mixin longshadow($color) {
text-shadow: makelongshadow($color);
}
Which provides us with the actual code.
That gets included in the element:
h1 {
#include longshadow(darken($color, 5% ));
}
#function is useful when you want to reuse it on different CSS properties.
Example, You have dynamic values that you want to use on both height and min-height, then using #function is the one you would use:
#function dynamic-height($height, $padding) {
#return $height + $padding;
}
.foo {
min-height: dynamic-height(300px, 30px);
}
.bar {
height: dynamic-height(300px, 30px);
}
But if you want to reuse it with same CSS properties, then you would use a #mixin:
#mixin dynamic-height($height, $padding) {
min-height: $height;
padding: $padding;
}
.foo {
#include #dynamic-height(300px, 30px);
}
think of #mixin as it is just a #function that returns a block of style rules.
also, you can use placeholders (like %selector) and #extend

Giving default arguments to mixins when there is more than one variable

I am trying to make a #mixin where I want to give a default value to the first variable but not to the second variable.
#mixin myCoolFont($color:purple, $size) {
color: $color;
font-size: $size;
}
Now when I call that mixin I do something like this:
p {
#include myCoolFont(white, 63px)
}
When compiling it outputs an error that says $size must come before any other arguments.
Please, tell me, what am I doing wrong?
You just do as error says, it will work, simple change order of arguments so that last argument(s) are those with default values. In your case:
#mixin myCoolFont($size, $color:purple) {
color: $color;
font-size: $size;
}
p {
#include myCoolFont(63px, white)
}

How to use !important keyword in a mixin?

I can't get Sass to output the !important keyword with a mixin, I've tried:
#include font-size($font-size-sml) !important;
And:
#include font-size($font-size-sml !important);
It always throws an error.
EDIT
I ended up with this which works fine:
#mixin font-size($font-size, $sledge-hammer: "") {
font-size: $font-size #{$sledge-hammer};
font-size: ($font-size / $base-font-size)+rem #{$sledge-hammer};
line-height: ceil($font-size / $base-line-height) * ($base-line-height / $font-size);
}
You can't add !important to whole mixin in SASS (It is possible in LESS I think) like you're trying to do in first example.
Second example works for me (you can pass !important with a parameter), I mean, if you use $font-size-sml directly as a property value it works, so maybe check your syntax.
But if it's really not working for you, you can do something with flag, set a important_flag as a mixin parameter and then use if-else statement in mixin. Something like this:
#mixin large-text($prop, $is_imp: false) {
#if $is_imp == false {
font-size: $prop;
} #else {
font-size: $prop !important;
}
}
Maybe it's not a glamorous way to do it, but it works ;-)
You can use the if(condition, when-true, when-false) function.
So the code will be simplest and compact:
#mixin large-text($size, $isImportant: false) {
font-size: $size if($isImportant, !important, null);
}

Resources