Importing SASS variables into Vue component - vue.js

I'm working on building a webpage using Vue, Typescript, Webpack, and Bulma. I got everything working and building correctly but I noticed that some of my bundles were massive (over 2mb in some cases). After a lot of confusion I figured out it was because I was importing my main SCCS file that included Bulma, Bulma Fluent, and Material Design Icons into my components so that I could use the variables, mixins, and extend some of the classes. From what I understand #import simply copies everything from the import, which would explain my massive bundles.
A close approximation of my working code:
main.scss
/*Color customizations*/
#import "bulma-fluent/bulma.sass";
#import "buefy/src/scss/buefy";
#import "#mdi/font/scss/materialdesignicons";
/*Some custom classes*/
MyComponent.vue
/*Template and Script here*/
<style scoped lang="scss">
#import "./main.scss";
.floating {
#extend .m-1;
position: fixed;
bottom: 0;
right: 0;
}
#include mobile {
.floating {
max-width: unset;
left: 0;
}
}
</style>
I want to be able to reference classes/variables/mixins from my main.scss without it ballooning the size of my modules. I thought about creating a separate variables.sass file but I couldn't get that to work plus it doesn't fix the issue of extending styles. I saw this question but I'm not using Nuxt.
How can I get this working?
P.S. I'm a bit of a noob when it comes to Webpack/Vue/SASS/SCSS so I apologize if I'm just being dumb here.
Edit
In the end I split out the variables to their own file and imported those globally. It doesn't solve the use case of extending styles but I think that's a lost cause. My code is below:
Variables.scss
/*Customization here*/
#import "bulma/sass/utilities/functions.sass";
#import "bulma-fluent/src/sass/color/_all.sass";
#import "bulma/sass/utilities/initial-variables.sass";
#import "bulma/sass/utilities/derived-variables.sass";
#import "bulma/sass/utilities/mixins.sass";
Main.scss
#import "./Variables.scss";
#import "bulma-fluent/bulma.sass";
#import "buefy/src/scss/buefy";
#import "#mdi/font/scss/materialdesignicons";
/*Some custom classes*/
webpack.js
/*Other irrelevant configurations*/
{
test: /\.s[ac]ss$/,
use: [
"vue-style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
additionalData: `
#import "./Variables.scss";
`
}
}
]
},
MyComponent.vue
/*Template and Script here*/
<style scoped lang="scss">
.floating {
margin: $size-1;
position: fixed;
bottom: 0;
right: 0;
}
#include mobile {
.floating {
max-width: unset;
left: 0;
}
}
</style>

I use the same stack. I have variables.scss file with variables and bulma mixins and that variables.scss file is imported only in main.scss .
To make all variables and mixins available in all components without using #import in style section you should add loaderOptions section to vue.config.js file. Here is my vue.config.js file:
module.exports = {
css: {
loaderOptions: {
scss: {
prependData: '#import "~#/assets/scss/_variables.scss";'
}
}
}
}

Related

SCSS: substitute scoped #import with #use

The official said that #import will be removed in years and recommend using #use instead.
I'm using scss with Vue, I can #import rules under :v-deep:
.article__content {
width: 100%;
max-width: 800px;
::v-deep() {
#import 'highlight.js/scss/github';
}
.article {
width: 100%;
}
}
But how can I change it to #use? #use can only been used at beginning of file, and the file I'm importing is a pure stylesheet which doesn't include any mixins and variables.

Using Vuetify classes within SASS

I would like to use some of the existing Vuetify classes in my own sass file, but I can't seem to figure out how to do it. I have a Vue-CLI3 project with the latest Vue/Vuetify, and have the following code:
main.sass
#import '~vuetify/src/styles/styles.sass'
.myTest
#extend .mr-1
#extend .shrink
I also have the vue.config.js setup to correctly reference the sass/scss files:
export default {
css: {
loaderOptions: {
sass: {
data: `#import "path/to/main.sass"`
},
scss: {
data: `#import "path/to/main.scss";`
},
}
}
}
When I compile, I get The target selector was not found, and it points to .mr-1 and .shrink. Am I doing this incorrectly?
All other CSS in my main.sass file works as expected, so I believe the wiring is correct.
It seems the spacing helper classes (including .mr-1) are only found when importing vuetify/src/styles/main.sass instead of styles.sass. The .shrink class is found in vuetify/src/components/VGrid/_grid.sass.
So your main.sass should look like this:
#import '~vuetify/src/styles/main.sass'
#import '~vuetify/src/components/VGrid/_grid.sass' // for .shrink
.myTest
#extend .mr-1
#extend .shrink
sass-loader config
The original question is probably using an older version of sass-loader, given the sass.data config. If using version 8 or newer, the loader option is sass.additionalData:
// vue.config.js
module.exports = {
css: {
loaderOptions: {
sass: {
// sass-loader >= 8
additionalData: `#import "~#/path/to/main.sass"`
// sass-loader < 8
data: `#import "~#/path/to/main.sass"`
}
}
},
}
Prepending global styles
With the sass-loader config above, importing vuetify/src/styles/main.sass in your project's root main.sass (as done in the original question) causes an error. The workaround is to copy the contents of Vuetify's main.sass into your own. However, if your app uses any Vuetify components, you'll see the same error for _grid.sass, and the same workaround applies:
// #import '~vuetify/src/styles/main.sass' ❌ SassError: This file is already being loaded.
#import '~vuetify/src/styles/tools/_index'
#import '~vuetify/src/styles/settings/_index'
#import '~vuetify/src/styles/generic/_index'
#import '~vuetify/src/styles/elements/_index'
#import '~vuetify/src/styles/utilities/_index'
// #import '~vuetify/src/components/VGrid/_grid.sass' ❌ SassError: This file is already being loaded.
.shrink
flex-grow: 0 !important
flex-shrink: 1 !important
.myTest
#extend .mr-1
#extend .shrink
This approach gets unwieldy the more you need to extend the component-specific styles.
Also, since this prepends the contents of your main.sass to all Sass entry points, you may notice a significant delay in build/dev times (hampering developer experience), and a sizable vendor CSS chunk in your build output.
Better alternatives
You could avoid the caveats above by importing your main.sass in main.js:
import '#/main.sass'
demo 1
On the other hand, if you only need these styles in a specific component, use local styles instead:
<script>
import '#/main.sass'
</script>
demo 2
<!-- or -->
<style lang="sass">
#import '~#/main.sass'
</style>
demo 3
<!-- or -->
<style lang="sass">
#import '~vuetify/src/styles/main.sass'
#import '~vuetify/src/components/VGrid/_grid.sass' // for .shrink
.myTest
#extend .mr-1
#extend .shrink
</style>
demo 4
So, I achieved to do it thanks to Vue CLI and some documentation. Here is my github repo, here is the codesandbox.
Basically, the setting was as follows:
// src/plugins/vuetify.js
import Vue from 'vue'
import Vuetify from 'vuetify/lib/framework'
Vue.use(Vuetify)
export default new Vuetify({
theme: {
options: { customProperties: true }, // interesting part
},
})
I'm not sure about the vue.config.js nor the webpack.config.js configurations since it also depends on your versions of node-sass and sass-loader but you said that you handled it well by yourself, so no big worries I guess.
I made the example in the App.vue file in which I wrote
<div id="priority" class="medium">hello, this is working !</div>
There is a global.sass file that will target it's div tag and apply color: var(--v-warning-base).
There is a global.scss file that will target it's .medium class and apply color: var(--v-accent-base).
Finally, the component itself will target it's #priority id and apply color: var(--v-error-base).
I found the answer thanks to this post, give it a thumbs up too !

How to do breakpoints in scss file with vuetify?

How to use media query breakpoints in my vuetify application but in scss file?
For example bootstrap enable me to do that in scss file:
#include media-breakpoint-up(sm) {
.custom-class {
display: block;
}
}
what is the equivalent in vuetify? I can't find any documention in vuetify website
You can do it in scss:
#import '~vuetify/src/styles/settings/_variables';
#media #{map-get($display-breakpoints, 'sm-and-down')} {
.custom-class {
display: block;
}
}
... and you're right there is very little docs regarding breakpoints within vuetify
I achieved this by attaching a class name corresponding to the breakpoint, which is available in the $vuetify.breakpoint object. Not a perfect solution, but I only needed to do it for once element in my app. Hope it helps!
Example:
<item :class="($vuetify.breakpoint.smAndDown) ? 'sm' : ''"></item>
...
<style scoped lang="scss">
#item{
right: 130px;
&.sm{
right: 35px;
}
}
</style>

How do I #extend external CSS classes in a Vue component?

For example, if I have something like this in my component:
<style lang="scss" scoped>
.color-blue {
color: blue;
}
.orange-blue {
#extend .color-blue; // This works!
#extend .bg-orange; // .bg-orange is defined in another css file somewhere. This doens't work
}
</style>
I get an error that says
".orange-blue" failed to #extend ".bg-orange".
The selector ".bg-orange" was not found.
I've read that I can add
#import 'path/to/orange.css'
to my style block, but that doesn't seem to do the trick here. Also, wouldn't that result in the CSS being repeated for every component that imports it?
Import the file with other styles like that
<style lang="scss" scoped>
#import '~styles/mixins';
...
</style>
or use the usual path
#import '../../../styles/mixins';
You can do it the following way:
Create the file containing the class you want to extend:
src/styles/style.scss
.my_class_to_extend {
backgroud-color: aqua;
}
In your vue.config.js file you add the following pointing to your file:
module.exports = {
css: {
loaderOptions: {
sass: {
data: `#import "#/styles/style.scss";`
}
}
}
}
After that you can extend in your vue component without problems:
...
<style lang="scss" scoped>
.my_extended_class {
#extend .my_class_to_extend;
}
Note that to use that you need scss files all along, not css.
In short, you can not extend css class in *.scss file by importing plain *.css file. Scss compiler will throw an error SassError: The target selector was not found.
The easy way is to rename referenced *.css file to *.scss. So outcome would be like #import 'path/to/orange.scss'

How to inject Global constant to a vue component <style>?

I'm searching how to inject a global (THEME_NAME in my example) to all the vue components:
<template>..</template>
<style lang="scss">
#import "bulmaswatch/<%=THEME_NAME=>/bulmaswatch.scss";
.foo {
color: $primary;
}
</style>
Context:
I'm using the darkly theme of bulma css in my vue application (vue Cli 3)
<template>..</template>
<style lang="scss">
#import "bulmaswatch/darkly/bulmaswatch.scss";
.foo {
color: $primary;
}
</style>
In order to switch to the cyborg theme, I will have to replace darkly with cyborg everywhere...
Is there a better way? Something like
<template>..</template>
<style lang="scss">
#import "bulmaswatch/<%=THEME_NAME=>/bulmaswatch.scss";
.foo {
color: $primary;
}
</style>
then somewhere in Webpack or vue.config.js, we can decide what is the theme
configureWebpack: () => {
return {
plugins: [
new webpack.DefinePlugin({
THEME_NAME: "cyborg"
}),
],
}
}
In order to switch to the cyborg theme, I will have to replace darkly with cyborg everywhere...
Not if you use the re-export pattern.
There's no need for any external libraries and codegen magics with webpack.
Basically, you create a file where you import your theme.
Let's call it _my-bulma-theme.scss and let's import darkly for now.
// _my-bulma-theme.scss
#import "bulmaswatch/darkly/bulmaswatch.scss";
In your code, you import this file instead of importing from Bulma directly:
// some-component.vue
<style>
#import "../../my-bulma-theme";
</style>
// some-other-component.vue
<style>
#import "../../my-bulma-theme";
</style>
Now, when you want to change the theme, you just need to change it in one place: the _my-bulma-theme.scss file.
// _my-bulma-theme.scss
#import "bulmaswatch/cyborg/bulmaswatch.scss";