Sass interpolated variable names [duplicate] - css

I'm trying to use string interpolation on my variable to reference another variable:
// Set up variable and mixin
$foo-baz: 20px;
#mixin do-this($bar) {
width: $foo-#{$bar};
}
// Use mixin by passing 'baz' string as a param for use $foo-baz variable in the mixin
#include do-this('baz');
But when I do this, I get the following error:
Undefined variable: "$foo-".
Does Sass support PHP-style variable variables?

This is actually possible to do using SASS maps instead of variables. Here is a quick example:
Referencing dynamically:
$colors: (
blue: #007dc6,
blue-hover: #3da1e0
);
#mixin colorSet($colorName) {
color: map-get($colors, $colorName);
&:hover {
color: map-get($colors, #{$colorName}-hover);
}
}
a {
#include colorSet(blue);
}
Outputs as:
a { color:#007dc6 }
a:hover { color:#3da1e0 }
Creating dynamically:
#function addColorSet($colorName, $colorValue, $colorHoverValue: null) {
$colorHoverValue: if($colorHoverValue == null, darken( $colorValue, 10% ), $colorHoverValue);
$colors: map-merge($colors, (
$colorName: $colorValue,
#{$colorName}-hover: $colorHoverValue
));
#return $colors;
}
#each $color in blue, red {
#if not map-has-key($colors, $color) {
$colors: addColorSet($color, $color);
}
a {
&.#{$color} { #include colorSet($color); }
}
}
Outputs as:
a.blue { color: #007dc6; }
a.blue:hover { color: #3da1e0; }
a.red { color: red; }
a.red:hover { color: #cc0000; }

Sass does not allow variables to be created or accessed dynamically. However, you can use lists for similar behavior.
scss:
$list: 20px 30px 40px;
#mixin get-from-list($index) {
width: nth($list, $index);
}
$item-number: 2;
#smth {
#include get-from-list($item-number);
}
css generated:
#smth {
width: 30px;
}
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#lists
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#list-functions

Anytime I need to use a conditional value, I lean on functions. Here's a simple example.
$foo: 2em;
$bar: 1.5em;
#function foo-or-bar($value) {
#if $value == "foo" {
#return $foo;
}
#else {
#return $bar;
}
}
#mixin do-this($thing) {
width: foo-or-bar($thing);
}

Here's another option if you're working with rails, and possibly under other circumstances.
If you add .erb to the end of the file extension, Rails will process erb on the file before sending it to the SASS interpreter. This gives you a can chance to do what you want in Ruby.
For example: (File: foo.css.scss.erb)
// Set up variable and mixin
$foo-baz: 20px; // variable
<%
def do_this(bar)
"width: $foo-#{bar};"
end
%>
#target {
<%= do_this('baz') %>
}
Results in the following scss:
// Set up variable and mixin
$foo-baz: 20px; // variable
#target {
width: $foo-baz;
}
Which, of coarse, results in the following css:
#target {
width: 20px;
}

I came across the need to reference a colour dynamically recently.
I have a _colours.scss file for every project, where I define all my colours once and reference them as variables throughout.
In my _forms.scss file I wanted to setup button styles for each colour available. Usually a tedious task. This helped me to avoid having to write the same code for each different colour.
The only downside is that you have to list each colour name and value prior to writing the actual css.
// $red, $blue - variables defined in _colours.scss
$colours:
'red' $red,
'blue' $blue;
#each $name, $colour in $colours {
.button.has-#{$name}-background-color:hover {
background-color: lighten($colour, 15%);
}
}

I needed to use dynamic color values in sass variables.
After lots of search, I applied this solution:
In application.html.erb:
<style>
:root {
--primary-color: <%= current_client.header_color %>;
--body-color: <%= current_client.footer_color %>;
}
</style>
In variables.sass:
$primary: var(--primary-color);
And boom you are good to go!
Reference: https://medium.com/angular-in-depth/build-truly-dynamic-theme-with-css-variables-539516e95837

To make a dynamic variable is not possible in SASS as of now, since you will be adding/connecting another var that needs to be parsed once when you run the sass command.
As soon as the command runs, it will throw an error for Invalid CSS, since all your declared variables will follow hoisting.
Once run, you can't declare variables again on the fly
To know that I have understood this, kindly state if the following is correct:
you want to declare variables where the next part (word) is dynamic
something like
$list: 100 200 300;
#each $n in $list {
$font-$n: normal $n 12px/1 Arial;
}
// should result in something like
$font-100: normal 100 12px/1 Arial;
$font-200: normal 200 12px/1 Arial;
$font-300: normal 300 12px/1 Arial;
// So that we can use it as follows when needed
.span {
font: $font-200;
p {
font: $font-100
}
}
If this is what you want, I am afraid as of now, this is not allowed

Related

Get sass variable-value instead of variable-name [duplicate]

I'm trying to use string interpolation on my variable to reference another variable:
// Set up variable and mixin
$foo-baz: 20px;
#mixin do-this($bar) {
width: $foo-#{$bar};
}
// Use mixin by passing 'baz' string as a param for use $foo-baz variable in the mixin
#include do-this('baz');
But when I do this, I get the following error:
Undefined variable: "$foo-".
Does Sass support PHP-style variable variables?
This is actually possible to do using SASS maps instead of variables. Here is a quick example:
Referencing dynamically:
$colors: (
blue: #007dc6,
blue-hover: #3da1e0
);
#mixin colorSet($colorName) {
color: map-get($colors, $colorName);
&:hover {
color: map-get($colors, #{$colorName}-hover);
}
}
a {
#include colorSet(blue);
}
Outputs as:
a { color:#007dc6 }
a:hover { color:#3da1e0 }
Creating dynamically:
#function addColorSet($colorName, $colorValue, $colorHoverValue: null) {
$colorHoverValue: if($colorHoverValue == null, darken( $colorValue, 10% ), $colorHoverValue);
$colors: map-merge($colors, (
$colorName: $colorValue,
#{$colorName}-hover: $colorHoverValue
));
#return $colors;
}
#each $color in blue, red {
#if not map-has-key($colors, $color) {
$colors: addColorSet($color, $color);
}
a {
&.#{$color} { #include colorSet($color); }
}
}
Outputs as:
a.blue { color: #007dc6; }
a.blue:hover { color: #3da1e0; }
a.red { color: red; }
a.red:hover { color: #cc0000; }
Sass does not allow variables to be created or accessed dynamically. However, you can use lists for similar behavior.
scss:
$list: 20px 30px 40px;
#mixin get-from-list($index) {
width: nth($list, $index);
}
$item-number: 2;
#smth {
#include get-from-list($item-number);
}
css generated:
#smth {
width: 30px;
}
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#lists
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#list-functions
Anytime I need to use a conditional value, I lean on functions. Here's a simple example.
$foo: 2em;
$bar: 1.5em;
#function foo-or-bar($value) {
#if $value == "foo" {
#return $foo;
}
#else {
#return $bar;
}
}
#mixin do-this($thing) {
width: foo-or-bar($thing);
}
Here's another option if you're working with rails, and possibly under other circumstances.
If you add .erb to the end of the file extension, Rails will process erb on the file before sending it to the SASS interpreter. This gives you a can chance to do what you want in Ruby.
For example: (File: foo.css.scss.erb)
// Set up variable and mixin
$foo-baz: 20px; // variable
<%
def do_this(bar)
"width: $foo-#{bar};"
end
%>
#target {
<%= do_this('baz') %>
}
Results in the following scss:
// Set up variable and mixin
$foo-baz: 20px; // variable
#target {
width: $foo-baz;
}
Which, of coarse, results in the following css:
#target {
width: 20px;
}
I came across the need to reference a colour dynamically recently.
I have a _colours.scss file for every project, where I define all my colours once and reference them as variables throughout.
In my _forms.scss file I wanted to setup button styles for each colour available. Usually a tedious task. This helped me to avoid having to write the same code for each different colour.
The only downside is that you have to list each colour name and value prior to writing the actual css.
// $red, $blue - variables defined in _colours.scss
$colours:
'red' $red,
'blue' $blue;
#each $name, $colour in $colours {
.button.has-#{$name}-background-color:hover {
background-color: lighten($colour, 15%);
}
}
I needed to use dynamic color values in sass variables.
After lots of search, I applied this solution:
In application.html.erb:
<style>
:root {
--primary-color: <%= current_client.header_color %>;
--body-color: <%= current_client.footer_color %>;
}
</style>
In variables.sass:
$primary: var(--primary-color);
And boom you are good to go!
Reference: https://medium.com/angular-in-depth/build-truly-dynamic-theme-with-css-variables-539516e95837
To make a dynamic variable is not possible in SASS as of now, since you will be adding/connecting another var that needs to be parsed once when you run the sass command.
As soon as the command runs, it will throw an error for Invalid CSS, since all your declared variables will follow hoisting.
Once run, you can't declare variables again on the fly
To know that I have understood this, kindly state if the following is correct:
you want to declare variables where the next part (word) is dynamic
something like
$list: 100 200 300;
#each $n in $list {
$font-$n: normal $n 12px/1 Arial;
}
// should result in something like
$font-100: normal 100 12px/1 Arial;
$font-200: normal 200 12px/1 Arial;
$font-300: normal 300 12px/1 Arial;
// So that we can use it as follows when needed
.span {
font: $font-200;
p {
font: $font-100
}
}
If this is what you want, I am afraid as of now, this is not allowed

CSS :root variables and SASS Functions

in the Header of my HTML-Page i set the following CSS Variables:
:root{
--data-color-primary: #ffcc00;
--data-color-secondary: #4b4b4b;
}
In my SASS-File i use it as follow:
DIV.color {
&.color-primary {
background-color: var(--data-color-primary);
}
&.color-secondary {
background-color: var(--data-color-secondary);
}
}
Now i try to set the font-color depending on the brightness of the background-color:
#function set-notification-text-color($color) {
#if (lightness($color) > 60) {
#return #000000;
} #else {
#return #ffffff;
}
}
DIV.color{
&.color-primary {
background-color: var(--data-color-primary);
color: set-notification-text-color(var(--data-color-primary));
}
}
But in my SASS-compliler i get the following i get the following Error:
Error: argument $color of lightness($color) must be a color
How is ist possible to hand over der CSS variable to the function.
My Problem is, the CSS Variables are set in the Backend of my CMS by User (Liferay 7) an will be rendered in a *.FTL-File and is printed in the HTML-Code.
$primary: ${clr_primary};
$secondary: ${clr_primary};
and then i cant use the SASS-Variable $primary in my SASS-File.
Use SASS variables in your CSS variables.
$primary: #ffcc00;
$secondary: #4b4b4b;
:root{
--data-color-primary: #{$primary};
--data-color-secondary: #{$secondary};
}
Now call your mixin
DIV.color{
&.color-primary {
background-color: $primary;
color: set-notification-text-color($primary);
}
}
Another options would be to create a mixin which retrieves the CSS variable
#function color($color-name) {
#return var(--data-color-#{$color-name});
}
Now call that function like so
DIV.color {
&.color-primary {
background-color: color(primary);
color: set-notification-text-color(color(primary));
}
}
Check out this link for usefull information about combining CSS and SASS variables
https://codepen.io/jakealbaugh/post/css4-variables-and-sass
If you need to change CSS variables outside of :root you can do the following
.class {
// sass-lint:disable no-duplicate-properties
#{--background}: transparent;
#{--background-focused}: transparent;
// sass-lint:enable no-duplicate-properties
}
and this compiles to
.class {
--background: transparent;
--background-focused: transparent;
}

Is it possible to set empty string as a class selector in sass?

I have a variable which have empty string as default value and it can be changed dynamically based on skin name. So I tried to write my sass selector like this.
default.scss:
$skin: '';
green.scss:
$skin: 'green';
main.scss:
#import 'default.scss';
.#{$skin} .header{
color: black;
}
So I expected the below output from generated CSS.
.header{
color: black;
}
and
.green .header{
color: black;
}
But it throws below error while compiling
Invalid CSS after ".": expected class name, was ".header"
You could use a function for this:
$skin: 'green';
#function skin(){
#if $skin != '' { #return '.' + $skin + ' '; }
#else { #return ''; }
}
Now you could use it like this:
#{skin()} .header { ... }
Which will output:
.green .header { display: block; }
It might be better to use a better global variable name (skin seems like something that might quickly be reused - I would suggest something like $global-skin-name or something, but thats just semantics).

Sass and libraries for theming [duplicate]

I'm refactoring some of my Sass code and I came across a weird issue. My code currently looks like this:
// household
$household_Sector: 'household';
$household_BaseColor: #ffc933;
// sports
$sports_Sector: 'sports';
$sports_BaseColor: #f7633e;
// the mixin to output all sector specific css
#mixin sector-css($sector_Sector,$sector_BaseColor) {
.sector-#{$sector_Sector} {
&%baseColor {
background-color: $sector_BaseColor;
}
}
}
// execute the mixin for all sectors
#include sector-css($household_Sector, $household_BaseColor);
#include sector-css($sports_Sector, $sports_BaseColor);
.product-paging {
h2 {
#extend %baseColor;
}
}
DEMO
The compiled result looks like this:
.product-paging h2.sector-household {
background-color: #ffc933;
}
.product-paging h2.sector-sports {
background-color: #f7633e;
}
But what I need is this:
.sector-household.product-paging h2 {
background-color: #ffc933;
}
.sector-sports.product-paging h2 {
background-color: #f7633e;
}
What I don't understand is why my placeholder (&%baseColor) isn't attached to the parent selector (&%baseColor) as I added the ampersand right in front of it?
Is this maybe a bug when combining & and %? Is there another solution on how to achieve what I want?
EDIT
Alright I figured out why this isn't possible. Anyway is there a workaround for what I'd like to achieve?
Extends, as you've already discovered, can get rather messy. I would go about solving your problem by using an #content aware mixin in combination with global variables (this uses mappings, which are part of 3.3... you can do it with lists of lists, but it's a little less elegant):
$base-color: null; // don't touch
$accent-color: null; // don't touch
$sections:
( household:
( base-color: #ffc933
, accent-color: white
)
, sports:
( base-color: #f7633e
, accent-color: white
)
);
// the mixin to output all sector specific css
#mixin sector-css() {
#each $sector, $colors in $sections {
$base-color: map-get($colors, base-color) !global;
$accent-color: map-get($colors, accent-color) !global;
&.sector-#{$sector} {
#content;
}
}
}
.product-paging {
#include sector-css() {
h2 {
background-color: $base-color;
}
}
}
Output:
.product-paging.sector-household h2 {
background-color: #ffc933;
}
.product-paging.sector-sports h2 {
background-color: #f7633e;
}
Update: Since you want to guarantee that the sector class is always at the top, you just need to switch around a little.
#mixin sector-css() {
#each $sector, $colors in $sections {
$base-color: map-get($colors, base-color) !global;
$accent-color: map-get($colors, accent-color) !global;
.sector-#{$sector} {
#content;
}
}
}
#include sector-css() {
&.product-paging {
h2 {
background-color: $base-color;
}
h3 {
background-color: #CCC;
}
h2, h3 {
color: $accent-color;
}
}
}

How to change a class format based on body's class with SCSS?

I am making a web app that is used in three (or more) different contexts, and I want each context to have a different color scheme. However, I don't want to have to maintain three different stylesheets when all that changes is colors, typically.
For instance, suppose the themes are red, blue, and orange. One of my stylesheets describes the link colors:
a {
color: $some_color;
}
I want to split this based on the class applied to the body:
body.style1 {
a {
color: $red;
}
}
body.style2 {
a {
color: $blue;
}
}
body.style3 {
a {
color: $orange;
}
}
You can see how this gets unwieldy pretty quickly if you're changing the style for lots of elements. Is there a way to do this more like this?
a {
&closest:body.style1 {
color: $red
}
&closest:body.style2 {
color: $blue;
}
&closest:body.style3 {
color: $orange;
}
}
This way I can code my scss in a clearer, more maintainable way.
It appers you don't have to have the & first, so this works (at least in 3.2.10):
a {
body.style1 & {
color: $red
}
body.style2 & {
color: $blue;
}
body.style3 &{
color: $orange;
}
}
This is what I prefer. Define a mixin like body-style :
#mixin body-style($style, $map) {
body.#{$style} & {
#each $property, $value in $map {
#{$property}: $value;
}
}
}
Then use this for every tag by passing $style as style class of body and $map as map of css keys and values.
a {
#include body-style(style1, (
color: red,
background: white
)
);
}
It will return :
body.style1 a {
color: red;
background: white;
}

Resources