I'm trying to create a sass mixin that will take an undetermined number of items in a list as arguments in a mixin.
The end goal is to have a mixin that can be used to style the colors of different values for a progress bar (i.e. red when the bar has a low value). Here's what I came up with for the mixin:
#mixin progress-value($value..., $color...) {
progress[value="#{$value}"] {
color: #{$color};
&::-webkit-progress-value { background-color: #{$color}; }
&::-moz-progress-bar { background-color: #{$color}; }
}
}
// Calling the mixin
#include progress-value("0.25, #de2b23", "0.5, #FF8330", "0.75, #8A9F4A", "1, #14BB64");
I know this is a list I'm using with the include, but I'm not sure how to break that list up and pass it to each argument, or if this is even the best way to go.
I could create a simpler version of the mixin and call it for each value being styled, but that didn't seem very DRY.
You can try something like this:
#mixin make_progress($val,$col){
progress[value="#{$val}"] {
color: #{$col};
&::-webkit-progress-value { background-color: #{$col}; }
&::-moz-progress-bar { background-color: #{$col}; }
}
}
#mixin progress-value($value-color...) {
#each $progress in $value-color {
#include make_progress(nth($progress,1),nth($progress,2));
}
}
// Calling the mixin
#include progress-value(0.25 #de2b23);
// and with a multideimensional list
#include progress-value(0.5 #FF8330, 0.75 #8A9F4A, 1 #14BB64);
This will work now if you pass the parameters as a comma separated list of space separated pairs - value/color, like I did in the above example, or in some other way make clear that your list of parameters is multidimensional - like including each passed pair in parentheses:
// with a single parameter
#include progress-value((0.25, #de2b23));
// or with multiple parameters
#include progress-value((0.5, #FF8330), (0.75, #8A9F4A), (1, #14BB64));
I also made a separate mixin make_progress, for a better overview, and in case you would want to call it in some other instance outside the loop, but you could easily leave that inside the loop.
DEMO
Related
Is it posible to invoke a mixin doing something like this?
#mixin font-mixin ($mixinName, $color) {
#include #{$mixinName};
color: $color;
}
I mean I tried that, it does not work but,is there any way to do something like this? I want to dynamically call a mixin providing the name of the mixin I need to invoke as parameter of another mixin since it will help reducing a lot of code in my project
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;
}
}
Is there any way to customize the variables in SASS?
For example:
.m-b-{$number} {
margin-bottom: $number;
}
If I give class="m-b-50" to an element, it should take margin-bottom 50. I just want to know if it is possible with SASS.
Yes it is possible with the help of variable interpolation or variable substitution which uses #{} for variable substitution in SASS and mixins which is a block of code just like function.
Interpolation is the process of evaluating an expression or a string containing one or more variables, yielding a result in which the variables are replaced with their corresponding values.
Simple example of interpolation and set values to the css property in SASS:
$number:60;
$n: 20px;
.m-b-#{$number}{
margin-bottom: #{$number}px;
margin-top: $n;
}
To create customize class names, will use mixins:
#mixin margin-class($side, $number) {
$firstLetter: str-slice($side, 0, 1);
.m-#{$firstLetter}-#{$number}{
margin-#{$side}: #{$number}px;
}
}
$margins: (10, 20);
$sides: ("top", "right", "bottom", "left");
#mixin generate-margin(){
#each $margin in $margins{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
#include generate-margin();
Here, generate-margin() will get executed which will call margin-class() for each $margins and $sides, and will generate the below CSS classes:
.m-t-10 {
margin-top: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-t-20 {
margin-top: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
That's the one way when you want only for specific values, but if you want to create margin class for 0-20, you can loop thru 0 to 20 as shown below:
#mixin generate-margin(){
#for $margin from 1 through 20{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
For anyone else facing this issue, here is how one can achieve this:-
#for $i from 1 through 10 {
.mb-#{$i} {
margin-bottom: #{$i}rem;
}
}
The answer is: no it is not possible. SASS is just a language to pre-generate CSS for you. There is no on-demand, dynamic creation of classes triggered by the contents of your HTML markup. When it comes time for the browser to render your HTML and apply your specified classes, it is still just using CSS. I.e. if you assign class="m-b-50" to an element, the class .m-b-50 must already be explicitly defined somewhere. As noted in the other answers, SASS can make it easier to generate a bunch of pre-defined classes but you must know which values you want to support up front.
Now, you could generate classes for some very large, all-inclusive range like -1000 to 1000 to effectively support all values you might ever try to use and it would seem to do what you wanted, but you would be forcing your users to download a larger CSS file with, most likely, a large percentage of it being unused CSS which is wasteful and can be inconsiderate in a world of paid & limited data plans.
I have around 196 images of country flags and each image is named after its two letter country code. I generated a sprite with all these images using gulp.spritesmith npm package. It also generates a css file (in my case, a scss file) to reference them in our project.
For simplicity, the following generated code is because of the two images.
/*generated code*/
$ad-name: 'ad';
$ad-x: 0px;
$ad-y: 14px;
$ad-offset-x: 0px;
$ad-offset-y: -14px;
$ad-width: 19px;
$ad-height: 14px;
$ad-total-width: 19px;
$ad-total-height: 1932px;
$ad-image: 'locate-sprite.png';
$ad: (0px, 14px, 0px, -14px, 19px, 14px, 19px, 1932px, 'locate-sprite.png', 'ad', );
$ae-name: 'ae';
$ae-x: 0px;
$ae-y: 966px;
$ae-offset-x: 0px;
$ae-offset-y: -966px;
$ae-width: 19px;
$ae-height: 14px;
$ae-total-width: 19px;
$ae-total-height: 1932px;
$ae-image: 'locate-sprite.png';
$ae: (0px, 966px, 0px, -966px, 19px, 14px, 19px, 1932px, 'locate-sprite.png', 'ae', );
$spritesheet-sprites: ($ad, $ae,);
$spritesheet: (19px, 1932px, 'locate-sprite.png', $spritesheet-sprites, );
#mixin sprite-width($sprite) {
width: nth($sprite, 5);
}
#mixin sprite-height($sprite) {
height: nth($sprite, 6);
}
#mixin sprite-position($sprite) {
$sprite-offset-x: nth($sprite, 3);
$sprite-offset-y: nth($sprite, 4);
background-position: $sprite-offset-x $sprite-offset-y;
}
#mixin sprite-image($sprite) {
$sprite-image: nth($sprite, 9);
background-image: url(#{$sprite-image});
}
#mixin sprite($sprite) {
#include sprite-image($sprite);
#include sprite-position($sprite);
#include sprite-width($sprite);
#include sprite-height($sprite);
}
In my project, I created a mixin which overrides the above generated sprite mixin. That is as follows:
#mixin blah($sprite){
&:before{
display: inline-block;
content: "\00a0";
position: relative;
vertical-align: middle;
top: -2px;
#include sprite-image($sprite);
#include sprite-position($sprite);
#include sprite-width($sprite);
#include sprite-height($sprite);
}
}
When I use this mixin in my project, I simply import the scss file generated and include this mixin. The following is the implementation:
#import "_gen.sprite";
$country-list: ad ae;
#each $current-country in $country-list {
.imageflag-#{$current-country}{
#include blah($ad); // This works
#include blah($current-country); //This doesn't because, a string is passed instead of a variable.
margin-right: 10px;
}
}
But, In the above implementation, I want to pass the given list values (ad and ae) as variables (i.e., $ad and $ae) during the each loop.
You might say, why can't you just have the country list as following:
$country-list: $ad $ae;
I can't do this because, $ad and $ae values are already generated by the plugin in the scss file and are equivalent to a value which can't be passed into the mixin as it will throw an error again. Expecting a variable not a string.
So, I thought of using interpolation. So, I did the following:
$dollar: '$';
$country-list: ad ae;
#each $current-country in $country-list {
.imageflag-#{$current-country}{
#include blah(#{$dollar}#{$current-country}); //expected $ad and $ae but throws an error.
margin-right: 10px;
}
}
But the interpolation is not working on the dollar. I am unable to append dollar to a string variable.
So, is there any other way to interpolate or append a dollar symbol to a variable or a string to achieve the required output.
Any feedback or suggestions are greatly appreciated.
Thank you.
Firstly, the sprite mixin here doesn't accept strings. They accept variables. Hence, I tried to create a whole new list from the existing list by appending the $ to every list item. Even then, the end result was a string but not a variable.
After a while, I looked at the types of the variables generated with a simple amend. It was a string. And the variable which was passed to the mixin was a list. As there is only one single item in a string [Note: In SASS, a single string is implicitly a list], whenever I try to do a nth($country, 9), it throws an index out of bounds error.
So, final conclusion from that experiment is just to pass a list variable to the mixin.
Now, for every country list variable we pass to the mixin, there should be a specific class generated with it's properties. How do we do this? We need to loop through the list of lists.
Hence, I overwrote the mixin already generated by the spritesmith as follows:
#mixin sprites-loop($sprites) {
#each $sprite in $sprites {
$sprite-name: nth($sprite, 10);
.iss-flag-#{$sprite-name} {
#include sprite($sprite);
margin-right: 5px;
}
}
}
Usage:
#include sprites-loop($spritesheet-sprites);
$spritesheet-sprites is a list of lists which is generated by the gulp.spritesmith in the css file.
By, simply using this one single line of #include, I didn't even had to iterate through the country list.
Coming to the interpolation, #{} is used to coerce the variables into strings. Even though when I using it thinking, an output would be a resemblance of the variable, it's still a string. Hence, the #{} doesn't work in this scenario.
Thanks a lot for everyone's comments and I hope this helps others who try to incorporate gulp.spritesmith in their projects.
Cheers,
SZ
I am writing a mixin that I want to output a CSS counter. Here's my goal:
.selector:nth-child(3n+1) {
color: green;
}
That makes every three elements, starting with the first, green.
I would like my mixin to take that first number, in my example 3 and output the CSS. Something like this:
#mixin counter($number) {
&:nth-child($number * n + 1) {
color: green;
}
}
Is this possible in Sass at this point?
To use a variable's value in the selector, you should use the interpolation syntax #{} and append the n to it like in the below code block.
#mixin counter($number) {
&:nth-child(#{$number}n + 1) {
color: green;
}
}
.selector{
#include counter(3);
}
The above code compiles successfully and produces the required output when tested using the online compiler at sassmeister.com.
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');