I have a custom mixin in SASS to generate a table and it takes simply some arguments for generating the colors and little details. This is the beginning:
#mixin generate-table($background, $foreground, $border: 3px, $background-hover: null, $foreground-hover: null) {
#debug "Background: #{$background}";
#debug "Foreground: #{$foreground}";
$background-hover: hover($background) !default;
$foreground-hover: hover($foreground) !default;
The issue is that I always get an error with my hover-color function that color is NULL, even if I pass normal values to it (used debugging to see if they contain stuff or not). This is the function:
#function hover($color, $ratio: 15%) {
#if (lightness($color) > 50%) {
#return darken($color, $ratio);
} #else {
#return lighten($color, $ratio);
}
}
It gives me this error: Error: $color: null is not a color for `lightness' which doesn't make sense at all.
Now, as I was trying stuff out I figured that when I pass the values directly to the function (within the mixin) it works. So what is going on/what can I do to fix this issue?
EDIT: How I call the mixin:
table {
#include generate-table($background: #FFF, $foreground: #000);
I also tried using the colors as strings like this:
table {
#include generate-table($background: '#FFF', $foreground: '#000');
and of course without direct assiciation like thas as well:
table {
#include generate-table(#FFF, #000);
which all had the same result.
change the statements to these,it should work
#function hover($color, $ratio: 15%) {
#if (lightness($color) > 50%) {
#return darken($color, $ratio);
} #else {
#return lighten($color, $ratio);
}
}
#mixin generate-table($background, $foreground, $border: 3px, $background-hover: null, $foreground-hover: null) {
$background-hover:hover($color:$background)!default;
$foreground-hover:hover($color:$foreground) !default;
}
table{
#include generate-table($background:#FFFFFF, $foreground:#000000);
}
Hope this helps
Related
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);
}
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;
}
I'm trying to make an if statement for scss key and value.
for example:
$directionLang: rtl;
#function rightOrLeft($directionLang) {
#if $directionLang == rtl {
#return 'left: 10px';
}
#else {
#return 'margin-left: 230px';
}
}
.arrow-icon {
#{rightOrLeft($directionLang)};
}
The problem is inside the .arrow-icon.
I get an error:
Error: property "#{rightOrLeft($directionLang)}" must be followed by a
':'.
Any suggestions?
Thanks!!
In that case, you should use SASS/SCSS #mixin instead of #function.
#function in SASS/SCSS should be used to provide/calculate value e.g.
.arrow-icon {
margin-left: functionName(args);
}
#mixin is more powerful but also fits to your case when you want to get whole different css rules.
Example snippet with your code:
HTML
<span class="arrow-icon">←</span>
SCSS
$directionLang: rtl;
//$directionLang: ltr; //uncomment for test
#mixin rightOrLeft($directionLang) {
#if $directionLang == "rtl" {
left: 10px;
}
#else {
margin-left: 230px;
}
}
.arrow-icon {
#include rightOrLeft($directionLang);
}
Live example
The functions are used to Get a value and you have to call them as value for your properties.
In your case you have to use mixin and include them in your class:
https://sass-lang.com/documentation/at-rules/mixin
LESS has a great little feature called Space that allows mixins to append rules to existing properties. Really useful for transform() mixins, because you can append many transform rules to the same property, just by calling the mixin multiple times, eg.
Example:
.scale() {
transform+_: scale(2);
}
.rotate() {
transform+_: rotate(15deg);
}
.myclass {
.scale();
.rotate();
}
Outputs:
.myclass {
transform: scale(2) rotate(15deg);
}
I'm trying to get into SASS, but I don't understand how to achieve this with the available syntax. Whatever I do, the output only ever applies one of the transformations, not both. What is the best way to achieve this behaviour using SASS alone?
You can use variable arguments in a mixin like so:
#mixin transform($transforms...) {
transform: $transforms;
}
.myclass {
#include transform(scale(0.5) rotate(30deg));
}
this will output:
.myclass {
transform: scale(0.5) rotate(30deg);
}
You can see a working example here:
http://codepen.io/sonnyprince/pen/RaMzgb
A little more info:
Sometimes it makes sense for a mixin or function to take an unknown
number of arguments. For example, a mixin for creating box shadows
might take any number of shadows as arguments. For these situations,
Sass supports “variable arguments,” which are arguments at the end of
a mixin or function declaration that take all leftover arguments and
package them up as a list. These arguments look just like normal
arguments, but are followed by ....
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_arguments
Sass does not offer such a feature.
You can get reasonably close by using global variables. However, every single mixin you use, including ones provided by a 3rd party, will have to be modified to work this way.
// the setup
$append-property-vals: (); // global variable
$old-append-property-vals: (); // global variable
#mixin append-property($key, $val, $separator: comma) {
$old-val: map-get($append-property-vals, $key);
#if $old-val {
$append-property-vals: map-merge($append-property-vals, ($key: append($old-val, $val, $separator))) !global;
} #else {
$append-property-vals: map-merge($append-property-vals, ($key: $val)) !global;
}
}
#mixin append-properties {
// cache the original value
$old-append-property-vals: $append-property-vals !global;
#content;
// write out the saved up properties
#each $key, $val in $append-property-vals {
#{$key}: $val;
}
// restore the original value
$append-property-vals: $old-append-property-vals !global;
}
// modify the OP's provided mixins to work
#mixin scale {
// if the vals should be comma delimited, write `comma` instead of `space` for the 3rd argument
#include append-property(transform, scale(2), space);
}
#mixin rotate {
#include append-property(transform, rotate(15deg), space);
}
// call the mixins
.myclass {
#include append-properties {
#include scale;
#include rotate;
}
}
Output:
.myclass {
transform: scale(2) rotate(15deg);
}
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