How to set default values to SCSS variables? [duplicate] - css

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");
}

Related

Is it possible to define and overwrite Sass/SCSS variables based on conditions

I have an I have a _overrides.scss file in which I want to provide a global variable based on conditions.
The variable should be true if my navigation with the .side-navigation class also contains the .-open class. If it contains the -closed class, the variable should have the value false.
Something like this:
$navbarOpen: false;
.side-navigation {
&.-open {
$navbarOpen: true;
}
&.-closed {
$navbarOpen: false;
}
}
I want to use the variable within another SCSS module in React, like:
#import 'overrides';
#if $navbarOpen == true {
footer {
background: red;
}
}
The variable is recognized, but the value is always false since it doesn't seem to be overridden by the condition set in _overrides.scss.
I think that the problem is that Sass variables can't be changed in the runtime, it'll be compiled to plain CSS and all vars will be replaced. Although as I see your condition depends on runtime events.
Check this for references: Dynamic Sass Variables

How to check in SCSS if a variable exists and ha a value?

I am new in SCSS so bear with me :)
I have a use case where a SCSS variable --my-variable can exist and can have a value depending on some settings from the backend. So, if --my-variable exists and has a value I should override some styling. If not I shouldn't override anything.
Example:
In file1 I have:
.my-div {
color: red;
}
In file2 I should have something like this:
.my-div {
#include customize(color, --my-variable);
}
#mixin customize($property, $variable) {
#if $variable and (var($variable)) {
#{$property}: var($variable);
}
}
The problem is that the if condition inside the mixin customize() is always true even if my document has no CSS variable called --my-variable. What am I doing wrong?
Thank you
Sass has a function that check if the variable exists.
variable-exists()
$colorVariable: crimson;
#if variable-exists($colorVariable) {
// Do some styling if the variable exists
}

Creating SASS override for white labelled application

I'm running a white label application where many brands are using a set of SASS variables. But over the time, it happens that additional variables needs to be added for a given breakpoint in order to allow more flexibility. Thus, in order to keep backward compatibility, I assign the current value to the newly created variable ($test--large). For the brand, to be able to customize style for large breakpoint.
The issue is that it doesn't work the way i'd expect it to work. If at brand level, I change $test, i'd like $test--large to also have the new value assignment.
Hope it makes sense, if not what would you suggest. Knowing that we have many brands and it would be painful to redefine all the new variables.
$test: blue !default;
$test--large: $test !default; // new variable
$test: green; // Override from brand
.text {
color: $test;
// Newly added piece
#include breakpoint(large) {
color: $test--large; //result will be blue but i'd expect it to be green
}
}
`
It looks like when you assign $test to $test--large, the variable is being passed by value, not reference. Hence when you reassign $test to green, you need to also reassign $test--large, e.g. like this:
$test--large: $test
or
$test--large: green
I wanted to avoid this solution because it will require me to change it in all my project.
The variables override need to happen before and not after the !default values
I'm still wondering if it isn't possible without interchanging the variable import order
$test: green; // Override from brand
$test: blue !default;
$test--large: $test !default; // new variable
.text {
color: $test;
// Newly added piece
#include breakpoint(large) {
color: $test--large; //result will be green as expected
}
}

SASS Multiple levels of variable precedence/inheritance

I'm using Laravel Mix and Webpack for SASS pre-processing.
I have two "themes" in my website which I want to be lean, inheriting variables where they need to. For example, my primary theme will include in this order:
// Primary theme
#import "./primary-variables.scss";
#import "/path/to/default/theme/main.scss";
My default theme would look like this:
// Default theme
#import "./default-variables.scss";
#import "~bootstrap-sass/assets/stylesheets/_bootstrap";
Similarly to this question, I've included the primary variables first, then the default theme variables, then bootstrap last.
In my default theme I add !default to all variables so where they are redefining Bootstrap they will be used in priority, and where new they will be a default value. The primary theme doesn't use !default at all.
Working example
If Bootstrap defines $brand-danger as say red !default, my default theme redefines it as blue !default and my primary theme redefines it as yellow, my rendered output will be yellow - great!
The problem
When I need to reference variables that are only defined at other levels from my primary theme. For example:
// Primary theme:
// This fails since I haven't defined $brand-primary in my primary theme
$my-primary-theme-variable: $brand-primary;
The build now fails with an error saying primary-theme/src/scss/main.scss doesn't export content.
Workaround
I can work around this problem by copying the entire Bootstrap variables file through to my primary theme and changing variables as necessary, but I don't really want to do this.
Question
How does the SASS variable processor actually work? Is it possible for me to just change one of the Bootstrap variables in my theme without necessarily having to redefine the entire file?
This question is pretty similar.
It seems like you are using #include to import your SCSS try using #import instead – If this is just a typo in the question please let me know :-)
#import "./primary-variables.scss",
"/path/to/default/theme/main.scss"
;
I've added a few quick notes on the question you were referring to.
The important thing to know about the !default flag is that it takes effect at the point when it is used in a selector and does not re-define variables.
Sass does not look ahead when processing variables – it prints out the current value. In this example .class-1 will be red as the re-definition comes after it being used in the selector and .class-2 will be blue as there is no default flag.
$brand-color: red !default; // defined
.class-1 { background-color: $brand-color; } // red
$brand-color: blue; // re-defined
.class-2 { background-color: $brand-color; } // blue
Default flags will cause Sass to skip variable re-definition. In this example the result will be red as being defined first. The two following re-definitions are ignored because of the default flags.
$brand-color: red !default; // defined
$brand-color: blue !default; // ignored
$brand-color: green !default; // ignored
.class-1 { background-color: $brand-color; } // red
In this case all variables from from the config will be used – then variables from partial-1 if not defined in config and last partial-2 will define any variable not defined in the two others.
#import '_config.scss'; // definition
#import '_partial-1.scss'; // contains defaults
#import '_partial-2.scss'; // contains defaults
Hope it makes sense :-)
Import structure
// _default-theme.scss
#import '_default-variables.scss', '_bootstrap.scss';
// _primary-theme.scss
// primary variables will override defaults or use defaults if not defined
#import '_primary-variables.scss', '_default-theme.scss';
// style.scss
#import '_primary-theme.scss'; // or '_default-theme.scss'
Scope
In case your default and primary has content that is unique to each theme you could create a scoping mixin to handle what is compiled.
Here is a very rudimentary version:
// _scope.scss
$scope-context: null !default;
#function scope($scopes: null, $true: true, $false: false) {
#each $scope in $scope-context {
#if index($scopes, $scope) { #return $true }
}
#return $false;
}
#mixin scope($scopes: null) {
#if scope($scopes) or length($scopes) == 0 and not $scope-context {
#content;
}
}
How it works
The scope mixin takes a context argument and a content block #content. If the passed context matches a global variable ($scope-context) the content block get's rendered.
// _default-theme.scss
.class { content: 'Will show in both themes'; }
#include scope(default-theme){
.class { content: 'Will only show in the default theme'; }
}
#include scope(primary-theme){
.class { content: 'Will only show in the primary theme'; }
}
// can also be used as "if" function
.class {
content: scope(default-theme, 'Is default', 'Not default')
}
In your case define the $scope-context in both default and primary variables
// _default-variables.scss
$scope-context: default-theme !default;
// _primary-variables.scss
$scope-context: primary-theme;
... and add _scope.scss to the _default-theme.scss
// _default-theme.scss
#import '_default-variables.scss', '_bootstrap.scss', '_scope.scss';
The problem I found was that I was assuming things incorrectly about how SASS works.
When you define a variable declaration, the value of it is compiled at the time your write it. For example $my-var: $brand-primary would assign the current value of $brand-primary to $my-var at the time it is processed.
This means simply that I can't achieve what I wanted, which was to include a minimal variables file over the top of Bootstrap, because it would only update the variable itself, but not any other variables that reference that variable within Bootstrap.
The solution
It's not elegant, but duplicate the entire variable file for each theme and adjust them as required in each place.

Append a variable's value to a selector #if it exists (using variable-exists)

I'm trying to add in a class name to SCSS if it exists.
Using a base framework which outputs 2 CSS files.
theme_1.css
theme_2.css
Each CSS file is compiled from its respective SCSS file - which either contains a variable called $body-container or not.
$body-container: .body-container;
If the variable exists then it should be appended to the body tag.
This is the SCSS that I've tried so far
body #if(variable-exists(body-container)) { $body-container } {
/* styles here */
}
I'm expecting the following:
For the SCSS file that contains the variable
body .body-container {
/* styles here */
}
and without the variable declared
body {
/* styles here */
}
But getting the following error Error: Invalid CSS after "...body-container ": expected ":", was "} {"
As mentioned in my comment, I don't think it is possible to append a variable to a selector in that way (the one used in question). The error message indicates that it expects a : after the variable name which is implying that it is expecting a value assignment to the variable within the #if loop.
Having said that, it is still possible to achieve this using the below workaround. The idea is to see if the variable is defined and if yes, set another dummy variable ($selector) as the concatenation of body and the original variable's ($body-container) value. If not, then just set it as body.
Then we can use this dummy variable ($selector) through interpolation. So, if $body-container has been defined the selector would be body .body-container. Else, it would simply be body.
$body-container: ".body-container";
#if variable-exists(body-container) {
$selector: "body " + $body-container !global;
}
#else {
$selector: "body" !global;
}
#{$selector} {
color: red;
}

Resources