Using Vuetify classes within SASS - vue.js

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 !

Related

Importing Scss into vue.js views

I've noticed that if I want to use my _variables.scss I have to import them in every Vue file. My question is, how can I check if I load the same styles multiple times or does Vue saves only once the same scss files on compiling?
This is my code in multiple view files.
<style lang="scss" scoped> #import '~#/abstracts/_variables.scss'; #import '~#/pages/_profile.scss'; </style>
I import _variables.scss in every view where I want to use my scss variables.
This depends on how you have your project setup, for example if you're using webpack you can do something like this where you have your CSS loaders setup:
scss: generateLoaders('sass', {
additionalData: `
#import "#/styles/_variables.scss"
`,
}),
Or if you have a vue.config.js you can do this:
module.exports = {
css: {
loaderOptions: {
sass: {
additionalData: `
#import "#/assets/scss/main.scss"
`
}
}
}
}
Then you will have access to this global SCSS file everywhere in your Vue application.
Side note on the additionalData portion - that will depend on the version of sass loader you're using:
For ^7.x.x use data, and for ^8.0.2 use prependData, finally for 9.0.0+ use additionalData

Importing SASS variables into Vue component

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";'
}
}
}
}

Vue invalid CSS - expected 1 selector or at-rule,

I'm trying to npm run serve in Vue and receiving the following error:
Failed to compile.
./src/App.vue?vue&type=style&index=0&lang=sass& (./node_modules/css-loader/dist/cjs.js??ref--9-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--9-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--9-oneOf-1-3!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=style&index=0&lang=sass&)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Invalid CSS after " ": expected 1 selector or at-rule, was "{"
on line 1 of /Users/MatthewBell/GitHub/pollify/client/src/App.vue
>> {
^
Clearly it says the error is in App.vue and seems like a misplaced curly brace.
But if check in that file there aren't even any curly braces there:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link>|
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
<style lang="sass">
#app
#extend %global-styles
</style>
..And the file of class being extended.. main.sass is shown below. None of the imported sass sheets have curly braces either, so I'm sitting here very confused about the error message.
// Importing all our globally accessible stylesheets
#import ./reset
#import ./variables
#import ./mixins_and_placeholders
%global-styles
font-family: $font
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
background-color: $grey-3
color: $text-black
I also searched this question on StackOverflow and some people seem to say it can be an issue with your config file, but I cant find any issues with my vue.config.js:
// vue.config.js
module.exports = {
css: {
loaderOptions: {
// pass options to sass-loader
// #/ is an alias to src/
// so this assumes you have a file named `src/variables.sass`
// Note: this option is named as "data" in sass-loader v7
sass: {
prependData: `
#import "#/styles/main.sass"
`
}
}
}
};
This was working fine before so it's not an issue with the prepend data path.
As Phil mentioned in the comments. The indented #import "#/styles/main.sass" in my config file vue.config.js was causing the issue, since backticks preserve indentation. The following syntax solves it:
// vue.config.js
module.exports = {
css: {
loaderOptions: {
// pass options to sass-loader
// #/ is an alias to src/
// so this assumes you have a file named `src/variables.sass`
// Note: this option is named as "data" in sass-loader v7
sass: {
prependData: `#import "#/styles/main.sass"`
}
}
}
};

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";