Sass 7-1 Pattern using #use and #forward - sass

What's the correct way of setting up a Sass 7-1 Pattern using #use and #forward?
Example:
Sass 7-1 Pattern
Files:
./scss/abstracts/_variables.scss
$main-color: #222222;
./scss/abstracts/_index.scss
#forward './variables';
./scss/components/_card.scss
.card {
color: $main-color;
}
./scss/components/_index.scss
#forward './card';
./index.scss
#use './scss/abstracts/index' as *;
#use './scss/components/index' as *;
If #use and #forward were replaced by #import (which the docs say is deprecated) it would work but when using the new #use and #forward Syntax the card component which is imported after abstracts is not able to "see" the variables.
It works if abstracts/_index.scss is imported via #use into components/_card.scss.
If I were to use Sass 7-1 Pattern, is duplicating #use on multiple files across the 7 folders the correct approach? It seems like it kind of kills the need of a main Sass file importing all partials.
To whoever tries to enlighten me, I thank you in advance!

After consulting SASS #import documentation
Looks like it's being deprecated because it makes all imports global across the files from the point it's imported, making it easier to cause name-collision between multiple libraries.
You'll need to implement #use across your files but as said into #use
documentation about the main differences between #import and #use:
#use only makes variables, functions, and mixins available within the scope of the current file. It never adds them to the global scope. This makes it easy to figure out where each name your Sass file references comes from, and means you can use shorter names without any risk of collision.
And it's nice to keep in mind that:
#use only ever loads each file once. This ensures you don’t end up accidentally duplicating your dependencies’ CSS many times over.
So for your code to work just add to
#use "../abstracts/index" as *;
.card {
color:$main-color;
}

Related

When importing a file with #use then: Error Undefined Mixin

I have a entry file:
#use '_mixins';
#use '_default';
Now when I use a mixin in default.scss it throws the error:
Dart Sass failed with this error: Error: Undefined mixin.
Since #use is encouraged I don't understand why this doesn't work. If I write #import like in the olden days it works well:
#import '_mixins';
#import '_default';
This way all is nice and dandy.
How do I have to use #use and still have everything available?
That may be because the use of the new rule #use has an high impact to the way you build your project structure. Using of #usechanges it dramatically:
With #import you add a file and the mixins/vars/modules are ready to use in every sass file you load later on.
With #use you have to load the special sass file you need (i.e. '_mixins') direct in the file where you want to use it direct ... not to the main file. (Yes: loading the mixins/functions/variables-files to EVERY single partial file where you want to use them is the NEW INTENDED way to write sass.
And one more point to your using of #use: If you load by #use 'mixins you have to call the mixin in your file in the separated namespace #include mixins.yourMixinYouCall. If you want to load the mixins without separated namespace you can do: #use 'mixins' as *.
This changing to seperate namespace has some advantages. But indeed that is a really big impact and a very hard discussed theme about the new way to build your sass files.
We had this this week and before posting that twice you may have a look to this posting:
The #use feature of Sass

What is the correct method for setting up a SASS module "library" with #use to allow users to override library variables?

With the move to #use and explicitly local namespaced variables, it seems that in order to override an Sass "library's" variables it ends up leading to somewhat unsightly code. Is this expected, or am I missing something, being relatively new to Sass?
Previously, one threw caution to the wind and simply created global variables.
(And, yes, I know #import is still around for another couple of years, but I'm interested in future-proofing my projects.)
#import "_vars";
#import "vendor/cool_library";
Now, since #import is being deprecated we should move to #use.
#use "_vars";
#use "vendor/cool_library";
The only way I've seen in my searches to get variables overridden in the "vendor/cool_library" code is to either add #use "../_vars" to the cool_library file, or use:
#use "vendor/cool_library" with ($varname: "var_data");
Adding my vars file to the library is a no-go, since it would make it a pain to keep a clean submodule repo. So that's out, which leaves the "with()" option as the only apparent method?
But, with some libraries, that could lead to something like this:
#use "vendor/cool_library" with (
$line-height: 2.4rem,
$global-radius: .2rem,
$border-color: #bbb,
$font-sans-serif: "Roboto", Helvetica, Arial, sans-serif !default,
$font-serif: "Merriweather", Georgia, "Times New Roman", Times, serif,
$font-monospace: Menlo, Monaco, Consolas, "Courier New", monospace,
$font-base: $font-sans-serif,
$font-color: #222222,
$font-size: 22px ,
$font-size--lg: 48px,
$font-size--md: 16px,
$font-size--sm: 14px,
$font-size--xs: 10px,
. . . . And on and on... however many variables you need to override...
);
A variation on that is to call #use "_vars" before that but that only gains you the ability to set those with() variables with content from _vars and manage the variable content in the _vars file. The #use line would still look a mess as it does above.
I had hoped that since with() wanted a map, that I could just feed it a variable comprised of a map, but didn't seem to work when I tried it...
Alternatively, mixins could be used, but this would lead to reconstructing libraries like web frameworks in your own Sass files, which is not appealing.
So... am I missing something obvious (really hoping I am)? Or is this really the new way with #use modules?
Unfortunately it seems like the with() method you point to is the only way to go. I first tried to #use just the _variables.scss file and then set up my modified variables in the with() method, and then import the rest of the scss project below that, hoping that the variables would be taken into account. But that didn't work although it actually did compile.
Only by #using the entry point file in node_modules/cool_css_framework/main.scss I was able to modify the variables, like you indicated:
#use './../../../node_modules/cool_css/main.scss' with (
$link-color: #ffe88d,
$link-color-hover: #fff0b4,
$container-max-width: 1600px,
$etc ...
);
For this to work the original $variables have to be set with the !default flag:
$link-color: green !default;

Importing SASS variables with #use

I have been using LESS for years and am only just trying out SASS. I am having trouble with a very basic thing so I hope someone can help me out.
What I want to achieve is:
a file theme.scss (which is compiled into theme.css) contains separate components (for header, navigation, font etc.)
define variables for things like brand colours to be re-used in the components
I have got it to work with #import, but because that is (going to be) deprecated I want to use #use instead. According to https://sass-lang.com/documentation/at-rules/use that should be possible.
My file setup is like this:
|- style
|- _definitions.scss
|- theme.scss
|- components
|- component.scss
Then I have this in the files:
// theme.scss
#use 'definitions';
#use 'components/component.scss';
// _definitions.scss
$base-color: blue;
// components/component.scss
body {
background: $base-color;
}
But this doesn't work: my compiler (I'm using Prepros) just says it can't find the variable $base-color. :( Note: if I replace #use by #import it works just fine.
Help?
#import will never be depricated. Sass' devs are a sleepy bunch of ****. #use is only supported by Dart-Sass yet, I assume you are using Node-Sass, which most are. Therefor you need to use #import, which makes in most use cases no difference.
You can find the info right under the heading, its the first line, virtually everbody skips.

Overriding a large number of !default values with the #use rule in Sass/Scss

I've got an SCSS module with a whole lot of overridable !default variables, and I'm trying to make use of it in a project using the new #use rule instead of the to-be-deprecated #import. Overriding a large number of variables used to be pretty straightforward. As long as I imported my variables before importing the module they would take precedence over the !default values.
Now, if I try to #use the module in the global namespace like so:
$h1-font-size: 40px;
#use 'my-module' as *;
I get an error - SassError: This module and the new module both define a variable named "$h1-font-size".
Apparently the proper way to do this is to use the with keyword like so
#use 'my-module' with (
$h1-font-size: 40px
);
But now my $h1-font-size variable isn't available to other parts of my code. It's only scope is in overriding the value in the module. If I tried to assign it to something else, I'd get an Undefined variable error.
So that leaves me doing this
$h1-font-size: 40px;
#use 'my-module' with (
$h1-font-size: $h1-font-size
);
.foo {
font-size: $h1-font-size;
}
This is the exact effect I want - a variable that overrides my module and is still available for use elsewhere. However this syntax is cumbersome for passing a large number of overrides. If I want to pass say 30 overrides, I need 30 variable definitions and then 30 lines within the with block to explicitly pass in 30 values.
Update / Edit
The answer from #Miriam Suzanne below makes it apparent I have oversimplified my use case for the purpose of asking a question.
Our shop has a set of mixins and base styles we use on many projects and I have been attempting to turn them into a reusable module to reduce the copy/pasting. Something like Bootstrap, with a lot of overridable !default values. Very much a work in progress, but the module itself can be seen here: Derekstrap.
This module is being developed in tandem with the first project we're actually using this on. The !default values are all in the module, and I would like to override them in my project.
Right now I have code in my main.scss (the primary entry point for stylesheets) that looks like this.
$base-font-family: 'Raleway', Tacoma, sans-serif;
$base-line-height: 1.8;
#use '~#evanshunt/derekstrap/index' with (
$base-font-family: $base-font-family,
$base-line-height: $base-line-height
);
I like to structure my scss files pretty granularly so ideally those variables would actually live in another file, and also be available to other files in the project.
If I do something like the following, where both my variables file and the module have a $base-font-family variable defined, it compiles but by variables don't override the module.
#use 'variables' as *;
#use '~#evanshunt/derekstrap/index' as *;
I suspect at this point the answer is that I need to add #use and/or #forward statements to my variables file(s), but it's breaking my brain a bit to figure out if I'll be able to successfully override different parts of the module in different parts of my project code. The suggestion above of just one variables file is also a bit of a simplification. How important will the order of the #use statements in my main.scss become if I am modifying the same namespace across the files being #used?
I get the purpose of scoping and passing values explicitly but I'd love some sort of shorthand or trick to accomplish this without writing so much extra code. As mentioned in my Sass feature request ticket I was hoping for a simple shorthand for passing one context into another like:
#use 'variables' as variables;
#use 'module' with variables;
This doesn't seem possible and I'm wondering if I need to completely rethink the way I structure SCSS files to achieve what I want. What can I do here?
I'm not entirely clear what you mean when you say the variable "isn't available to other parts of my code". The entire purpose of #use is to make those variables available – but inside a namespace. So you can do:
#use 'module' with (
$h1-font-size: 40px
);
.foo {
font-size: module.$h1-font-size;
}
or remove the namespace using as *:
#use 'module' as * with (
$h1-font-size: 40px
);
.foo {
font-size: $h1-font-size;
}
or forward the module while also adding configuration, to use in other files:
#forward 'module' with (
$h1-font-size: 40px !default
);
or both at once:
#forward 'module' with (
$h1-font-size: 40px !default
);
#use 'module';
.foo {
font-size: module.$h1-font-size;
}
I think that likely covers all the potential use-cases you might have for defining the same variable again?
In response to updates on the question…
Using the module syntax means that there is no global scope across files. That means every individual file needs to explicitly #use any-and-all required modules for that file. That means:
You will need to #use '~#evanshunt/derekstrap/index' (or similar) at the top of every single file that should have access to Derekstrap mixins, variables, or functions
Since index files are imported automatically, and default namespaces are generated based on the path, I'd probably #use '~#evanshunt/derekstrap' instead…
Namespaces are always local to the importing file, so you can #use '~#evanshunt/derekstrap' in one place (creating a derekstrap namespace), and #use '~#evanshunt/derekstrap' as * in another (removing the namespace), and still #use '~#evanshunt/derekstrap' as derek in a third place.
The goal of !default "configurable" variables is that they can be pre-configured before any compilation. That has to happen "first time" a module is used. If you want to do that in a file all to itself, you can:
// _config.scss
#use '~#evanshunt/derekstrap' with (
$h1-font-size: 40px,
);
// main.scss
#use 'config'; // keep this path first...
#use 'anything-else';
// _anything-else.scss
#use '~#evanshunt/derekstrap';
// derekstrap.$h1-font-size will already be configured by config.scss
Those values can only be pre-configured once - meaning the compilation will start with the configuration value. But you can update those variables any time in any file:
// _anything-else.scss
#use '~#evanshunt/derekstrap';
derekstrap.$h1-font-size: 32px;
// The $h1-font-size now has a value of 32px
// for any file to uses derekstrap later in the compilation
On my site, I use a combination of both approaches - just inside my one color config file:
https://github.com/mirisuzanne/mia/blob/main/src/scss/config/_color.scss
I have the #use … with pattern for configuring cascading-color-systems, but I also use accoutrement tools without configuration, and then override tools.$colors.
You can get quite a bit more complex by adding in layers of forwarding, but this should get you started. Remember:
The variable $h1-font-size exists inside the '~#evanshunt/derekstrap' module.
That module can used multiple times, and given a different namespace each time. The module is always the same module, but namespaces are local.
Pre-compile "Configuration" can only happen the first time a module is used, but you can continue to override those variables at any time.
Much like before, there is only one Derekstrap $h1-font-size variable, and it takes on different values over the course of compilation. what's new with modules is only that you need to explicitly #use Derekstrap in every file that wants access to that variable.
I understand it can take some getting used to, but in the end I've found my code is much more organized. You might want to set up a test project and play around, until the mental model makes more sense.

Can SASS mixins and variables sit in different files?

Hi I am trying to organise my sass files into separate chunks on a project using GULP. However when I import my mixins and variables in separate files:
File:variables.scss
//first import variables that might be used throughout all the other files
#import "common/_variables.scss";
File:mixins.scss
Mixins
#import "common/_mixins.scss";
Then try to access those mixins from other files for example
File:buttons.scss
#import "common/_buttons.scss";
I get the following errors when running gulp sass:
throw er; // Unhandled 'error' event
no mixin named 'foo'
or
undefined variable 'foo'
In spite of the mixins/variable being defined in the variable.scss and mixins.scss files. So gulp interrupts the task half way though and the stylesheet is not created.
Is there a rule in SASS that means the variables and mixins must all be imported in the same files using them? If this is the case, it is a problem as I have a lot of files I would like to keep separate and not have to keep importing both mixins and variables inside them.
The short answer is you dont have to import them into everyfile that might include them. However there does need to be a relationship somewhere, and how you do this depends on whether or not you are looking to build one single final CSS file - or a series of individual files.
If you want the former, you could consider having a master import file that does little more than import all the appropriate *.scss files, and have this be the main SASS file your gulp build script looks to compile.
You'll need to take care to import files in the correct order - so no importing a file that uses a mixin before the file that defines the mixin has been imported it's self.
So for example - personally I work with a main.scss file that is structured as
#import "common/_variables.scss";
#import "common/_mixins.scss";
// Now import the files that might use these vars/mixins
#import "common/_buttons.scss";
which is built via gulp to create css/main.css.
Should you want a series of CSS files - i.e. buttons.css, type.css, layout.css etc - then you would need to have the appropriate variables and mixin #import declarations in each file that calls them
I found the best solution for me with pre-concating SASS files with GULP.
Like in this example for STYLUS
gulp.src(config.projects.css.source)
.pipe(sourcemaps.init())
.pipe(mktimecss(__dirname+'/web'))
.pipe(concat('styles'))
.pipe(stylus({
'include css': true,
use: [nib()],
// compress: true,
linenos: false
}))
...... and so on .......
It is most definitely possible to have your mixins and variables in different files. In fact, I'd recommend any mixins/variables that you find yourself reusing, you should take a more global approach with them instead of importing them into individual files.
One of the best ways I've seen for organizing a directory of Sass files is called The 7-1 Pattern and makes the entire structure of styling so much easier to understand and build upon.
The most applicable piece of this organizational strategy to your question would be creating a main.scss file that imports every one of the files you plan on using. Make sure you're careful about the order that you import, though. You can't import a file that uses mixins or variables from a file imported after it.

Resources