Pass function or mixin by reference in SASS - css

Is there any way to pass a function or a mixin by reference to another function or mixin in SASS, and then call the referenced function or mixin?
For example:
#function foo($value) {
#return $value;
}
#mixin bob($fn: null) {
a {
b: $fn(c); // is there a way to call a referenced function here?
}
}
#include bob(foo); // is there any way I can pass the function "foo" here?

Functions and mixins are not first-class in Sass, meaning you can't pass them around as arguments like you can with variables.
Sass 3.2 and older
The closest you can get is with the #content directive (Sass 3.2+).
#mixin foo {
a {
#content;
}
}
#include bob {
b: foo(c); // this replaces `#content` in the foo mixin
}
The only caveat is that the #content can't see what's inside your mixin. In other words, if c was only defined inside the bob mixin, it essentially wouldn't exist because it isn't considered in scope.
Sass 3.3 and newer
Starting with 3.3, you can use the call() function, but it is only for use with functions, not mixins. This requires passing string containing the name of the function as the first argument.
#function foo($value) {
#return $value;
}
#mixin bob($fn: null) {
a {
b: call($fn, c);
}
}
#include bob('foo');

Related

Generating sassdoc's from dynamic definitions like #each

What I am trying to do
I have a number of #extends that are build dynamically via a list in my SCSS project.
/// #type Map
$sizes-list: ( 'none', 'xs', 'sm', 'md', 'lg', 'xl' );
#each $size in $sizes-list {
/// #access public
%padding-#{$size} {
#include padding($size);
}
/// #access public
%padding-top-#{$size} {
#include padding-top($size);
}
/// #access public
%padding-right-#{$size} {
#include padding-top($size);
}
/// #access public
%padding-bottom-#{$size} {
#include padding-top($size);
}
/// #access public
%padding-left-#{$size} {
#include padding-top($size);
}
}
What I expected to happen
I thought I would generate 5 padding definitions generated for each iteration of the #each (30 definitions in total).
What actually happened
However I have noticed that these definitions that are rendered as part of a #each loop do not generate iteration of the loop when building the docs. What actually happen was only 5 definitions were documented without the $size value. I guess this is because the doc generator reads the file statically:
My questions
Is it possible to generate documentation for dynamically generated definitions like this? or,
Is there an alternative path I could take to document in this use case?

Gaps in sass css output

I'm writing some sass to generate a set of icons based on a series of parameters. I have a function that analyses a set of variables and then returns a 'scenario' variable which in turn is used to filter the information taken from the nested map where everything is stored.
The code which retreives the information from the nested map is as follows:
#each $key-lv0, $lv0 in $icon-config {
#if($key-lv0 == $scenario) {
.icon{
#each $key-lv1, $lv1 in $lv0 {
#if type-of($lv1) != "map" {
#{$key-lv1}: $lv1;
}
#each $key-lv2, $lv2 in $lv1 {
#if type-of($lv2) != "map" {
.#{$key-lv1} {
#{$key-lv2}: $lv2;
}
}
#each $key-lv3, $lv3 in $lv2 {
#if($key-lv2 == "hover") {
.#{$key-lv1}:#{$key-lv2} {
#{$key-lv3}: $lv3;
}
} #else {
.#{$key-lv1} #{$key-lv2} {
#{$key-lv3}: $lv3;
}
}
}
}
}
}
}
}
... and this produces something along these lines:
.icon .icon-header {
background-color: #00a9f0;
}
.icon .icon-header:hover {
border-color: #040100;
}
... etc ...
... which is fine - repeated statements aside whicvh I'll deal with later.
The problem is the cap between ".icon" and ".icon-header". These classes will all be used in a single element and for the css to be interpretted correctly it needs to generate something like this:
.icon.icon-header {
background-color: #00a9f0;
}
.icon.icon-header:hover {
border-color: #040100;
}
I've tried bringing ".icon" down like so:
#if type-of($lv1) != "map" {
.icon#{$key-lv1}: $lv1;
}
and removing it from the top but sass rejects this with the following error:
Error: Properties are only allowed within rules, directives, mixin includes, or other properties.
It seems such a minor thing but it's nagging me and I can't seem to find an answer.

SASS functions check if value is map returns error

I want to check if the value provides to a function is a map or not.
#function func($props...) {
#if(is-map($props)) {
#return 'something';
}
#else {}
}
h1 {
color: func((color: red));
}
I'm getting the error:
(color: red) isn't a valid CSS value.
What am I doing wrong?
I personally never heard about any native Sass function called is-map. If you want to check the type of something, use the type-of function, so for example, checking for type-of($props) == map would solve your problem in this case.
#function func($props...) {
#if(type-of($props) == map) {
#return 'something';
}
#else {}
}
Because function returns map. Not a color as expected. Use map-get to get the access to properties values.
#return map-get($props, color);
And you have the variable argument. To get the first of arguments use nth($props, 1).
Sassmeister demo.
Update
If parameters in map are dynamic you can use this mixin instead of function.
#mixin print($declarations) {
#if $declarations {
#each $property, $value in $declarations {
#{$property}: $value
}
} #else {
#error "mixin print: $declarations is not specified";
}
}
Mixin sassmeister demo.

setting a Polymer CSS mixin in a nested mixin

Polymer supports CSS mixins, which can be set like this:
scope1 {
--mixin1: {
attr1: val1;
};
}
and applied like this:
scope2 {
#apply --mixin1; /* sets attr1 */
}
Is there a way to set the value of a mixin from inside a mixin? I tried this, but it doesn't work:
scope1 {
--mixin1: {
attr1: val1;
--mixin2: {
attr2: val2;
};
};
}
scope2 {
#apply --mixin1; /* sets attr1 */
#apply --mixin2; /* is attr2 set? */
}
A real case of why this would be useful: Say you have an app that uses several custom components based on paper-listbox and paper-item. You would like to style all of the lists in your custom components with a different spacing and font. You could set the --paper-listbox and --paper-item mixins in a global scope. But that would affect every occurrence of the two elements relying on defaults. Instead in your custom components, you would simply #apply --custom-list; and set that mixin in a global :root {--custom-list: {/*set list style, set item style*/}; }.
Workaround: Instead of nesting mixins, refactor into selectors:
scope1 {
--mixin1: {
attr1: val1;
};
}
scope1 scope2 {
--mixin2: {
attr2: val2;
};
}
In the use case above, instead of --custom-list with nesting:
:root custom-list {
--paper-input-container: {/*set list style*/};
--paper-item: {/*set item style*/};
}

LESS CSS define variables when another variable is true

I've used guard expressions elsewhere in my CSS to achieve IF statements in LESS, however these don't work for me when trying to declare variables like so...
#neutral: false;
#warm: true;
when (#neutral = true) {
#green: #91C95B;
#red: #F15647;
etc...
}
when (#warm = true) {
#green: #91AD3C;
#red: #BF2A23;
etc...
}
This is an example of how I would like to be able to use that variable
h1 {
color:#green;
}
This is how I would expect it to compile down to CSS
h1 {
color: #91AD3C;
}
Is this possible with LESS or would I need to modify my code to use mixin guards?
You can use Guarded Mixins like this :
#neutral: false;
#warm: true;
.color() when (#neutral) {
#green: #91C95B;
}
.color() when (#warm) {
#green: #91AD3C;
}
.color();
h1 {
color:#green;
}

Resources