I would like if it's possible get css variables from sass variable. I have the next sass variable:
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px,
);
And I'm trying to do an iteration as the next:
#each $key,$val in $breakpoints{
// Here I want to generate the css variables with a for or manually
--breakpoint-#{$key}: $val; // I tried to do something like this but it doesn't works
}
Expected output result:
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
--breakpoint-xxl: 1400px;
Change your loop as below. Notice I replaced $val by #{$val}:
:root {
#each $key, $val in $grid-breakpoints {
--breakpoint-#{$key}: #{$val};
}
}
The syntax #{…} is called interpolation and it is the only way to inject dynamic values into a CSS variable (custom property). Here is a quote from the doc:
CSS custom properties, also known as CSS variables, have an unusual declaration syntax: they allow almost any text at all in their declaration values. What’s more, those values are accessible to JavaScript, so any value might potentially be relevant to the user. This includes values that would normally be parsed as SassScript.
Because of this, Sass parses custom property declarations differently than other property declarations. All tokens, including those that look like SassScript, are passed through to CSS as-is. The only exception is interpolation, which is the only way to inject dynamic values into a custom property.
$primary: red;
:root {
--primary: #{$primary}; // this one works
--primary: $primary; // this does not
}
Related
I'm trying to initialize a variable from custom properties but I can't access it from another file.
More specifically I have set in my _breakpointes-variables.scss
: root {
//Main breakpoints variables
--mobile: 30em;
--tablet: 64em;
--sm-screen: 82em;
--m-screen: 120em;
--l-screen: 140em;
--xl-screen: 160em;
}
And in my _breakpointes.scss i want something like this
$breakpoints: (
"mobile": 30em,
"tablet": 64em,
"sm-screen": 82em,
"m-screen": 120em,
"l-screen": 140em,
"xl-screen": 160em,
) !default;
I already tried with var(--mobile) but I can't get the value in $breakpoints.
$mobile: 30em;
$tablet: 64em;
$sm-screen: 82em;
$m-screen: 120em;
$l-screen: 140em;
$xl-screen: 160em;
:root {
//Main breakpoints variables
--mobile: #{$mobile};
--tablet: #{$tablet};
--sm-screen: #{$sm-screen};
--m-screen: #{$m-screen};
--l-screen: #{$l-screen};
--xl-screen: #{$xl-screen};
}
First I created the UI/UX for the application in which I extracted frequently used components, color palette, fonts, and made these into libraries along with media queries. I realized that some components are essentially the same but only differ in a few properties such as height or width. So I made component styling and components that can be customized on the go, so when it comes to the actual interface implementation I can play LEGO essentially.
The question is in the title. Can I set a default value somehow for the variables? So when It is not passed in I don't get an error, but displays as default? If yes, what is the easiest way and the best practice?
Component SCSS:
#mixin component($width, $height, $color) {
.component{
height: $height;
width: $width;
.somethingNested {
color: $color;
}
...
}
#content (if something needs to be overwritten)
}
Yes it’s possible. You add a : value after the parameter.
#mixin component($width: 80px, $height: 80px, $color: white) {
...
}
Tips: mixin supports both positional argument and named argument.
// you wanna keep the default $width and $height but change $color
// with positional argument you have to repeat yourself
.foo {
#include component(80px, 80px, pink);
}
// with named argument, just provide the $color
. foo {
#include component($color: pink);
}
Ref: https://sass-lang.com/documentation/at-rules/mixin#optional-arguments
TL;DR: How can you use SCSS to have CSS variables with a fallback for older browsers.
I'm trying to make sense of this article. In my opinion, you have to already be an advanced SASS user to understand it, which I'm not. To make matters worse, it's the only article I found on the subject.
Here is what I'm trying to achieve:
My scss should be along the lines of :
body {
#include v(background-color, primary)
}
then the processed CSS should be
body{
background: yellow; /* Yellow being defined above as the primary color */
background: var(--color-primary);
}
By playing around a bit, I can already get the value of the CSS variable like so:
$colors: (
primary: yellow,
);
:root {
#each $name, $color in $colors {
--color-#{$name}: $color;
}
}
#mixin background-color($color_) {
background: var(--color-#{$color_});
}
To use it:
body{
#include background-color(primary);
}
Which will result in this:
body {
background: var(--color-primary);
/* But the fallback is missing :(, I tried things with the map-get but it's really eluding me... */
}
If you're using Sass, you can automate fallbacks through a Sass mixin. Create a map of your CSS variable names and their values, and then you can look up those values in a mixin that outputs the fallback style and the preferred one
$vars: (
primary: yellow,
);
:root {
--primary: map-get($vars, primary);
}
#mixin var($property, $varName) {
#{$property}: map-get($vars, $varName);
#{$property}: var(--#{$varName});
}
The above mixin is used like so:
body {
#include var(background-color, primary);
}
and outputs the following CSS:
:root {
--primary: yellow;
}
body {
background-color: yellow;
background-color: var(--primary);
}
Et voilà :)
Update: Postcss Custom properties can do fallback and is way easier than the below code
step 1: declare scss variables
So first of all we want to put some variables in a $map, I'll go with color variables:
$colors: (
primary: #FFBB00,
secondary: #0969A2
);
step 2: automate css 4 var generation
// ripped CSS4 vars out of color map
:root {
// each item in color map
#each $key, $value in $colors {
--colors-#{$key}: $value;
}
}
What happens in root is : for each key and value in the colors map, we print the followng :
--colors-#{$key}: $value;
Which corresponds to css variable declarations. I believe the weird bit with #{} around the key is to not have spaces around the value.
Thus the result is:
--colors-primary: #FFBB00,
--colors-secondary: #0969A2
Note that the prefix (--colors-) is the same name as the scss color map above it. The why will become clear in last step.
step 3: Plenty of maps !
$props: (
background-color: $colors
);
$map-maps: (
background-color: colors
);
Here we add the map $props which maps a css property to the map containing the values. background-color will hold color, so the correct map is $colors.
map-maps is a copy of props where instead of the map we have the name of said map. (this is relative to the note in step 2).
Step 4 : let's make it work !
#mixin v($prop, $var) {
// get the map from map name
$map: map-get($props, $prop);
// fallback value, grab the variable's value from the map
$var-fall: map-get($map, $var);
// our css4 variable output
$var-output: var(--#{$map}-#{$var});
#{$prop}: $var-fall;
// css4 variable output
#{$prop}: $var-output;
}
body{
#include v(background-color, primary);
}
I simplified the code in the article quite a bit, it still works, for this example at least, the code in the article takes more into account.
Anyhow, here is what happens.
First, we call the mixin with:
#include v(background-color, primary);
Then upon entering,
$map: map-get($props, $prop); // map-get($props, background-color)
we have a variable called $map to which we assign the value that is inside the $props map at the key background-color which happen to be the $colors map. It's a bit of a maze but it's not that complicated once you resolve it.
Then for the fallback:
$var-fall: map-get($map, $var);
This simply gets the value of the map we just got (which is $colors) at the $var key (which happens to be primary). Thus the result is #FFBB00.
For the css var
$map-name: map-get($map-maps, $prop);
$var-output: var(--#{$map-name}-#{$var});
we recreate what we did to generate the var in the #each loop
Whole code would be :
$colors: (
primary: #FFBB00,
secondary: #0969A2
);
// ripped CSS4 vars out of color map
:root {
// each item in color map
#each $name, $color in $colors {
--colors-#{$name}: $color;
}
}
$props: (
background-color: $colors,
color: $colors
);
$map-maps: (
background-color: colors
);
#mixin v($prop, $var) {
// get the map from map name
$map: map-get($props, $prop);
// fallback value, grab the variable's value from the map
$var-fall: map-get($map, $var);
// our css4 variable output
$map-name: map-get($map-maps, $prop);
$var-output: var(--#{$map-name}-#{$var});
#{$prop}: $var-fall;
// css4 variable output
#{$prop}: $var-output;
}
body{
#include v(background-color, primary);
}
Now this is a simplification of what is done in the article. You should check it out to have code a bit more robust.
I assume you are aware of the reason why it didn't show the fallback. But since it's an answer I will explain the reasons
The current mixin block has only one background property which makes the sass compiler to generate only one property. I don't think sass can identify whether 'var' is supported in browser or not. So, we have to explicitly specify if we need the fallback.
Since you already have the map all you need is to get the value by giving the key 'primary'
#mixin background-color($color_) {
background: var(--color-#{$color_});
background: map-get($colors, primary);
}
This will add the background: yellow to the body class always. Alternatively if you want to control the addition of the fallback based on condition. You can do like this
#mixin background-color($color_, $showFall) {
background: var(--color-#{$color_});
#if $showFall {
background: map-get($colors, primary);
}
}
and call like this
body{
#include background-color(primary, true);
}
Code pen for the same
https://codepen.io/srajagop/pen/xdovON
Note: I am writing the answer under the assumption that you want only the background-color to work and not all the other properties like mentioned in that post. For that you need to create a proper data structure
I'm trying to use LESS to dynamically generate a set of mixins that would help me write cleaner media query code. So far in my limited knowledge of the language I've put together code that looks like this:
#sizes: xxs, xs, sm, md, lg;
.mediaQueries(#iterator:1) when(#iterator <= length(#sizes)) {
//Extract name
#sizeName: extract(#sizes, #iterator);
//Attempt to build min-width query
.MQ-min-#{sizeName} (#content) {
#media (min-width: #screen-#{sizeName}) {
#content();
}
}
//Attempt to build max-width query
.MQ-max-#{sizeName} (#content) {
#media (max-width: #screen-#{sizeName}) {
#content();
}
}
.mediaQueries((#iterator + 1));
}
.mediaQueries();
The goal is to have a set of mixins that would allow me to easily and cleanly define some CSS properties for a specific breakpoint, like so:
.generic-class {
background: black;
//Sizes #screen-sm and up
.MQ-min-sm({
background: transparent;
})
}
It doesn't work. Something to note, I'm trying to interpolate the size name into a variable name that would then output me a the px value of that variable into the #media query. Is something like this even possible?
Otherwise my compiler currently breaks on the start of the nested mixin (.MQ-min-#{sizeName} (#content) {) with the error:
Potentially unhandled rejection [2] Missing closing ')' in file ../mixins.less line no. 43
Is something like what I'm trying to achieve possible?
I think the simplest way for you to achieve this is by using a single parametric mixin like given below. This avoids the need for all those iterations, dynamic mixin creations etc.
#sizes: xxs, xs, sm, md, lg;
#screen-xxs: 100px;
#screen-sm: 200px;
.MQ(#content, #sizeName, #max-min) { /* get ruleset, size name and min/max as input */
#selector: ~"(#{max-min}-width: #{screen-#{sizeName}})"; /* form the media selector */
#media #selector { /* use it */
#content();
}
}
.generic-class {
background: black;
.MQ({
background: transparent;
}, /* ruleset */
sm, /* screen size */
max /* min/max */
);
}
If the mixins are for your own usage then this is all that you need. If it is for distribution as library then you may want to put some guards on #sizeName and #max-min variables to restrict invalid values.
Note: Less compiler always had a problem with the interpolation here - #media (min-width: #screen-#{sizeName}) also (I am not sure if it has been addressed) and that's why I created a temp variable.
I have this variable:
$color_pr1: #d6ad3f;
Now, I'm using Gumby and it uses it's own settings sheet where the following is set:
$header-font-color: #55636b !default;
Is it possible to use $color_pr1 instead? Like this?
$header-font-color: $color_pr1; ?
If now, am I thinking about this all wrong?
I'd like to have my own set of colors etc and reuse those within my project.
From the docs: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#variable_defaults_
You can assign to variables if they aren’t already assigned by adding
the !default flag to the end of the value. This means that if the
variable has already been assigned to, it won’t be re-assigned, but if
it doesn’t have a value yet, it will be given one.
For example:
$content: "First content";
$content: "Second content?" !default;
$new_content: "First time reference" !default;
#main {
content: $content;
new-content: $new_content;
}
is compiled to:
#main {
content: "First content";
new-content: "First time reference"; }
Variables with null values are treated as unassigned by !default:
$content: null;
$content: "Non-null content" !default;
#main {
content: $content;
}
is compiled to:
#main {
content: "Non-null content"; }
Use css calc() function:
$header-font-color: calc(#{$color_pr1});
You can define a map:
From the Sass Documentation
Users occasionally want to use interpolation to define a variable name based on another variable. Sass doesn’t allow this, because it makes it much harder to tell at a glance which variables are defined where. What you can do, though, is define a map from names to values that you can then access using variables.
SCSSSassCSS
SCSS SYNTAX
#use "sass:map";
$theme-colors: (
"success": #28a745,
"info": #17a2b8,
"warning": #ffc107,
);
.alert {
// Instead of $theme-color-#{warning}
background-color: map.get($theme-colors, "warning");
}