How to Assign CSS Variable Value to scss Variable or Expression - css

I'm trying to build my own tiny scalable grid in CSS / scss.
So far I found this decision:
:root {
--page-width: 1170px;
--gutter: 15px;
--columns: 12;
}
.wrapper {
max-width: var(--page-width);
margin-right: auto;
margin-left: auto;
padding-left: var(--gutter);
padding-right: var(--gutter);
}
.row {
margin-left: calc(-1 * var(--gutter));
margin-right: calc(-1 * var(--gutter));
}
.col {
display: block;
margin-left: var(--gutter);
margin-right: var(--gutter);
}
Then I tried to use scss to shorten columns classes description (which at the same time will allow me to change number of columns in one single place in whole code - in CSS Variable --columns) like this
#for $n from 1 through var(--columns) {
.col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); }
}
but it didn't work. The interesting detail is that when I change #for statement from #for $n from 1 throughvar(--columns)`` to #for $n from 1 through12 it compiles well. And there is no problem in compiling CSS-Variable inside #for body. .col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); } compiles well into needed series of classes.
If I use scss variable $columns instead of CSS variable then I'll need to import my grid.scss file into all other scss files of the project.
It's my first question on StackOverflow, so let me know if any other details are needed.

CSS and SCSS variables are two very different things (please see this pen)
To make it work you need a static variable for SCSS to compile
// static (SCSS) variables used produce CSS output
$page-width: 1170px;
$gutter : 15px
$columns: 12;
// dynamic (CSS) variables used at run-time
// note the values are interpolated
:root {
--page-width: #{$page-width};
--gutter : #{$gutter};
--columns: #{$columns};
}
// the for loop is aimed at producing CSS output
// ... why you need the static variable
#for $n from 1 through $columns {
// the content becomes CSS output
// ... why you can use dynamic variables
.col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); }
}

You need to use interpolation (eg. #{$var}) on your variable in order for Sass to treat it as a CSS property. Without it, you're just performing variable assignment.
#mixin w_fluid($property_name, $w_element, $w_parent:16) {
#{$property_name}: percentage(($w_element / $w_parent));
}

The accepted answer is no longer valid. Newer versions of SASS require interpolation to be used for variables.
Refer here for more details
$accent-color: #fbbc04;
:root {
// WRONG, will not work in recent Sass versions.
--accent-color-wrong: $accent-color;
// RIGHT, will work in all Sass versions.
--accent-color-right: #{$accent-color};
}

Related

Why I can not use sass variables to define value of css variable

I am trying to use sass variables and sass mathematical operator to define different css font-size variables.
I defined css variables like that:
$base-font-size: 1rem;
:root {
--font-size-s: #{$base-font-size} * 0.5;
--font-size-m: #{$base-font-size};
--font-size-l: #{$base-font-size} * 2;
}
Then in other file I used my css variable like that:
p {
font-size: var(--font-size-l);
}
This doesn't work at all my font size doesn't change at all. I thought that maybe there is something wrong with sass mathematical operators, so I tried something like that:
p {
font-size: 1rem * 2;
}
This worked and my font size was equal 2rem as expected. After this discovery I thought that maybe I am doing something wrong with sass variables so changed my css variables to that:
:root {
--font-size-s: 1rem * 0.5;
--font-size-m: 1rem;
--font-size-l: 1rem * 2;
}
And this doesn't work! I really do not know what I am doing wrong. Can someone explain to me how should I define those css variables with sass variables and sass mathematical operators?
Notes:
If I use css calc function this will work too:
:root {
--font-size-s: calc(#{$base-font-size} * 0.5);
--font-size-m: #{$base-font-size};
--font-size-l: calc(#{$base-font-size} * 2);
}
For compiling .scss files I am using Webpack 5.10.3 with css-loader and sass-loader
You need to evalute all the expression like below:
$base-font-size: 1rem;
:root {
--font-size-s: #{$base-font-size * 0.5};
--font-size-m: #{$base-font-size};
--font-size-l: #{$base-font-size * 2};
}

Using env(safe-area-inset-top) in SCSS with max() function

I am developing a site which I wish to display correctly on devices with a notch (particularly the iPhone X as I own one).
In this page the following code sample is given:
#supports(padding: max(0px)) {
.post {
padding-left: max(12px, env(safe-area-inset-left));
padding-right: max(12px, env(safe-area-inset-right));
}
}
However when I have this set, in Chrome I can see that it's not valid (see photo linked below)
Is there a way to correct this or can an SCSS #if statement be used to detect if a parent element has padding > 0 and if not add 1rem of padding to it?
My problem is not the one mentioned here, this is how I am using the code, I have also tried putting this in a standard CSS file without the unquote however its not working either.
If I read your question right you are referring to the css max function not the max function provided by Sass - also the example is CSS and hence needs the mentioned Sass 'hack' to work in SCSS.
The first thing you need to deal with is the iOS 11.0 - 11.2 implementation using constant. The easiest way to do this is to assign the safe-area-inset to CSS variables.
In the example below I've created a default value of 0px for all variables – but you could also use fallback values when using the variables var(--some-var, 12px) (uses 12px if --some-var is not defined).
The second part is your code using the --safe-area-inset variables.
I hope it makes sense :-)
:root {
/* -------------------------------------------------------------------
Assign the default/constant/env values to CSS variables
*/
--safe-area-inset-top : 0px;
--safe-area-inset-right : 0px;
--safe-area-inset-bottom: 0px;
--safe-area-inset-left : 0px;
/* it is probably safe to skip the `constant` test in 2023 :) */
#supports (top: constant(safe-area-inset-top)){
--safe-area-inset-top : constant(safe-area-inset-top);
--safe-area-inset-right : constant(safe-area-inset-right);
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
--safe-area-inset-left : constant(safe-area-inset-left);
}
#supports (top: env(safe-area-inset-top)){
--safe-area-inset-top : env(safe-area-inset-top);
--safe-area-inset-right : env(safe-area-inset-right);
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left : env(safe-area-inset-left);
}
}
#supports(padding: Max(0px)) {
.post {
/* -------------------------------------------------------------------
Use the CSS variables in the max function
*/
padding-left: Max(12px, var(--safe-area-inset-left));
padding-right: Max(12px, var(--safe-area-inset-right));
}
}
You need to unquote max in #supports too, the referenced example should be:
#supports(padding: unquote('max(0px)')) {
padding-left: unquote('max(#{$susy-gutter-width}, env(safe-area-inset-left))');
padding-right: unquote('max(#{$susy-gutter-width}, env(safe-area-inset-right))');
}

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.

Basic arithmetic in GWT CssResource

I'm looking for a way to do something like this:
// style.css
#def borderSize '2px';
.style {
width: borderSize + 2;
height: borderSize + 2;
}
where the width and height attributes would end up having values of 4px.
Sometimes I use the following:
#eval BORDER_SIZE_PLUS_2 2+2+"px"; /* GWT evaluates this at compile time! */
Oddly, this only works, if you don't put any spaces between the + operator and the operands. Also, in #eval you can't use constants that were previously defined by #def. You can however use constants that are defined as static fields in one of your Java classes:
#eval BORDER_SIZE_PLUS_2 com.example.MyCssConstants.BORDER_SIZE+2+"px";
Or you could let the calculation be performed completely by Java:
#eval WIDTH com.example.MyCssCalculations.width(); /* static function,
no parameters! */
#eval HEIGHT com.example.MyCssCalculations.height();
.style {
width: WIDTH;
height: HEIGHT;
}
But what I would actually like to do is very similar to your suggestion:
#def BORDER_SIZE 2;
.style {
width: value(BORDER_SIZE + 2, 'px'); /* not possible */
height: value(BORDER_SIZE + 3, 'px');
}
I don't think that's possible in GWT 2.0. Maybe you find a better solution - here's the Dev Guide page on this topic.
Mozilla kind-of-sort-of-not-really supports this with it's CSS calc() function.
This example shamelessly stolen (with attribution!) from Ajaxian
/*
* Two divs aligned, split up by a 1em margin
*/
#a {
width:75%;
margin-right: 1em;
}
#b {
width: -moz-calc(25% - 1em);
}
It's not cross-browser, and it's probably only barely supported by even bleeding-edge versions of Firefox, but there's at least being progress made in that direction.
You could also calculate in your provider method, if you put a parameter in the function:
#eval baseFontSize com.myexample.CssSettingsProvider.getBaseFontSize(0)+"pt";
#eval baseFontSize_plus_1 com.myexample.CssSettingsProvider.getBaseFontSize(1)+"pt";
com.myexample.CssSettingsProvider would look like this:
public static int getBaseFontSize(int sizeToAdd) {
if (true) {
return 9 + sizeToAdd;
}
return baseFontSize;
}
I would also love somehting like that, but it's not possible.
Even in CSS 3 their is nothing planned like this.
If you really want to make something like that, one possibility is to use
php and configure your webserver, so that .css files are parsed by php.
So you could do something like
<?
$borderSize = 2;
?>
.style {
width: <? borderSize+2 ?>px;
height: <? borderSize+2 ?>px;
}
But as this is no 'standard' way, i think its better to not do it.

Resources