Solved, but I don’t know from which version this works. I believe the solution could have always been available. Tested on:
> sassc --version
sassc: 3.2.1
libsass: 3.2.5
sass2scss: 1.0.3
We are going to use a simplified environment, so filenames do not match with Bootstrap’s.
Challenge
Given a framework we do not control (for example installed only on the Continuous Integration environment and not available in our machines) that expresses SCSS variables in the following manner:
// bootstrap/_variables.scss
$brand-primary: #f00 !default;
$brand-warning: #f50 !default;
$link-color: $brand-primary !default;
And given a file in that same framework that uses the variables:
// bootstrap/main.scss
a:link, a:visited {
color: $link-color;
}
The challenge is:
Include the framework in your own application’s SCSS in such a way that
- variables’ dependencies in the framework are preserved and honors;
- you can depend in on the default values but still be able to change the results on the framework dependencies.
More precisely:
Include the framework in your application’s SCSS in such a way that $brand-color
will always be the inverse of $brand-warning
, whatever its value is in the framework.
Solution
The main file would look like this:
// application.scss
@import "variables";
@import "bootstrap/variables";
@import "bootstrap/main";
And your variables file would look like this:
// _variables.scss
%scope {
@import "bootstrap/variables";
$brand-primary: invert($brand-warning) !global;
}
Results:
> sassc main.scss
a {
color: blue; }
Explanation
The %scope
part is not something magic of SCSS, it’s simply a hidden class with the name scope
, available exclusively for later extensions with @extend
. We are using it just to create a variable scope (hence the name).
Inside the scope we @import
the framework’s variables. Because at this moment there’s no value for each variable every variable is created and assigned its !default
value.
But here’s the gimmick. The variables are not global, but local. We can access them but they are not going to pollute the global scope, the one that will be later used to derive variables inside the framework.
In fact, when we want to define our variables, we want them global, and indeed we use the !global
keyword to signal SCSS to store them in the global scope.
Caveats
There’s one major caveat: you cannot use your own variables while you define them.
That means that in this file
%scope {
@import "bootstrap/variables";
$brand-primary: black !global;
@debug $brand-primary;
}
The @debug
statement will print the default value defined in bootstrap/_variables.scss, not black
.
Solution
Split variables in two parts:
%scope {
@import "bootstrap/variables";
$brand-primary: black !global;
@debug $brand-primary;
}
@debug $brand-primary;
The second @debug
will indeed correctly print black
.