What is the simplest way to divide a variable in SASS? - css

I would like to divide a SASS variable (which is a map value) by two, as follows:
$grid-gutter-widths: (
xs: 30px,
sm: 30px
...
);
$col-padding-xs: #{map-get($grid-gutter-widths, xs)/2}; // returns 15px
div {
padding-right: $col-padding-xs / 2; // returns 15px/2
}
Unfortunately I was expecting padding-right value to be 7.5px however it doesn't perform the division and instead returns a string with the slash in the middle. However this seems to work:
$col-padding-xs: 15px;
div {
padding-right: $col-padding-xs / 2; // returns 7.5px
}
So the map value must be the wrong type. Is there an easy way to cast it as a number so I can perform simple math on it in SASS?
Thanks for your help!

You are very close. It's a matter of the interpolation on the map-get that you have to remove. You can see the codepen for the compiled css.
$grid-gutter-widths: (
xs: 30px,
sm: 30px
);
$col-padding-xs: map-get($grid-gutter-widths, xs) / 2; // returns 15px
div {
padding-right: $col-padding-xs / 2; // returns 7.5px
}

In the new SASS version math.div takes the place instead of the divider symbol(/).
e.g. $col-padding-xs: #{map-get(math.div($grid-gutter-widths, xs), 2)};
It uses two numbers and divide them.
Source: https://sass-lang.com/documentation/breaking-changes/slash-div

Related

How to change ag-grid row group indent?

I want to change (increase/decrease) the magnitude of indent (current default is 28px) for each inner group in my Tree Data. However, I could not find any configuration options for the same in the documentation. The closest thing I could find was suppressPadding, which disables padding altogether. I tried using DOM piercing CSS but found that every level has a different CSS class (ex. ag-row-group-indent-2), which makes writing a general CSS rule in my container component difficult.
Currently, I'm overriding the style in a loop, which seems to be working
// Taken from https://github.com/ag-grid/ag-grid/blob/master/dist/styles/ag-theme-base/sass/parts/_grid-layout.scss
// support 20 levels here because row group indentation is used for tree data which can be quite deep
#for $i from 1 to 20 {
.ag-row-group-indent-#{$i} {
padding-left: $i * 8px;
}
.ag-row-level-#{$i} .ag-row-group-leaf-indent {
margin-left: 40px;
}
}
You can add a cellStyle callback that computes the padding-left value for each cell based on its current group level.
defaultColDef: {
cellStyle: (params) => {
const { level } = params.node;
const groupCell = params.value === params.node.key;
const indent = 28; // change this value to your liking
if (groupCell) {
return {
paddingLeft: (level + 1) * indent + "px"
};
}
}
},
If you use this approach, remember to suppress the initial css padding value from agGrid or both agGrid and your padding values will add up.
::ng-deep .ag-cell-wrapper.ag-row-group[class*="ag-row-group-indent"],
::ng-deep .ag-cell-wrapper.ag-row-group-leaf-indent[class*="ag-row-group-indent"] {
padding-left: 0;
}
Live Demo
If you're using SCSS and theming, you can change the indent by setting the value of row-group-indent-size, like this:
:root {
#include ag-theme-balham((
row-height: 20px,
cell-horizontal-padding: 8px,
row-group-indent-size: 18px
));
}
Reference: https://www.ag-grid.com/javascript-data-grid/global-style-customisation-variables/

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.

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.

How to calculate percentages in LESS CSS?

I would like to calculate the width of child-container (div etc) in percentages depending on the parent container with LESS CSS.
I am using the forumula by Ethan Marcotte: target / context = result.
Parent container: 620px
Child container: 140px
I am using this calculation:
div.child-container {
width: (140/620)*100%;
}
However the output is:
div.child-container {
width: 0.2258064516129;
}
I would like to move the decimal point two digits and add the %, like this:
div.child-container {
width: 22.58064516129%;
}
Any hints greatly appreciated.
According to the LESS CSS website, you need to change the order of your equation
The output is pretty much what you expect—LESS understands the difference between colors and units. If a unit is used in an operation, like in:
#var: 1px + 5;
LESS will use that unit for the final output—6px in this case.
It should be:
width: 100%*(140/620);
Maybe the percentage function didn't exist when OP was asking but for future reference I add this answer.
div.child-container {
width: percentage(140/620);
}
As for 2022, LESS 4.0 and division operator concerned:
From 4.0, No division is performed outside of parens using / operator.
#color: #222 / 2; // results in `#222 / 2`, not #111 (!)
background-color: (#FFFFFF / 16); //results is #101010
See the docs.

Create a collection (array) in SASS for use in #for loop

I'm learning SASS and I'm trying to pass a collection of data (an array) into a #mixin and process based on that. The trouble I'm having is defining the data structure to pass the values into the #mixin
Here's some pseudo code:
#mixin roundcorners($collection) {
$collectionLength = length(collection);
#for($i from 0 to $collectionLength) {
border-#{$collection[$i]}-radius: 9px;
}
}
.mybox {
#include roundcorners(['top-right','bottom-left']);
}
The desired output would be this:
.mybox {
border-top-right-radius: 9px;
border-bottom-left-radius: 9px;
}
The closest thing SASS has to an array is a list, which you can iterate with the #each directive, like so:
#mixin roundcorners($collection: (top-left, top-right, bottom-right, bottom-left), $radius: 0)
#each $corner in $collection
border-#{$corner}-radius: $radius
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#each-directive
I've used string interpolation to drop the value of the list entry into the rule itself - I'm not entirely sure that's legal, but I'm not on my dev. machine to check.
I've also used default values on the arguments, which means you can pass in a custom radius. If you do pass in any corner in the list, you'll clear the whole default list (which I think is what you want, but something to be aware of).
A different, simpler way to do this might be:
#mixin rounded($topLeft:false, $topRight:false, $bottomRight:false, $bottomRight:false)
#if $topLeft != false
border-top-left-radius: $topLeft
#if $topRight != false
border-top-right-radius: $topRight
#if $bottomRight != false
border-bottom-right-radius: $bottomRight
#if $topLeft != false
border-bottom-left-radius: $topLeft
By setting defaults, you can call this mixin like:
#include rounded(false, 9px, false, 9px)
Using 'false' instead of 0 as the default means you don't create more radius rules than you need. It also means you can override and set corners back to 0 radius if you need to.
This is how I solved it and allow you to set different radius.
#mixin border-radius($radius:5px){
#if length($radius) != 1 {
$i:1;
//covers older modzilla browsers
#each $position in (topleft, topright, bottomright, bottomright) {
-moz-border-radius-#{$position}:nth($radius, $i);
$i:$i+1;
}
//Covers webkit browsers
-webkit-border-radius:nth($radius, 1) nth($radius, 2) nth($radius, 3) nth($radius, 4);
//Standard CSS3
border-radius: nth($radius, 1) nth($radius, 2) nth($radius, 3) nth($radius, 4);
} #else {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
}
Which means you can set all the radius the same:
#include border-radius(5px)
or different like this:
#include border-radius((5px, 0, 5px, 0))
Hopefully keep your generated CSS succinct too :)
Using the code provided by #Beejamin I was able to devise the following solution after fixing some syntax issues.
#mixin roundcorners($collection: (top-left, top-right, bottom-right, bottom-left), $radius: 0) {
#each $corner in $collection {
border-#{$corner}-radius: $radius
}
}
#include roundcorners((top-right, bottom-left), 9px);
I however prefer his final solution which allows me to assign different radii to each corner.

Resources