VueJS and SASS - Import and use SASS variables in script - vue.js

I'm currently working on a VueJS project with a SASS integration, and I would like to get access to the SASS variables inside the script part of the component to make dynamic style calculations. I saw examples of this with pure JS, and I'm trying to translate that to a VueJS component.
So far, I've been able to integrate SASS variables in the style part of the Vue file, but I can't get the variables imported into the script of the component.
I tried the following:
Exporting the variables from my scss file:
:export {
primaryColor: $primary;
}
And then import the file in my component:
import variables from '#/assets/scss/main.scss'
And access it like so: variables.primaryColor
But I can't get it working, variables is always an empty object with no value inside. I'm using Nuxt with the packages node-sass and sass-loader installed. Any help would be appreciated!

If you want to just access the variables from a SASS file you can add this to configureWebpack in your vue.config.js file:
module:{
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
}]
}]
}
Then let's say that you have a file containing your SASS variables called style.sass that looks something like this
$btn-color: #D75893
:export
buttonColor: $btn-color
In your <script> tag you can import your SASS variables file like so
import styleInfo from 'style.sass';
and access your variables like so
console.log("The button color is", styleInfo.buttonColor);
Note: It is not possible to dynamically change SASS variables at run time as the variables are compiled.
Sources: This article from HashRocket and This article from itnext

Related

Symfony: How to Import a Sass File into Every Vue Component

I am currently struggling with this. In my Symfony project I have a _variables.scss file, where I keep my global variables (e.g. colors).
This is included in my main scss file like this #import "variables"; - which works fine. Now I also use VueJs in my project and I would like to use my global variables inside Vue components. Now one way to achieve this is just importing the variables.scss itself:
//CustomButton.vue
<template>
...
</template>
<script>
export default {
name: "CustomButton"
}
</script>
<style lang="scss" scoped>
#import "../../scss/_variables.scss";
//able to use variables here
</style>
However with a growing project and different paths, this seems to be unnecessary work. What I'd like to achieve is to load the variables.scss into every Vue component automatically.
This is quite well explained here: [css-tricks.com - How to Import a Sass File into Every Vue Component in an App][1]
Sadly this does not work in my case (I think the vue.config.js is ignored completly) - the variables are still not usable inside the Vue component. I also tried to add the JavaScript into my main js file - where I load Vue - however this seems to break stuff (some module exception).
Is there any specific way to achieve this with symfony?
PS: I am using Symfony 5, Sass-Loader 9.0.1, Vue 2.6.12, Vue-Loader 15, Vue-Template-Compiler 2.6.12
[1]: https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/
Solution 1:
//webpack.config.js
.enableSassLoader(options => {
options.additionalData = `
#import "./assets/scss/_variables.scss"; //path.resolve is not working in my case to import the absolute path
`
})
```
Webpack config:
module.exports = {
...
module: {
rules: [
{
test: /\.scss$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'sass-loader',
options: {
data: `
#import "functions";
#import "variables";
#import "mixins";
`,
includePaths: [
path.resolve(__dirname, "../asset/scss/framework")
]
}
}
]
},
]
}
this config imports 3 files: "_functions.scss", "_variables.scss", "_mixins.scss" from folder"../asset/scss/framework" and no need to use #import "../../scss/_variables.scss"; every time in your vue components. Maybe, you will adapt this webpack config to your needs or will get some idea to resolve your issue.
This is what you need c:
Since you are using Encore, modify your webpack.config.js, add this code
var Encore = require('#symfony/webpack-encore');
Encore.configureLoaderRule('scss', (loaderRule) => {
loaderRule.oneOf.forEach((rule) => {
rule.use.push({
loader: 'sass-resources-loader',
options: {
resources: [
// Change this url to your _variables.scss path
path.resolve(__dirname, './assets/app/styles/_vars.scss'),
]
},
})
})
})
You also are going to have to install sass-resources-loader
npm install sass-resources-loader
Then compile your code again and your sass variables will be available everywhere
Solution to my question:
//webpack.config.js
.enableSassLoader(options => {
options.additionalData = `
#import "./assets/scss/_variables.scss"; //path.resolve is not working in my case to import the absolute path
`
})
this will import the file in every sass template + vue component

Can't access CSS variables in script tag of Vue file

Problem
I am trying to access variables defined within an SCSS file from a .vue file. The project is using vue-cli.
According to Vue's docs:
"Vue CLI projects comes with support for PostCSS, CSS Modules and pre-processors including Sass, Less and Stylus."
However, if I create a variables.css file with a variable called variable, and try to import it within the script, this variable is not found.
styles/variables.module.css
$variable: 'foo';
:export {
variable: $variable
}
App.vue
<script>
import variables from "./styles/variables.module.scss";
export default {
name: "App",
methods: {},
computed: {
variable() {
console.log(variables); // Object {}
return variables.variable || "not found";
}
}
};
</script>
Importing the variables.css file within the <style module> tag of the same vue file does work however.
App.vue
<style module lang="scss">
#import "./styles/variables.module.scss";
:export {
variable: $variable;
}
</style>
What I'm trying to achieve
<p>Importing within <script>, the variable is {{variable}}</p>
// 'not found', should be "foo"
<p>Importing within <style>, the variable is {{$style.variable}}</p>
// correctly showing "foo"
Have tried:
Adding .module to the SCSS file name (as per vue's docs)
Creating a vue.config.js file with requireModuleExtension: false
(from same docs)
Reproducible demo
https://codesandbox.io/s/importing-css-to-js-o9p2b?file=/src/App.vue
You need to add webpack and CSS modular code into webpack.config.js.
npm install -D vue-loader vue-template-compiler webpack
Here is the working demo
Note: your vue-template-compiler and vue should be the same version

Overriding Vuetify variables

I'm using Vuetify in my project, and I want to use a variable file to override the styles generated by Vuetify.
I'm loading the components and their corresponding styles using the a-la-carte method, so I'm NOT importing the Vuetify SASS file using this:
#import '~vuetify/src/styles/styles.sass'
// Not using this method because I don't want to generate styles that are not being used by
// vuetify components I'm not using
Also, my project is using *.scss, not *.sass.
I'm also injecting a global SCSS file containing mixins and other variables in my vue.config.js:
css: {
sourceMap: productionSourceMap,
loaderOptions: {
scss: {
prependData: `#import '#/scss/_common.scss';`
}
}
},
I included a Vuetify variable, $border-radius-root, in that common.scss file, but it doesn't seem to have any effect.
Any idea how to do what I want without having to write entirely new CSS rules to override Vuetify's generated stylesheet? Basically I want to change the units that Vuetify uses using their own stylesheet generator.
Actually the solution is, and I'm dumb for not thinking of this before, to add another loader to vue.config.js:
css: {
sourceMap: productionSourceMap,
loaderOptions: {
scss: {
prependData: `#import '#/scss/_common.scss';`
},
sass: {
prependData: `#import '#/sass/_vuetify-variables.sass';`
}
}
},
Since vuetify is using sass as the css pre-processor, it needs sass-loader to handle the variable overrides and apply it to the framework.
If you are using Nuxt:
you can add customVariable path in your nuxt.config.js file, in vuetify object
Note you have to enable treeShake. This option is required for custom SASS variables to work
example:
vuetify: {
// usually file should be in assets folder
customVariables: ['~/path/to/variables.scss'],
treeShake: true,
}
If you are using Vue CLI:
Create a folder with name: sass, scss, or styles
Create new file inside this folder and name it: variables.scss or variables.sass
vuetify-loader will automatically bootstrap your variables into Vue CLI’s compilation process, overwriting the framework defaults.
From Vuetify docs:
If you have not installed Vuetify, check out the quick-start guide. Once installed, create a folder called sass, scss or styles in your src directory with a file named variables.scss or variables.sass. The vuetify-loader will automatically bootstrap your variables into Vue CLI's compilation process, overwriting the framework defaults.
So, the vuetify-loader automatically loads #/scss/variables.scss in a Vue CLI project, so you could set $border-radius-root in that file, and it will overrride the framework default.

Dedupe CSS in Vue.js development mode

I am working on a Vue.js project that heavily uses single file components. These components have scss styles associated with them.
In production mode the duplicate css that occurs from importing the same component multiple times is filtered out. But in development mode the same scss is imported multiple times.
This leads to slow downs with the chrome debugger when inspecting and modifying the css.
Does anone know a way to dedupe the css/scss attatched to single file components in developlment mode?
Here is my current vue config:
module.exports = {
lintOnSave: false,
configureWebpack: {
resolve: {
alias: require("./aliases.config").webpack
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
_: "lodash"
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
}
Here's how we ended up solving it.
Import only pure SCSS in components (ie. mixins, variables, functions). If a file with CSS is imported in each component the sass loader will NOT dedupe the CSS in development mode.
In your vue config add the following to include you scss variables in every single file component:
module.exports = {
...
css: {
loaderOptions: {
sass: {
data: `
#import "#src/_variables.scss";
`
}
}
},
...
}
Import your global scss in your app entry (main.js or equivalent)
import "bootstrap";
import "#src/global.scss";
In your global.scss file you can import your variables file so that it can also access your scss variables.

Importing SCSS file in Vue SFC components without duplication with Webpack

When I try to access a scss file in all my Vue SFCs the styles are duplicated causing large css bundles and Dev Tools to crash when style debugging
I am using Webpack 4 and webpack-dev-server to build and run development services with hot reload. I did not create the project with Vue CLI.
I have quite a lot of SFCs (~50) and a sass file (index.scss) that contains global styles and variables. I need to be able to use the styles and variables in index.scss across my SFCs. My current approach is using the data option in my Webpack sass-loader.
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
use: [
isDev ? { loader: 'vue-style-loader', options: { sourceMap: hasSM }} : {loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: {
sourceMap: hasSM
}
},
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/index.scss";`
}
}
]
}
]
}
This is successful, however I am noticing my index.scss styles included in every component. Local development with devserver is almost impossible because the duplication across 50 components is so vast and devtools can't cope. When I do a build to extract the css then I can see the duplication and the app.css file is huge. I can use a minifying process for deployments but this is not suitable at all for local development.
I have tried other approaches such as removing the data option from sass-loader using import ./styles/index.scss in my main.js file instead, however this fails to build because it can't find the variables I use in my SFCs.
Please see my code snippet for roughly how I have my loaders set up. I feel as if there is a better way to do this, whether it's using different loaders/plugins and I am open to using different approaches
I managed to solve. Essentially when every Vue component was being processed then the sass-loader was importing my global sass file that included Variables, Mixins and most importantly my Styles. The import statement in my main.js doesn't work because the Variables and Mixins are not available by the time the component is being processed.
So I only need to import Variables and Mixins in my components and the Styles can be external and included in my main.js. Since Variable and Mixins are just being included when required (with #include statements), then there is no duplication since it's getting compile to CSS.
So I split my Styles and Variables and Mixins into separate variables
Styles => styles.scss
Variable and Mixins => variableMixins.scss
then import ./styles/styles.scss in my main.js
and my webpack sass-loader would be like
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/variableMixins.scss";`
}
}
Been trying to solve this for too long.
This solution works but isn't perfect for me because I want to keep variables and global styles for specific things together.
_typography.scss, for example should contain general styles and variables relating to global line-height and vertical rhythm.
Then more granularly, a component such as _H1.scss would contain it's own sandboxed styles.
I don't want those general styles duplicated for every component.
The value of using CSS is sharing styles and being able to adjust and rebrand in a project globally. Components do the opposite of this by sandboxing everything.
I think CSS is currently at odds with componentized development and perhaps the answer may be to create a new approach where we use 'component ecosystems' to create relationships between otherwise dumb nodes.
A bit like how we use revealing module patterns in JS.
We ended up splitting up css into separate files (one per component), imporing them all into index.scss and importing index in App.vue`
import '#/scss/index.scss'
This eliminated lots of bloat / duplication and significantly simplified workflow: using sourcemaps + workspaces we can now edit scss directly in devtools and it won't break layout or multiply <style> tags or do any other quakery described all over Webpack's and vue-cli's github issues.
Surprisingly, even code-splitting for css still works, and dynamically loaded modules have their separate css files.
If you need scoping (which you'd need), just use id="component-name" on main node and wrap your scss in
#component-name {
...
}
It amazes me how unsupported modern css techniques by Devtools, Webpack etc. Every aspect is full of bugs and only simpliest scenarios sort of work out of the box (if work at all).