How to call a dynamic mixin in SASS? - sass

It is possible to call dynamic mixin name from within class?
Something like:
#mixin mixin_test {
color: red;
position: relative;
}
$mixin_name: mixin_test;
.test {
#include $mixin_name;
}
Because I have a List structure of class names and mixin names, and I need to #include them (key is class name, value is the name of mixin).
$helper-declarations--mixins: (
full-parent: _full-parent,
full-of-parent: _full-of-parent
);
It is necessary for me, because I dynamically building the classes like normal:
// .full-parent > .full-of-parent
#include generate_breakpoints_helpers($helper-declarations--mixins);
or next time with some rules for some breakpoints, like:
#include generate_breakpoints_helpers($helper-declarations--mixins, (
'medium': (
'full-parent',
),
'large': (
'full-parent',
'full-of-parent'
),
'no-tablet': (
'full-parent'
)
));

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

Creating class names from source map keys

I'm getting an error saying that ('background-color': #333) is not valid css. I'm trying to get the theme name to append to .body-- for each theme (there's only one them for now). The error looks to me like it's trying to add the contents of theme-name to .body-- instead of just the title of it.
$themes: (
'dark': (
'background-color': #333
),
);
#each $theme-name in $themes {
.body--#{$theme-name} {
& .container {
background-color: map-get($theme-name, background-color);
}
}
}
I'm trying to get:
.body--dark {}
.body--dark .container {background-color: #333}
Thanks
The problem is in your #each statement. You are getting just the map value, instead of the key and the value.
$themes: (
'dark': (
'background-color': #333
),
);
// get the key and the value in two separate variables: first variable is the key, second one the value
#each $theme-name, $theme-config in $themes {
.body--#{$theme-name} {
.container {
background-color: map-get($theme-config, 'background-color');
}
}
}
As a note, you don’t need the & for nested selectors. That’s just the default behavior. Use the & only for pseudo classes, concatenate class names, aggregated class names and some other strange selector usages. Check my answer about this note on this other question.

Can I generate variables inside a mixin to use it outside, without !global?

I'm creating a theme system and want to reuse some vars depending on the scope, like:
.black-friday {
#include generate-theme('black', 'yellow');
}
.light-theme {
#include generate-theme('blue', 'pink');
}
.dark-theme {
#include generate-theme('black', 'orange');
}
I don't want to declare a variable with !global since it would overwrite the previous var, and would not exist in a closed scope.
I want to use it in a scope, so that I can change the theme easily with only one class changing.
This was my attempt:
$colors: (
pink: (
light: lighten(#F91364, 20),
normal: #F91364,
dark: darken(#F91364, 20)
),
blue: (
light: lighten(#3E61FF, 20),
normal: #3E61FF,
dark: darken(#3E61FF, 20)
),
);
$contrasts: (
pink: (
light: #eee,
normal: #fff,
dark: #fff
),
blue: (
light: #eee,
normal: #fff,
dark: #fff
)
)
#function get-color($color, $force: 'normal') {
$chosenColor: map-get($colors, $color);
#return map-get($chosenColor, $force);
}
#function get-contrast($color, $force: 'normal') {
$chosenColor: map-get($contrasts, $color);
#return map-get($chosenColor, $force);
}
#mixin generate-theme($primary, $accent) {
$primary-color: get-color($primary);
$primary-light-color: get-color($primary, 'light');
$primary-dark-color: get-color($primary, 'dark');
$primary-contrast-color: get-contrast($primary);
$primary-light-contrast-color: get-contrast($primary, 'light');
$primary-dark-contrast-color: get-contrast($primary, 'dark');
$accent-color: get-color($accent);
$accent-light-color: get-color($accent, 'light');
$accent-dark-color: get-color($accent, 'dark');
$accent-contrast-color: get-contrast($accent);
$accent-light-contrast-color: get-contrast($accent, 'light');
$accent-dark-contrast-color: get-contrast($accent, 'dark');
}
If I am getting all that right, than the answer is: No, not possible, those variables are local and the only way to make them global is, well, to use !global, to make them global. I another Option:
A #function get-variables($primary, $accent) which returns a map containing all the variables as key, value pairs, which can be called where ever needed.
Or you need to change your code in a way that #mixin generate-theme itself includes all other mixins with appropriate parameters.

Sass configuration map with default values

I am creating css using SASS and would like to make it possible for another developer to create a custom css by changing sass variables. This works fine when I in my base file use a single variable like this:
$text-color: #000 !default;
To test the override I create a new project where I first declare an override for the variable and then import the "base" sass file.
$text-color: #0074b;
#import "base-file";
But I would also like to use maps for configuration but then I do not get the override to work. How should I use configuration maps that can be overriden?
$colors: (text-color: #000, icon-color: #ccc );
Adding !default after #000 gives me a compilation error: expected ")", was "!default,")
Adding !default after the ) gives no error but the variables does not get overwritten either.
Any ideas on what I am doing wrong?
I don't think the functionality you want exists in standard Sass. I built this function though that does what you're asking for:
//A function for filling in a map variable with default values
#function defaultTo($mapVariable: (), $defaultMap){
//if it's a map, treat each setting in the map seperately
#if (type-of($defaultMap) == 'map' ){
$finalParams: $mapVariable;
// We iterate over each property of the defaultMap
#each $key, $value in $defaultMap {
// If the variable map does not have the associative key
#if (not map-has-key($mapVariable, $key)) {
// add it to finalParams
$finalParams: map-merge($finalParams, ($key : $value));
}
}
#return $finalParams;
//Throw an error message if not a map
} #else {
#error 'The defaultTo function only works for Sass maps';
}
}
Usage:
$map: defaultTo($map, (
key1 : value1,
key2 : value2
));
Then if you have a mixin for something, you can do this sort of thing:
#mixin someMixin($settings: ()){
$settings: defaultTo($settings, (
background: white,
text: black
);
background: map-get($settings, background);
color: map-get($settings, text);
}
.element {
#include someMixin((text: blue));
}
Outputted CSS:
.element { background: white; color: blue; }
So you would use it like this based on what you said in the question:
$colors: defaultTo($colors, (
text-color: #000,
icon-color: #ccc,
));
Bootstrap has solved this issue as:
$grays: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$grays: map-merge(
(
"100": $gray-100,
"200": $gray-200,
"300": $gray-300,
"400": $gray-400,
"500": $gray-500,
"600": $gray-600,
"700": $gray-700,
"800": $gray-800,
"900": $gray-900
),
$grays
);
https://github.com/twbs/bootstrap/blob/v4.1.3/scss/_variables.scss#L23

Resources