Is there any way to append $ to a variable in sass? - css

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

Related

SASS Customize Class Names with Variables

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.

LESS Declare variables using class names?

I have a button class that sets up padding etc for an element, followed by a class that defines a background colour.
.button {
padding: 0.5em 1em;
text-transform: uppercase;
color: #fff;
&.green {
background:#green; //declared previously
}
// ... more colours
}
Is it possible to declare the #green variable as the class name? This would save me having to copy/paste the &.green block for each colour I am wanting to use.
I've not been able to find anything the docs regarding this sort of selector, but something along the lines of:
&.(green|blue|red) {
background: #{$1};
}
which would generate the following:
.button.green{background:#00ff00;}
.button.blue{background:#0000ff;}
.button.red{background:#ff0000;}
You could achieve this by having a variable with the required list of colors, a loop to create the required rules and selector interpolation like shown below.
#colors: "green","blue","orange","red","yellow"; // the list of colors required
button {
padding: 0.5em 1em;
text-transform: uppercase;
.loop-colors(#index) when (#index > 0){ // loop to generate rules for each color
.loop-colors(#index - 1); // call for the next iteration
#color: e(extract(#colors, #index)); // pick the color value from the list one by one based on current index
&.#{color} {
background:#color;
}
}
.loop-colors(length(#colors));
}
Codepen Demo
Note: As mentioned in comments, LESS PHP is quite outdated and hence some of the new features offered by LESS (with respect to loops) are not supported by it. It could be overcome by doing the work-around mentioned in this answer.
You could also adopt an approach similar to the one mentioned by seven-phases-max in this answer (2nd option). That one achieves a similar effect without using loops.

Pass sass list to mixin with multiple arguments

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

How do I implement OOCSS' spacing module in SCSS?

I've got a SCSS-based layout in which I want to use the spacing module from OOCSS.
The OOCSS module is pure CSS - ptl, for example, stands for padding-top: large, where large is a defined value (by default 20px).
I'd like to enhance it with SCSS. So far I've been able to replace the fixed values with SCSS variables, so I can change the values in one place if I want to (I don't want to):
$spacing-small: 5px;
$spacing-medium: 10px;
$spacing-large: 20px;
...
.pts,.pvs,.pas{padding-top:$spacing-small !important}
Now I'd like to be able to use ptn,pvs, etc. as mixins, so I can do this:
.client-name {
#include spacing-pvs; // this has the same padding properties as pvs
}
I'm flexible in the syntax, but that's the functionality I'd be interested in having.
The only way I can think of for doing this is manually defining every single mixin:
#mixin spacing-pvs {
padding-top: $spacing-small !important;
padding-bottom: $spacing-small !important;
}
.pvs { #include spacing-pvs; }
But there are around 56 styles/mixins. Doing each one individually like this would be pain to write and to maintain.
Is there a better way to do this in SASS/SCSS?
The most efficient mixin would be like this (you'll need a similar mixin for padding, or add an extra argument to switch between margin/padding):
#mixin marginify($t: null, $r: null, $b: null, $l: null) {
margin-top: $t;
margin-right: $r;
margin-bottom: $b;
margin-left: $l;
}
.test {
#include marginify($t: 10px, $b: 10px);
color: green;
}
Which generates this:
.test {
margin-top: 10px;
margin-bottom: 10px;
color: green;
}
The null (available in Sass 3.2+) is doing its magic here: if a variable is null, then it doesn't generate a property for it. However, you have to give up the use of !important (most people would argue that you should only use it as a last resort anyway). Reliance on this mixin is going to introduce a fair bit of bloat because the longhand form is always used over the shorthand (margin: 10px 0), so you'll need to use it responsibly or write a more powerful mixin that will generate the shorthand if appropriate.
That said, using a mixin for this purpose (adding margins) does reduce readability in your code. Before I looked at the entire source, the names made no sense. There's a lot to be said about the readability of vanilla CSS. The marginify mixin isn't really a reusable pattern like a clearfix or inline-menu mixin might be: writing a mixin isn't just about saving keystrokes.
I ended up not using mixins at all. Instead, I left the CSS rules as they were, and I used this less documented feature called #extend. Behold!:
.client-name {
#extend .pvs; // this has the same padding properties as .pvs
}

Creating variable groups in Sass

On the site I'm working on we were using Scaffold, which is a PHP-based system similar to Sass. It also can process Sass functions\files. Unfortunately that system is now abandonware, and we are looking on a way to move completely to Sass. There is one big feature with Scaffold though that I'm not finding a way to port to Sass, the variable groups.
Variable in Scaffold can be organized in groups and used with a point-separated markup. For example I would define them as:
#variables vargroup1{
variable1: ####;
variable2: ####;
variable3: ####;
variable4: ####;
}
And later use on the code as, for example.
body{ width: vargroup1.variable1; margin: vargroup1.variable2 + 10;}
This helps development a lot, since you can group together variables from a system and reading the CSS files you can easily know what to reference. I didn't find anything like that on the Sass documentation, anyone knows if it is possible? Or if there is anyway using Mixins to do this?
Thanks
I came across this somewhat clunky solution (see Chris Eppstein's reply) using zip and index. Apparently a maintainer of SASS added these built-in functions in response to a similar question.
To quote his reply:
$border-names: a, b, c;
$border-widths: 1px, 1px, 2px;
$border-styles: solid, dashed, solid;
$border-colors: red, green, blue;
$borders: zip($border-widths, $border-styles, $border-colors);
#function border-for($name) {
#return nth($borders, index($border-names, $name))
}
#each $name in $border-names {
.border-#{$name} {
border: border-for($name);
}
}
Would generate:
.border-a { border: 1px solid red; }
.border-b { border: 1px dashed green; }
.border-c { border: 2px solid blue; }
The "naming your variables" comes from the list "-names" at the top; you then use the index of a desired variable name from that variable list to get the nth value from another variable lists. zip is used to mush separate lists together, so that you can retrieve the same index from all lists at the same time. Wrapping that behavior in a function makes it easier to retrieve a set.
There is no equivalent in Sass. But I can think in two workarounds:
1) Sass lists and its related list functions.
Your code could look like the following:
$variables = 40px 30px 20px 10px;
body {width: nth($variables, 1); margin: nth($variables, 2) + 10;}
It's not the same because list indexes can't be strings, so you haven't any way to name your variables.
2) Define a custom function. Look at Function Directives section in Sass reference
#function variables($variable_name) {
#if ($variable_name == 'variable1') {
#return 40px;
} #else if ($variable_name == 'variable2') {
#return 30px;
}
}
body {width: variables('variable_1'); margin: variables('variable_2') + 10;}
This way is less intuitive and uglier but you can 'name your variables'.
You could use the scss/sass map function:
#use "sass:map";
$variables: (
"variable1": ####;
"variable2": ####;
"variable3": ####;
"variable4": ####;
}
body {
width: map.get($variables, "variable1");
margin: map.get($variables, "variable2") + 10;
}
Documentation
You can use SASS lists a it's related functions on a way similar to that:
// List order: top, bottom, left, right, width, height, ...
$Header: 10px,auto,10px,auto,100%,50px;
$Footer: auto,0px,0px,auto,100%,20px;
#function getVar($variable,$name:top){
$var_index:1;
#if $name==bottom {
$var_index:2;
} #else if $name==left {
$var_index:3;
}
// Continue de if else for each property you want.
return nth($variable,$var_index);
}
That way calling something like:
getVar($Header,left)
Should return the value of the left property for the list of Header, but changing it to getVar($Footer,top) would return the value for the top property of the "Footer Group" (Footer List of Values).
That works for the time of using the values, but a the definition, you must follow the exact order and cannot leave any empty value, the nearest to an empty value that I found is #{''} what means "Empty String with no quotes", an empty value, but is added to the CSS.

Resources