SassError: Undefined variable - css

I have a variable file that has two definitions of themes I want to overwrite:
_vars.scss
body {
$bg-color: #fff
}
body.theme-dark {
$bg-color: #333
}
I´m calling the variables in my Angular button component:
button.scss
#import '_vars.scss';
.button {
background-color: $bg-color;
}
But I´m getting the following compiling error:
SassError: Undefined variable.
background-color: $bg-color;
Whats is the right way to overwrite variables depending on my body theme class?
Thanks

You define $bg-color depending on the theme, but never $font-size-18.
On a side note, I would consider to use CSS Custom Properties instead of SASS variables if I was in your shoes. Codyhouse have an interesting and easy to understand article about this, which also talks about color themes.
If you want to dig deeper into this topic you may want to read this article.

First of all variables inside scope valid only in that scope not universally. Now for your question see below :-
_vars.scss
Instead of making variables inside body scope create it in global scope.
$bg-color: #fff;
body {
background-color: $bg-color;
}
and then import it in your button.scss without underscore "_"
#use "vars" as *;
button {
background-color: $bg-color;
}

Related

Css variables declared in :root block in styles.scss are invalid

In my angular 11 app I am trying to globally use css variables to declare global colors.
In my styles.scss I have:
:root{
--primary : #0b68e8;
--secondary:#ABCFFF;
}
.test-class{
background: var(--primary);
}
When I am applying that class in one of mine components I can see that var was not properly taken from declared variables:
I tried to find any solution , but cant resolve it. Do you maybe know what is wrong here?
try
.test-class{
background: #{var(--primary)};
}
BTW, if you're using sccs, why not use sass variables?
$primary: #0b68e8;
.test-class{
background: $primary;
}

CSS variable & SCSS mixin

I need to be able to use CSS variables because I need to have an hover effect (background-color) to be customizable by my VueJs app.
But my CSS stylesheet should have a default value, which is stored in a nested SCSS map. (map-getter is a function which returns values from nested maps)
I know that my SCSS code works, because I get the intended result when I do this:
.theme--dark .AppNavTile:hover {
background-color: map-getter($theme-dark, AppNav, hover);
//returns background-color: rgba(255, 255, 255, 0.87); in my browser's console
}
In order to use CSS variables, I can modify the code as follows:
.theme--dark .AppNavTile:hover {
--hover-bg-color: red;
background-color: var(--hover-bg-color);
}
It works fine and I have a red background when hovering the element.
Then I try to combine both:
.theme--dark .AppNavTile:hover {
--hover-bg-color: map-getter($theme-dark, AppNav, hover);
background-color: var(--hover-bg-color);
}
According to by browser's console, this returns the following:
.theme--dark .AppNavTile:hover {
--hover-bg-color: map-getter($theme-dark, AppNav, hover);
background-color: var(--hover-bg-color);
}
So it seems that the SCSS code remains uncompiled in the CSS variable. Is there any way around it?
Thanks!
The "problem" with CSS variables is they can have any value – why map-getter($theme-dark, AppNav, hover) is rendered as is. To instruct SCSS that this is actual SCSS code and not a random string you need to use interpolation (like if you use SCSS variables inside calc):
--hover-bg-color: #{map-getter($theme-dark, AppNav, hover)};

Unable to set SCSS variable to CSS variable?

Consider the following SCSS:
$color-black: #000000;
body {
--color: $color-black;
}
When it is compiled with node-sass version 4.7.2, it produces following CSS:
body {
--color: #000000;
}
When I compile the same SCSS with version 4.8.3 or higher, it produces following:
body {
--color: $color-black;
}
What am I missing? I checked release logs, but could not found anything useful. Also, I wonder if this change is genuine why does it have only minor version change? Should it not be a major release?
Also, what is my alternative? Should I use Interpolation?
Just use string interpolation:
$color-black: #000000;
body {
--color: #{$color-black};
}
Apparently the old behaviour is not intended and violated the language specs of SASS:
CSS variables mixed with SCSS variables don't emit proper CSS in 4.8+
CSS variables aren't properly compiled
Assigning SASS variables to CSS Variables (Custom Properties) no longer works
scss and css
I found a workaround to mapping the scss variables to css variables.
See Terry's answer for better use
Scss:
// sass variable map
$colors: (
color-black: #FFBB00
);
// loop over each name, color
:root {
// each item in color map
#each $name, $color in $colors {
--#{$name}: #{$color};
}
}
Css:
:root {
--color-black: #FFBB00;
}
I had an issue with older sass versions.
Trying to compile a list of variables coming from an array, it would get stuck with the double dash. Here's my solution in case it helps someone
$var-element:'--';
:root {
#each $color in $color-variables {
#{$var-element}#{nth($color, 1)}: #{nth($color, 2)};
}
}

How to access a variable defined in another scope in less?

To avoid style conflicts, I've put Bootstrap in a separate namespace as described in this answer:
.bootstrap-styles {
#import 'bootstrap';
}
Now using a Bootstrap variable such as #gray-lighter gives an error:
.footer {
// NameError: variable #gray-lighter is undefined in ...
border-top: 1px solid #gray-lighter;
}
How can I access a variable defined in a different (non-parent) scope in less?
In that way you can't (see #1848).
If a namespace contains only variables you could do it like:
.footer {
.bootstrap-styles(); // copy that namespace into this scope
border-top: 1px solid #gray-lighter;
}
But since .bootstrap-styles also contains styles this will also put all its styles onto .footer. So if you really need to re-use BS variables in a namespaced manner you need to import them separately from styles, e.g. like this:
.bootstrap-styles {
#import 'bootstrap';
}
.bootstrap {
#import (multiple) 'variables';
}
.footer {
.bootstrap(); // using Bootstrap variables here
border-top: 1px solid #gray-lighter;
}
---
Also note that by wrapping the whole bootstrap.less into a namespace you actually break many of its styles (see #2052 etc., so using it this way is quite questionable).

How to override mixins in LESS CSS 1.4+

I've been using what I thought was a very elegant pattern for defining the styles of reusable components/widgets, using LESS. It works beautifully in LESS 1.3-, but after upgrading recently, my whole library is broken. Does anyone know a way to accomplish something like this in 1.4+?
Here's a very simple example of a component:
#componentName {
.loadMixins(){
.text() {}
.header() {}
}
.apply(){
> h3 {
// markup-specific styles
padding: 3px;
margin-bottom: 0;
// custom styles
.header();
}
> div.body, > div.popup p {
color: red;
// custom styles
.text()
}
}
}
And here's how it would be used:
.coolWidget {
#componentName.loadMixins();
// override mixins here
.text(){
color: green;
}
#componentName.apply();
}
This keeps all the markup-dependent styles abstracted from the user. I could completely change my markup and the user's styles would still work. According to the less.js changelog, 1.4.0 Beta 1 has a line "variables in mixins no longer 'leak' into their calling scope"
Is there any way around this?
Strictly speaking nested variables and mixins are still expanded into calling scope unless this scope already has those names defined.
Your example above results in a error:
SyntaxError: .header is undefined...
and it's expected as no .header() is actually defined within the .coolWidget (or anywhere else).
This can be fixed by providing "default" definitions for .text and .header somewhere inside #componentName.
For example if you modify .loadMixins() to:
.loadMixins() {
.text();
.header();
// default properties in case a caller does not provide its own:
.text() {}
.header() {}
}
then the example compiles OK and all text/header properties are overridden as expected.
I can imagine how your library may become broken because of new scope rules but this particular example you gave above does not illustrate the problem.

Resources