How to Define a Dynamic Mixin or Function Name in Sass

How to define a dynamic mixin or function name in SASS?

Variable interpolation in @mixins does not appear to be supported currently.

The SASS documentation calls this #{} interpolation and describes it like this:

Interpolation: #{}

You can also use SassScript variables in selectors and property names using #{} interpolation syntax:

$name: foo;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}

Per the documentation, interpolation of variable names only appears to be supported for selectors and property names, and not for @mixins. If you'd like that feature, you may want to file an Issue for it, although this one may already be tracking what you're describing.

Edit:
Are you sure you need to use a @mixin to accomplish the kind of styling you're talking about? Could you just use a selector with interpolation? For example, would this work:

$event-icons: fair, concert, art-show, conference, dance-show, film, party, festival, theatre, launch
@for $i from 1 to length($event-icons)
$event-icon: nth($event-icons, $i)
.event-icon-#{$event-icon}
background-position: -($event-icon-width * $i) 0

Sass Interpolation of Mixin, Function, and Variable names

Interpolation doesn't work on mixins or variables at this point in time. You'll have to come up with a different way to achieve your goal.

As of Sass 3.3, you can use mappings for this purpose for variables:

$dialogs:
( error:
( light: red
, dark: darken(red, 10%)
)
, success:
( light: green
, dark: darken(green, 10%)
)
);

@each $name, $colors in $dialogs {
.#{$name} {
color: map-get($colors, dark);
}
}

And for functions:

@function green() {
@return lighten(green, 10%);
}

@function red() {
@return lighten(red, 10%);
}

@mixin my-bg($function-name) {
background: call($function-name);
}

.foo {
@include my-bg('red');
}

Generate SCSS mixins based off certain values?

You are not allowed to generate mixins dynamically in Sassbut CSS classes can be created dynamically.

reference - Read this thread


Method 1

In first method, I have used "key": value pair to define a Sass map and from that we can easily set the name and value you prefer to mention as class name and it's property value.

$spacing-extra-small: 0.25rem;
$spacing-small: 0.5rem;
$spacing-medium: 1rem;
$spacing-large: 2rem;
$spacing-extra-large: 4rem;

$paddings: (
"pr0": 0,
"pr1": $spacing-extra-small,
"pr2": $spacing-small,
"pr3": $spacing-medium,
"pr4": $spacing-large,
"pr5": $spacing-extra-large
);

@each $padding, $value in $paddings {
.#{$padding} {
padding-right: #{$value};
}
}

Note - Instead of variables I have added in $padding map, you can apply absolute pixel values directly as shown below.

$paddings: (
"pr0": 0,
"pr1": 0.25rem,
"pr2": 0.5rem,
"pr3": 1rem,
"pr4": 2rem,
"pr5": 4rem
);

Method 2

Here in second method, I have used only values to define a Sass map and it's class name is generated dynamically using index of map value.

Not like in JavaScript array object, initial Index of map is started from number 1. So I have perform a calculation by subtracting 1 value from the current index of map value and match to your individual mixin names mentioned above.

$spacing-extra-small: 0.25rem;
$spacing-small: 0.5rem;
$spacing-medium: 1rem;
$spacing-large: 2rem;
$spacing-extra-large: 4rem;

$paddings: ( 0, $spacing-extra-small, $spacing-small, $spacing-medium, $spacing-large, $spacing-extra-large );

@each $padding in $paddings {
.pr#{index(($paddings), ($padding)) - 1} {
padding-right: #{$padding};
}
}

Method 3

Here I have made it bit simplify by reducing to 1 mixin and 5 variables. This mixin can be included inside any css class with a preferred padding variable.

HTML

<div class="box">
set a padding value here
</div>

SCSS

$spacing-extra-small: 0.25rem;
$spacing-small: 0.5rem;
$spacing-medium: 1rem;
$spacing-large: 2rem;
$spacing-extra-large: 4rem;

$pr0 : 0px;
$pr1 : $spacing-extra-small;
$pr2 : $spacing-small;
$pr3 : $spacing-medium;
$pr4 : $spacing-large;
$pr5 : $spacing-extra-large;

@mixin set-padding ($pr){
@if($pr){
padding-right: $pr;
}
}

/*Apply set-padding mixin to .box class*/

.box {
background-color: #ccc;
@include set-padding($pr2);
}

All three methods will be helpful to solve your problem. Thanks :)

Creating or referencing variables dynamically in Sass

Sass does not allow variables to be created or accessed dynamically. However, you can use lists for similar behavior.

scss:

$list: 20px 30px 40px;    
@mixin get-from-list($index) {
width: nth($list, $index);
}

$item-number: 2;
#smth {
@include get-from-list($item-number);
}

css generated:

#smth {
width: 30px;
}
  • http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#lists
  • http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#list-functions

Is it posible to dynamically invoke a mixin in Sass?

You can't do this directly, but you can work around it. You could do kind of a switch statement:

@mixin breakpoint($mixin) {
@if $mixin == mixin1 {
@include mixin1;
}

@if $mixin == mixin2 {
@include mixin2;
}
}

How to dynamically change variable in mixin function in sass?

You need to specifically loop over the mixin a certain number of times, else how does the sass know how many nth-child selectors to create?

I've removed your custom functions to make my example cleaner, but you can see that I have created a loop around the call to the mixin, which runs 9 times, passing the index to the function each time:

@mixin child($n) {
&:nth-child(#{$n}){
right: (50px * $n) !important;
}
}

li {
position: absolute;
top: -30px;
right: 0;

@for $i from 1 through 9 {
@include child($i);
}
}

How to use @each in mix includes in Sass?

The problem is here: @include media-#{$media-key} {...} 'cause you can't use interpolation in @mixin. See this post, it is very clear about this issue: How to define a dynamic mixin or function name in SASS?

So, we have to use another way. A solution could be create a general mixin and work with its arguments. Something like this:

@mixin general-media($width){
@media only screen and (min-width: $width) {
@content;
}
}

After that, I choose to add your width values in $media-map map and use them with that mixin (I tried to use your code):

$media-map: (
smartphone-xs: ( type: 'xs', width: $xs-width ),
smartphone-sm: ( type: 'sm', width: $sm-width ),
tablet-md: ( type: 'md', width: $md-width ),
tablet-lg: ( type: 'lg', width: $lg-width ),
desktop: ( type: 'xl', width: $xl-width )
);

And this is your loop with some changes:

@each $media-key, $media-type in $media-map {
@include general-media(map-get($media-type, 'width')) {
@each $display-key, $display-type in $container-map {
.container-#{map-get($media-type, 'type' )}-#{$display-key} {
display: map-get($map: $display-type, $key: display );
}
}
}
}

This is all code in action:

/* MEDIA_QUERIES.SCSS */

$xs-width: 320px;
$sm-width: 576px;
$md-width: 768px;
$lg-width: 992px;
$xl-width: 1200px;

@mixin general-media($width){
@media only screen and (min-width: $width) {
@content;
}
}

/* SIXBASE-GRID.SCSS */

$container-map: (
flex: ( display: flex ),
inline: ( display: inline )
);

$flex-direction-map: (
row: ( flex-direction: row ),
row-reverse: ( flex-direction: row-reverse ),
column: ( flex-direction: column ),
column-reverse: ( flex-direction: column-reverse )
);

$media-map: (
smartphone-xs: ( type: 'xs', width: $xs-width ),
smartphone-sm: ( type: 'sm', width: $sm-width ),
tablet-md: ( type: 'md', width: $md-width ),
tablet-lg: ( type: 'lg', width: $lg-width ),
desktop: ( type: 'xl', width: $xl-width )
);

* {
&::before,
&::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}

@each $media-key, $media-type in $media-map {
@include general-media(map-get($media-type, 'width')) {
@each $display-key, $display-type in $container-map {
.container-#{map-get($media-type, 'type' )}-#{$display-key} {
display: map-get($map: $display-type, $key: display );
}
}
}
}

The output:

*::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}

@media only screen and (min-width: 320px) {
.container-xs-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.container-xs-inline {
display: inline;
}
}

@media only screen and (min-width: 576px) {
.container-sm-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.container-sm-inline {
display: inline;
}
}

@media only screen and (min-width: 768px) {
.container-md-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.container-md-inline {
display: inline;
}
}

@media only screen and (min-width: 992px) {
.container-lg-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.container-lg-inline {
display: inline;
}
}

@media only screen and (min-width: 1200px) {
.container-xl-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.container-xl-inline {
display: inline;
}
}


Related Topics



Leave a reply



Submit