In my Nuxt app I load all my SASS thus:
css: [
'~assets/scss/main.scss'
],
It works perfectly, except when I'm trying to use some SASS variable from within a component.
<style lang="scss">
.container {
background-color: $white;
}
</style>
In this case I get this error message:
SassError: Undefinied variable: $white
Yet, all of the SCSS contained in the SASS file where the variable is defined works throughout the app.
It is as if the app as a whole knew these files, but each individual component doesn't.
What's going on?
Most of the other answers don't take into account that Nuxt.js hides all the Webpack setup and forces you to do everything through nuxt.config.js.
My guess is that Webpack isn't compiling all the SCSS declarations together and therefore can't find the variable.
It's been a few months since I had this issue so things may have changed but here goes...
Make sure you have the correct Node packages installed (Nuxt DID NOT do this by default for me) npm i -D node-sass sass-loader
Add your CSS & SCSS files to the css: [] section of nuxt.config.js Order matters here so make sure things like variables are added before things that use them if you have separate files.
If you're using layouts (I think that's the default Nuxt setup) make sure that layouts/default.vue has a <style lang="sass"></style> block in it. If I remember correctly this can be empty but had to exist. I only have one layout but it may need to exist in all of them.
If all that seems like too much of a pain, there's a Nuxt Plugin that takes most of the work/management out of that process. Nuxt Style Resources Module
The confusing part is that:
styles from scss files CAN be loaded like this
//nuxt.config.js
css: [
'~assets/scss/main.scss'
],
//global scss file
$varcolor: black
h1{background: $varcolor}
BUT
the variables inside CAN NOT be used inside a component
//inside component
.component {background: $varcolor} // DOES NOT WORK
I also suggest the use of the nuxt style resource module:
https://github.com/nuxt-community/style-resources-module
new founded solution, checked and it's work. Founded here
add #nuxtjs/style-resources
export default {
css: [
'vendor.min.css'
],
modules: [
'#nuxtjs/style-resources'
],
//You will have to add this new object if it doesn't exist already
styleResources: {
scss: ['./assets/scss/main.scss'] // here I use only main scss with globally styles (variables, base etc)
},
}
it's strange, but if u change tilda (~) to dot(.), it's help for someone
from css: [ '~assets/scss/main.scss' ] to css: [ './assets/scss/main.scss' ]
this solution finded here
Us should either load the scss in your component
<style lang="sass">
#import 'path/to/your/_variable.scss'; // Using this should get you the variables
.my-color {
color: $primary-color;
}
Or adding the following to you to your vue.config.js
module.exports = {
css: {
loaderOptions: {
sass: {
data: `#import "#/pathto/variables.scss";`
}
}
}
};
Ref:
SassError: Undefinied variable: $white
Each <style lang="scss"> is compiled individually. You need to #import the file which defines $white into your component before the parser knows what $white means.
This is why most frameworks keep their variables in a _variables.scss file which is imported in all the other SCSS files/contexts.
The _variables.scss is not even loaded in the page, because in most cases it doesn't actually contain any rules. It only contains variable definitions which are imported into other .scss files, which output .css.
Ref:
Yet, all of the SCSS contained in the SASS file where the variable is defined works throughout the app.
If you import an SCSS file in your vue.config.js the output will be an ordinary <style> tag. Its contents will be generated at compile/build time and will result into some CSS (which apply to the entire document).
Unless specifically imported into the component SCSS, (using an #import command), the compiler will not know what $white means.
There is an important distinction to make between compilation context and browser context. Compilation happens at compile time (most likely in node-sass). Browser context is the actual browser, which only understands the CSS resulted from compilation.
How does Vue only apply style rules to the parent and not to the children with the same class? That's achieved by scoping.
It means applying a custom data-v-{key} attribute to all selectors in the generated <style> tag and to all elements the style should apply to.
See this example and inspect it using your web console: https://codesandbox.io/s/vue-template-ge2hb
It produces this markup:
As you can see, the scoped CSS has an extra [data-v-763db97b] added to the selector, which means it only applies to elements having that data attribute.
Related
Question
How can I globally import variables.scss 1. without importing them in every file and 2. by referencing instead of duplicating them in my build?
Setup
I use Vue2 and laravel-mix and I have index.scss imported in my main.js
variables.scss
$--test: #ff0000;
index.scss
#import 'variables';
.dashboard-title {
color: $--test;
}
This colors the title red. But when I try to do the same thing inside of the component, it doesnt work:
<style scoped lang="scss">
.dashboard-title {
color: $--test;
}
</style>
This doesn't work, but I proved that index.scss is global in my first example. How is
variables.scss not global, when I import it in my global index.scss?
I can fix the error by importing the variables file in the component, but by doing this, I essentially duplicate the whole variables.scss file every time I import it in a vue component.
I found this out by analyzing my bundle with a webpack bundle analyzer, this is the output:
webpack bundle analysis image (all blue crossed parts are increased in size because the variables file is imported, this isn't a big problem now, but this will exponentially increase my bundle size with time)
It would reduce my bundle size by atleast 20% right now...
How can I reference the variables.scss file instead of duplicating its content?
What I've tried:
https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/ (I wasn't able to "migrate" this to a laravel-mix config)
I've also tried using purgeCss to remove duplicate css, this just completely messed up my styles but reduced the bundle size by 50% lol
Adding this to the webpack.mix.js
mix.webpackConfig({
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
//this might be "data" or "prependData" depening on your version
additionalData: `#import "./resources/js/styles/variables.scss";`
}
}
]
}
]
}
})
This does make the variables global, but imports(duplicates) them for every vue component, even if they aren't being used.
Edit: This only is an issue, when the imported file is relatively big. In my project, the imported file itsself imported a theme scss (to get access to the themes variables), which ultimately copied this whole thing everywhere I needed the variables.
I fixed this by defining my custom variables in a seperate file and using those variables in the "overwriting-variables" file, something like this:
custom-variables.scss
$red: #ff0000;
overwriting-variables.scss
import 'theme.scss'; //this bloated my project
import 'custom-variables';
$--theme-red: $red
And when I needed this theme color in my vue components I just imported the custom-variables.scss instead of overwriting-variables.scss.
This does fix my bloating issue, but doesn't fully solve the problem, I still have multiple instances of the custom-variables.scss in my project, it just doesn't matter (yet) because its really small. So I'd be still happy to hear about other solutions!
If you import every .scss in your index.scss, then every variable should work. Try this in your vue.config
css: {
loaderOptions: {
// by default the `sass` option will apply to both syntaxes
// because `scss` syntax is also processed by sass-loader underlyingly
// but when configuring the `data` option
// `scss` syntax requires an semicolon at the end of a statement, while `sass` syntax requires none
// in that case, we can target the `scss` syntax separately using the `scss` option
scss: {
prependData: `#import "#/style/index.scss"`
}
}
},
So I got it working with laravel-mix like this:
mix.webpackConfig({
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
//this might be "data" or "prependData" depening on your version
additionalData: `#import "./resources/js/styles/variables.scss";`
}
}
]
}
]
}
})
Not sure yet if this prevents the duplication though
Edit: It does not prevent duplication, it does increase the bundle size in every vue component. So I now have a 150% bigger bundle, because the variables file is imported in every single vue component. Even if the variable isn't even used.
I use Symfony + Webpack Encore and try to split styles into "layout" and "page-based", but only to make development more comfortable: I still want to compile one css file for them (in fact, there is a limited number of such css files, each one for block of pages, but for easier understanding let's assume only one is necessary). So I do like this:
_global.scss
// ... bootstrap variables redefenition here
#import "~bootstrap/scss/bootstrap";
// ... common functions, mixins, font-face definitions here
.my_style1 {
padding-left: 12px;
padding-right: 12px;
}
.my_style2 {
#include make-container-max-widths();
}
app.css
#import "_global"
// other styles here
During the compilation (require('../css/app.scss'); only in my app.js) styles are ordered: [ global, bootstrap, app ] and I don't understand why. I mean, if you use them as:
<div class="container my-style1"></div>
container's padding will override defined in my-style1.
The most strange thing is that in dev app.css they are ordered as expected (my-style is lower than container), but in prod not (container is lower than my-style). When you work in dev (and Chrome display non-compiled styles, you also see that _grid.scss overrides _global.scss)
Sorry for this quick self-answer, I've really spent a lot of time before asking, but after it found the solution quickly. Hope, can save smb's time.
You should simply add other styles to app.js. This way they will recompile on any file change (in previous example they recompile only on app.scss change) and the order will become correct:
app.js
require('_global.scss');
require('app.scss');
I do already have SCSS variables defined in src/styles/settings/_variables.scss and I am importing them into src/styles.scss, but still these variables aren't available for every single component.
Is there any way to make a global file which holds all SCSS variables for the rest of my components? Because writing #import in every single component .scss file it is very frustrating, especially if I have many nested components.
I know, there is a lot of similar questions, but it seems like they're all outdated and do not relate to the recent versions of Angular.
I use Angular 7.3 with CLI.
You just need to add a little more config, so where you are declaring your global variables, you need to wrap it up in :root{}. So in src/styles/settings/_variables.scss.
:root
{
--blue: #00b; // or any global you wish to share with components
}
Then when you use them in the SCSS you will need to access them like so.
.example-class {
background-color: var(--blue)
}
To add to this regarding comments, this method can use mixins, #media and keyframes and is not limited to just colours / font. That was an example.
From my understanding you need a global file src/assets/style/global and then to import each scss file into there where you are defining them like so.
#import 'filename';
If you dont want global variables to be used in within a component look when you have the globals working. Look into ViewEncapsulation, as this can be used to ignore them.
Is there any ways to make global file with scss variables available for all components?
Without importing global file everytime in each component, you want those sass variables been available, it's not possible.
The way it works in SASS, if using partials to better organize code, you can apply #import directive for referencing. So if there're some sass variables in shared/_variables.scss:
$lightslategray: #778899;
$darkgray: #A9A9A9;
and these variables need to be used in another stylesheet, stylesheet with them must be #import-ed into it firstly:
// Shared
#import "shared/variables";
.content {
background: $lightslategray;
}
In Angular it works in a similar way (related referencing external stylesheet). So if you need some sass variables, mixins or functions to be used by a particular component.scss, there is no other clean way, but to reference them in that component.scss using #import directive. To ease the task, you can create a file src/_variables.scss and use syntax like this in your component.scss:
#import “~variables.scss”;
step one : go to custom scss file (shared/css/_variable.scss) and write this part
:root{
--color-text: red;
--color-btn-success: green;
}
after go to style.scss (this is main file) and import this file :
#import './shared/css/Variables';
now you can use variables in all components with this Syntax:
.sample{
color : var(--color-text);
}
Easily possibe to access sass style(s) from a global file with two steps.
Add folder path of the style files to includePaths array in angular.json file.
Import style file by file-name in any component.
let say your files and folder structures is as follows: src > my-styles-folder > var.scss
angular.json
"architect": {
"build": {
...
"options": {
"stylePreprocessorOptions": {
"includePaths": [
"src/my-styles-folder" // add path only, do not include file name
]
},
"styles": [
...
]
}
...
}
}
some-component.scss
#import "var"; // var.scss
mat-toolbar {
height: $toolbar-height;
}
In angular 8 work for me.
In your _variable.scss file you have to add:
:root{--my-var:#fabada}
After that go in your angular.json and add this in "styles":
{"input":"yourPath/_variables.scss"}
I have a weird issue with Vue.js CLI production bundling where I cannot quite pinpoint the root cause and I appreciate some help.
I have a Vue CLI 3 application with the following (relevant extract) in my main.js:
// Bootstrap
import "#/assets/bootstrap/bootstrap.scss";
import "bootstrap-vue/dist/bootstrap-vue.css";
import BootstrapVue from "bootstrap-vue";
Vue.use(BootstrapVue);
// Toastr
import Toastr from "vue-toastr";
import "vue-toastr/dist/vue-toastr.css";
Vue.use(Toastr, {
defaultPosition: "toast-bottom-right"
});
Running this in my dev environment (npm run serve) CSS works fine.
When I run this after the production complilation (npm run build) some classes... are not applied and I cannot reason why. Given that the only difference I can see is the bundling process, I'm incline to look for an issue in that direction.
I customized the bundling as follow (relevant extract) in my vue.config.js:
cacheGroups: {
icons: {
name: "icons",
test: /[\\/]node_modules[\\/](#fortawesome)[\\/]/,
chunks: "all",
priority: 3
},
vendors: {
name: "vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "all",
priority: 1
}
}
And, as result, my CSS bundled are correctly created as follows:
a vendor chunk that includes the Toaster CSS.
This includes a 'toaster' class and a 'toaster-info' class (this latest has just a background-color)
a app chink that include my custom built bootstrap CSS.
As much as the bootstrap files are in the node_modules folder and, as such, they should go in the previous chink, they get in here because I'm compiling them as import in a the SASS file above that actually comes from by code.
This includes a 'toaster' class again.
Now, what I can see is that:
both chunks appear to be loaded by the browser
the markup correctly uses the 'toastr toastr-info'
only the 'toaster' class from app (bootstrap) is applied
the 'toaster' and 'toaster-info' class are totally ignored by the browser and the background color from 'toaster-info' is not applied
I tested this with several browsers to exclude any specific browser weirdness.
Browser computed styles shows that the classes are "excluded" for some reason I don't understand (with "excluded" meaning are in the style tree but strikethrough).
Can anyone help me understand why this is happening?
Thank you.
This is purely a CSS problem.
Since your app and vendor CSS defines a .toast class style, whatever is loaded last is given the highest specificity.
I'm assuming all you're trying to do is change the default .toast background colour to white (instead of black) but you want to leave the more specific classes like .toast-info, .toast-success, etc as they are.
To do so (without altering the vendor files), you can change your .toast definition to...
.toast:not(.toast-info),
.toast:not(.toast-success),
.toast:not(.toast-warning),
.toast:not(.toast-error) {
background-color: rgba(255, 255, 255, .85);
}
If you're using a pre-processor like Sass (which I think you are), this can be written more succinctly.
Ideally, the vendor files should have defined their styles like...
.toast {
background-color: #030303;
}
.toast.toast-success {
background-color: #51a351;
}
which would give the "info", "success", etc styles a higher specificity than the .toast class but it doesn't look like they do. Perhaps you could create a pull-request 🙂
I'm wondering if there's a grunt plugin that can compare two files and remove duplicates from one of them.
Example: if both blog.css and main.css contain the rule .button { color: red; } I'd like to remove that rule from blog.css. (main.css should always remain unchaged)
Background:
I've got two LESS-bundles, main.less and blog.less, which I compile into main.css and blog.css
The idea is that my site should load main.css on every page. On blog pages I'll load both both main.css and blog.css.
The problem is that these LESS-files share a few "utility"-files (with variables, mixins and some common classes like .button)
So I end up with blog.css containing duplicates of some rules which are already defined in main.css, and I'd like to get rid of those duplicates to reduce file size.
Found it
https://www.npmjs.com/package/grunt-csscss
csscss: {
dist: {
src: ['css/x.css', 'css/y.css']
}
}
Allthough in my case the solution was actually much simpler. Turns out LESS now has import ("reference") which will import a file to use as a dependency only, without outputting any of it's css.
So now I can use import ("reference") commonstuff.less in blog.less and thus none of the styles from commonstuff.less will be output to blog.css! :)