Is there a way to combine #use and #import in SCSS? - sass

I've got a mechanism that uses one of the following 3 files, depending on what UI mode (light, dim or dark) is selected. There are only SCSS variables defined in those files.
_variables_light.scss
$primaryColor: red;
_variables_dim.scss
$primaryColor: blue;
_variables_dark.scss
$primaryColor: black;
Now I've got the following file which should work regardless of which UI mode is selected.
_styles.scss
.navMenu {
background: $primaryColor;
}
I used to solve this using the following file (example for light mode) which would then be compiled to CSS.
light.scss
#import 'variables_light';
#import 'styles';
I want to replace #import by #use as the former is deprecated but the only way I could make it work is
#use 'variables_light';
#import 'styles';
Now if _styles.scss contains #import statements again, the variables from variables_light can not be used everywhere. Is it even possible to use #use over several layers of imports?

Related

How to access variables imported with #use method in sibling imports

This is my main.scss file.
// Main
// Variables (variables.scss)
#use "../../../style/variables" as *;
// Custom Normalize (normalize.scss)
#use "../../../style/normalize";
// Site header (header.scss)
#use "sections/header";
Variables defined in variables.scss are not accessible in normalize.scss & header.scss. Is there a way to use them inside those files without separate #use 'variables' statement?
Or would you just use separate #use method for each file? I am a newbie, so I don't know what's better.
You have two options.
1. #import
If you use #import except #use for imports, then you can access variables defined in variables.scss inside the normalize.scss & header.scss.
But it has a disadvantage. It is difficult to trace where your variables and mixins are coming from because #import enables an endless chain of imported files. It also allows for overlap and makes it difficult to trace back why your perfect css breaks. This is a problem especially with complex file structures and projects with multiple contributors and global libraries, which is why #import is no longer recommended by Sass.
2. #use
It also works like #import to break our stylesheet into smaller sections and load them inside other stylesheets. The key difference is how you access the original files' members. To do this you will need to refer to the namespace of the original file.
Here's an example of simple SCSS partials.
_variables.scss
$primary_color: #000;
_header.scss
#use 'variables';
.header {
color: variables.$primary_color
}
If you need more info read this article - https://www.liquidlight.co.uk/blog/use-and-import-rules-in-scss/
Let me know if you need further support.

How can I provide configuration variables to a Sass/SCSS file before including it?

I'm migrating a Stylus library to SCSS since Angular 12 has deprecated Stylus and I'm in that impacted 0.3%. I've run into something we were doing that I'm not sure how to convert to SCSS—maybe it's impossible.
Let me lay this out simply: I work on several projects that all use loads of the same styles, so we put those styles together into one style sheet in its own NPM package. We can then just grab #import '#company/design/styles'; and suddenly we've got all of our regular styles and variables and mixins available in the project, or we can import #import '#company/package/styles/common'; for just the variables and mixins.
The thing is, our projects might need to configure the library before we import it. Suppose the library contains this bit:
// #company/package/styles/_forms.scss
input:invalid {
background: url('/assets/input-error.svg') no-repeat center right;
}
Not every project will have /assets/input-error.svg at that exact location. Maybe one of my projects has to use /subfolder/static/input-error.svg.
I could include this then overwrite input:invalid { background-image: url(...) } to supply it with the correct location, but there may be many references to this particular file and many other assets on top of that to correct. So we instead, in our Stylus library, we introduced an $asset-input-error variable that points to /assets/input-error.svg by default and did something like this:
// #company/package/styles/_forms.scss
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
// the local project
$asset-input-error: '/subfolder/static/input-error.svg';
#import '#company/package/styles';
The above is heavily simplified and isn't actually legitimate SCSS, but I hope it conveys what we're trying to do: we want to set up what are effectively environment variables in our SCSS, include the common style sheet, and have it use those variables.
The thing is, I'm not sure what the legitimate or idiomatic approach is to do this in SCSS. Unlike Stylus, which has a global scope for its variables, SCSS would have me #use '../config'; and reference config.$asset-input-error, and from outside the library there's no way I see to change the configuration to point that asset to a different location. I'm sure SCSS has a way for me to do this, but I'm not sure what it is. Do I convert the entire library into a giant mixin to which I pass optional configuration? Do I do something with global variables? Something else?
How can I provide variables to my SCSS style sheet to configure it as part of including it in a project?
Ultimately the end goal here is just to be able to say to the library things like: “the assets to reference are here” (very important) or “the error color is this in this particuilar project” (less important).
Using #import
You can use global variables declared before the #import as you stated.
SCSS Documentation for this method
#company/package/styles/_forms.scss
$asset-input-error: '/subfolder/static/input-error.svg' !default;
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
#company/package/styles/styles.scss
#import 'forms';
local.scss
$asset-input-error: '/different/path/input-error.svg';
#import '#company/package/styles';
CodeSandbox Demo
Using #use [...] with
You can also hop aboard the #use train if you prefer to future-proof your library.
SCSS Documentation for this method
SCSS Documentation for using mixins
SCSS Documentation for configuring forwards
#company/package/styles/_forms.scss
$asset-input-error: '/subfolder/static/input-error.svg' !default;
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
#company/package/styles/styles.scss
#forward 'forms';
local.scss
#use 'styles' with (
$asset-input-error: '/different/path/input-error.svg'
);
Sadly CodeSandbox and StackBlitz don't support dart-sass, so I don't have a live demo for this but I tested it on the latest version of sass from npm.

Order of importing when overriding variables of Bootstrap 5

At Bootstrap 5 docs they say:
Variable overrides must come after our functions, variables, and
mixins are imported, but before the rest of the imports.
But, at the same time, they provide two examples with variable overrides coming after only their functions, whereas their variables and mixins are imported later.
Here is a copy of the contradictory examples contained in the same document:
// Custom.scss
// Option B: Include parts of Bootstrap
// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
#import "../node_modules/bootstrap/scss/functions";
// 2. Include any default variable overrides here
// 3. Include remainder of required Bootstrap stylesheets
#import "../node_modules/bootstrap/scss/variables";
#import "../node_modules/bootstrap/scss/mixins";
// 4. Include any optional Bootstrap components as you like
#import "../node_modules/bootstrap/scss/root";
#import "../node_modules/bootstrap/scss/reboot";
#import "../node_modules/bootstrap/scss/type";
#import "../node_modules/bootstrap/scss/images";
#import "../node_modules/bootstrap/scss/containers";
#import "../node_modules/bootstrap/scss/grid";
// 5. Add additional custom code here
and
// Required
#import "../node_modules/bootstrap/scss/functions";
// Default variable overrides
$body-bg: #000;
$body-color: #111;
// Required
#import "../node_modules/bootstrap/scss/variables";
#import "../node_modules/bootstrap/scss/mixins";
// Bootstrap and its default variables
// Optional Bootstrap components here
#import "../node_modules/bootstrap/scss/root";
#import "../node_modules/bootstrap/scss/reboot";
#import "../node_modules/bootstrap/scss/type";
// etc
What would be the correct way?
The magic of Bootstrap is that all variables are declared as !default. (The library does, you shouldn't.)
Normally when you assign a value to a variable, if that variable already had a value, its old value is overwritten. But with Bootstrap, a Sass library, variables assigned by you will not overwritten if they were not declared !default.
See: https://sass-lang.com/documentation/variables#default-values
Generally speaking it does not really matter where you declare your variables as long as CSS is not generated yet - and if you've set variables, they will not be overridden.
Having said that - it's best practice to set yours after functions ... but before root and the other CSS generation SCSS.
The $utilities variable is the exception to that. If you declare a value to that you'll break the utilities/api CSS generation. Rather manipulate that map with the Utilities API (See https://getbootstrap.com/docs/5.0/utilities/api/) after you've also imported variables, mixins & utilities.
I suppose, looking at this with a different lens, the Bootstrap authors should be more consistent with their docs, or elaborate on inconsistencies - regardless, drop them an "issue" to let them know: https://github.com/twbs/bootstrap/issues
I agree that this is slightly misleading..
"Variable overrides must come after our functions, variables, and
mixins are imported, but before the rest of the imports."
This statement is true, if you're referencing any of the Bootstrap variables in your overrides. For example...
// Required
#import "../node_modules/bootstrap/scss/functions";
#import "../node_modules/bootstrap/scss/variables";
#import "../node_modules/bootstrap/scss/mixins";
// Variable overrides (referencing other Bootstrap vars)
$body-bg: $red;
$body-color: $gray-800;
// Optional Bootstrap components here
#import "../node_modules/bootstrap/scss/root";
#import "../node_modules/bootstrap/scss/reboot";
#import "../node_modules/bootstrap/scss/type";
// etc
overrides example referencing Bootstrap vars
But the example in the docs is correct (and works) because it's not referencing any Bootstrap variable in the overrides...
// Required
#import "../node_modules/bootstrap/scss/functions";
// Default variable overrides
$body-bg: #000;
$body-color: #111;
// Required
#import "../node_modules/bootstrap/scss/variables";
#import "../node_modules/bootstrap/scss/mixins";
// Bootstrap and its default variables
// Optional Bootstrap components here
#import "../node_modules/bootstrap/scss/root";
#import "../node_modules/bootstrap/scss/reboot";
#import "../node_modules/bootstrap/scss/type";
// etc
overrides example not referencing Bootstrap vars
Bootstrap 5 is still in Beta so the docs are still a WIP. Currently there are several open issues relating to variables (example). As far as the misleading phrase, I would wait for the release version since they're talking about separating base vars from the other vars.

Keep #import at the end of CSS after SCSS compile

We need to put #import at the end of my CSS file. For example:
SCSS:
#import "reset.css";
Body {
font: 0.8em arial;
}
#import "customation.css"
compile to:
#import "reset.css";body{font: 0.8em arial;}#import "customation.css"
but after compile it changed the #import order and CSS file will be this:
#import "reset.css";#import "customation.css";body{font: 0.8em arial;}
It's very important for us to keep #importing the custom.css file at the end for our customization. We can't put the #import to CSS file manually because SCSS file will be changed and CSS file will be rewritten.
Any suggestion?
You can't. Sass is smart enough to know that #import declarations must be at the beginning of the file so it rewrites it for you to be valid.
The #import CSS at-rule allows to import style rules from other style sheets. These rules must precede all other types of rules, except #charset rules; as it is not a nested statement, it cannot be used inside conditional group at-rules.
https://developer.mozilla.org/en-US/docs/Web/CSS/#import
If this is not acceptable, you'll need to use multiple link declarations (which are arguably better anyway for the user).
Are you using #import for any particular reason? There are performance impacts, and no major use case anymore.
It would be better if you used Sass's #import to concatenate the file instead, this would also allow you to import in the order you want and rely on the cascade.
#import "reset";
Body {
font: 0.8em arial;
}
#import "customation";

How to separate ie hacks code from main scss file

I am using sass and compass for my project. However it provides a very cool mixins for i.e hacks.
For eg:
.display { #include inline-block; }
will execute
.display { display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
zoom: 1;
*display: inline;
}
But I want to separate all the ie hacks code to different css file such as ie.css and want it to place inside ie conditional comments, so that my css file would be w3c compatible.
Is there any idea to overcome this problem.. Any help will be greatly appreciated. Thanks in advance.
Just create two "parent" sass files. e.g. screen.scss & ie.scss
Put all of your actual sass into separate "child" scss files.
Then in your "parent" files only have import statements.
EXAMPLE:
SCREEN.SCSS
//Global
#import "vars";
#import "mixins";
//Base
#import "reset";
#import "base";
#import "layout";
//MODULES
#import "modules/module_a";
#import "modules/module_b";
IE.SCSS
#import "vars";
#import "mixins";
#import "ie_hacks";
This lets you import all of the sass dependant "child" files like any variable or mixins. It also lets you keep all the ie stuff in a separate css file. ie.scss is obviously going to be markedly smaller than your main screen.scss

Resources