I am working with a mixin called button with the following signature:
#mixin button($expand: false,
$background: $button-background,
$background-hover: $button-background-hover,
$color: $button-color, $style: solid)
I would like to create a mixin to wrap this one called sized-button, that takes an additional parameter to indicate the size of the button based on a $button-sizes map.
The map looks like this:
$button-sizes: (
tiny: 0.6rem,
small: 0.75rem,
default: 0.9rem,
large: 1.25rem,
);
I was hoping to use variable arguments so that I don't have to redefine the arguments for the button mixin. For example, this was my first attempt:
#mixin sized-button($size: default, $buttonArgs...) {
#if (map-has-key($button-sizes, $size)) {
#include button($buttonArgs);
font-size: map-get($button-sizes, $size);
}
#else {
#warn "Invalid button size";
}
}
However, doing this does not seem to work. For example, if I pass one of the button arguments to the sized-button mixin, I get an error:
#include sized-button($size: large, $expand: true);
Mixin sized-button has no parameter named $expand
Is it possible to do what I'm trying to do?
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);
}
How do I conditionally apply a mixin if --my-custom-var is present? For example:
.test {
#if var(--my-custom-var) {
#include someExampleMixin()
}
#if var(--another-custom-var) {
#include someExampleMixin()
}
}
I don't care what the value of the --my-custom-var is but just want to check its existence.
Sass has introduced the variable-exists() function already in alpha. Be aware that Sass can only check for Sass variables. Therefor if you'd really want to use CSS variables you need to define the content of your CSS variable inside a Sass variable, for example $sassVar: /* content */; --cssVar: $sassVar;. Be also aware that the #if statement must be inside a #mixin or a #function to work. I posted a working example below, but here is aslo my Codepen Example since Stack doesn't compile Sass.
Note:
I used "null" inside my $var which basically expresses that there
is no content within this variable, you can pass whatever you
want it won't affect the outcome unless you remove or change the actual
variable.
You can use multiple #if statements which I commented out in this example, but there should always follow an #else statement.
$var: null;
:root {
--someVar: $var;
}
#mixin checkForVariable {
#if variable-exists(var){
body {
background-color: red;
}
}
// #if variable-exists() {
// ...
// }
// #if variable-exists() {
// ...
// }
#else {
body {
background-color: blue;
}
}
}
#include checkForVariable;
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
In a programming language like javascript, even though I don't make any a "returned" result in the end, but it still returns a value, "undefined".
In case of Sass, I know that #mixin does not "return" anything like #function.
However, it "actually" returns nothing at all?
If it returns something actually (something like "undefined" or "void"), then what it returns?
I'm not sure I get the question but...
Mixins lets you create reusable CSS declarations (CSS content) 'called' using #include.
Unlike functions you can't assign mixins to variables as they do not produce a return value (throws an error).
#function fs($value){
#return $value;
}
#mixin fs($value){
font-size: $value;
...
}
.class { // returns value
font-size: fs(16px); // => 16px;
}
.class { // generates properties and values
#include fs(16px); // => font-size: 16px; ...
}
$var: fs(16px); // will work (function)
$var: #include fs(16px); // won't work (mixin)
Try to read this article and this one, you have some answer :
"They obtain full CSS rules where properties that are dynamic will be passed into arguments"
So when you #include a #mixin in your code, it will return CSS rules.
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);
}