How to override Vuetify 2 variables without using Vue CLI - vue.js

The vuetify documentation only provides example for injecting sass variables using vue-cli configuration in vue.config.js
https://vuetifyjs.com/en/customization/sass-variables#markup-js-vue-config-sass-variables
What is the correct way to provide the modified vuetify variables when not using the CLI?
I am upgrading an older project from v1 (stylus) to v2 (sass) and I need to override some variables, lets say I only need to change the font-family to Arial.
I am also using treeshaking with vuetify.
Now I am kind of stuck because I don't know where to import the style overrides... Importing these in src/main.ts obviously does not work.
I have created a minimal repro here: https://github.com/Sharsie/vuetify-theme-repro/
What I have so far is a webpack config in build directory and style overrides in src/styles/main.scss
$body-font-family: Arial;
#import "~vuetify/src/styles/styles.sass";
Running the project creates a simple page that prints out computed styles for the v-app container
<v-app id="app">
<v-container>
<v-content>
<p>Wanted font-family: Arial</p>
<p>Current font-family: {{ fontFamily }}</p>
</v-content>
</v-container>
</v-app>

After digging through the source code of vue-cli, I figured out that the config just gets passed to sass-loader options, so the solution was pretty straightforward:
Instead of providing the stylesheet with variables through vue.config.js as such:
module.exports = {
css: {
loaderOptions: {
sass: {
data: `#import "~#/styles/main.scss"`,
},
},
},
}
You can provide it directly to sass-loader options in webpack config like this:
module.exports = {
...
module: {
rules: [
...
{
test: /\.(s?c|sa)ss$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
implementation: sass,
sassOptions: {
fiber: fibers,
},
prependData: `#import "~/styles/main.scss"`,
},
},
],
}
...
]
}
...
}
or for sass-loader<8.0.0
options: {
implementation: sass,
fiber: fibers,
data: `#import "~/styles/main.scss"`,
},

Related

Dynamic Classes in Vue 3 SFC

I try to inject classnames into a Vue 3 component via props and would like to keep the classes defined in the component. Problem is, those classes are not included in the build, since they are "unreferenced".
Is there a way around this?
<script setup lang="ts">
const {className} = defineProps<{className: string;}>();
</script>
<template>
<div :class="className">Hello</div>
</template>
<style lang="scss" scoped>
.some-class {
color: pink;
}
</style>
.some-class is not included in the build since it's not directly referenced :-(
The application is built using webpack 5 with vue-style-loader:
module: {
rules: [
{ test: /\.css$/, use: ["vue-style-loader", "css-loader"] },
{
test: /\.scss$/,
use: [
"vue-style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
additionalData: `#import "scss/_variables.scss";`
}
}
]
}
]
}
EDIT: Added my rather unspectacular webpack5 config
Thanks

Webpack not loading Vue's single file components CSS

Webpack is compiling single file components but not loading CSS. The HTML and Vue is rendered correctly but without CSS. It seems to be an issue with webpack configuration. Any idea what's wrong?
I'm using webpack-dev-server to load the development server.
src/index.html
<html>
<head>
<title>Vue Hello World</title>
</head>
<body>
<h1>Header</h1>
<div id="app"></div>
</body>
</html>
src/Hello.vue
<template>
<p>{{ greeting }} Test!</p>
</template>
<script>
module.exports = {
data : function () {
return {
greeting: 'Hello'
}
}
}
</script>
<style scoped>
p {
font-size: 18px;
font-family: 'Roboto', sans-serif;
color: blue;
}
</style>
src/main.js
import Vue from 'vue';
import Hello from './Hello.vue';
new Vue({
el: '#app',
render: h => h(Hello),
});
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
{ test: /\.vue$/, exclude: /node_modules/, use: 'vue-loader' },
{ test: /\.css$/, exclude: /node_modules/, use: ['vue-style-loader', 'css-loader']},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
]
};
I just spent the last 4 hours figuring this out. I wanted to use only Webpack because it integrates much nicer into my Gulp pipeline than Vue CLI does. Not sure what version of css-loader you're on and if it's the same cause, but hope this helps you out as well.
TL;DR: instead of just 'css-loader', use:
{
loader: 'css-loader',
options: {
esModule: false
}
}
I use SCSS which has the same problem, but for your example that would be:
{ test: /\.css$/, exclude: /node_modules/, use: ['vue-style-loader', { loader: 'css-loader', options: { esModule: false }}]}
I'm not sure if this is a proper solution or just a workaround. I figured this out because I had an old project that worked before, but as soon as I upgraded all packages it failed. Eventually I traced it down to css-loader, which was version 2.1 before and 4.3 after the update. It worked up to version 3.6, then with 4.0 it failed. Then I just tried toggling the various options and this was the one that fixed it (I never claimed to know what I was doing ;-)).
Edit: seiluv added a link to a comment on vue-style-loader's Github that confirms this as a workaround. It was apparantly posted a month before, but I did not find it in my search for an answer. Thanks seiluv!
I encountered the same problem, so either :
Use style-loader instead of vue-style-loader
As suggested here.
use: ['style-loader', 'css-loader']
Basically the upgrade to css-loader 4.x.x + doesn't work well with vue-style-loader.
or
Disable css-loader's esModule option
The solution by MvRens is also valid
and confirmed by this comment on github.
The problem is css-loader's esModule is set to true by default and should be false in this case.
In addition to the above two answers, here sum up the possible solutions including the new one -- sideEffects. Please edit the list if someone can improve it.
Possible Solutions
in webpack.config.js
Add esModule: false to css-loader's option. (as #MvRens's answer)
Change vue-style-loader to style-loader in webpack.config.js. (as #seiluv's answer)
in package.json
Add "*.vue" to {sideEffects: []}
See: https://github.com/vuejs/vuepress/issues/718#issuecomment-412345448 and https://stackoverflow.com/a/53737250/493168

Implementing Vue SFC with Webpack (no Vue CLI)

I've recently switched to Webpack and have all my JS and CSS running perfectly through it now. Here's the relevant piece of webpack.config.js:
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
},
{loader: 'import-glob-loader'}
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
},
{loader: 'sass-loader'},
{loader: 'import-glob-loader'}
]
}
]
I have Vue included from a CDN and with this setup I can do the following no problem:
Vue.component('test-component', {
data: function () {
return {
title: 'Title',
description: 'Description'
};
},
methods: {
created: function () {
console.log('Created');
}
},
template: '<section id="test-component"><h2>{{ title }}</h2>{{ description }}</section>'
});
new Vue({el: '#app'});
And in my HTML:
<div id="app">
<test-component></test-component>
</div>
I'd now like to use Vue single file components instead, and reading the docs it tells me to simply run .vue files through vue-loader, so I changed my rules to the following:
rules: [
// NOTE: This is new
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
},
{loader: 'import-glob-loader'}
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
},
// NOTE: This is new too, but commented because it throws errors
// {loader: 'vue-style-loader'},
{loader: 'sass-loader'},
{loader: 'import-glob-loader'}
]
}
]
With that in place my .vue files are picked up and added to dist/main.js so it seems to be working (as long as I don't include a <style> element in the Vue file in which case it fails), but now new Vue({el: '#app'}) does absolutely nothing. Checking the DOM the <test-component> is still in there and not rendered by Vue at all.
If I also try to enable vue-style-loader the build fails entirely saying:
(1:4) Unknown word
> 1 | // style-loader: Adds some css to the DOM by adding a <style> tag
| ^
2 |
3 | // load the styles
What am I doing wrong here?
Edit: Progress. Thanks to Daniel my <style> now works as long as it has lang="scss" set. This is because my webpack config only has rules for scss files and not css files.
I've also figured out the reason the <test-component> won't render is because I never actually register it, simply including the .vue-file is not enough for it to be registered obviously.
The problem I'm having now is trying to glob import all my .vue-files as an array of components. If I do this it works fine:
import TestComponent from "./test-component.vue";
import AnotherComponent from "./another-component.vue";
document.querySelectorAll('[data-vue]').forEach(el => {
new Vue({
el: el,
components: {
'test-component': TestComponent,
'another-component': AnotherComponent
}
});
});
But I'd like to be able to do this some how:
import components from "./**/*.vue";
document.querySelectorAll('[data-vue]').forEach(el => {
new Vue({
el: el,
components: components
});
});
Using import-glob-loader.
Simply importing the vue files is not enough for them to be available for use. You also have to register them with Vue.
So, this is wrong:
import 'component.vue';
new Vue({el: '#app'});
This is right:
import component from 'component.vue';
new Vue({
el: '#app',
components: {
'component': component
}
});
That takes care of making them usable.
The reason the <style> elements don't work is because I don't have a webpack rule for CSS files - only for SCSS files. Thanks to #Daniel for pointing out that I need <style lang="scss">.
vue-style-loader is only needed to inject styles into the DOM as <style> elements which I don't actually use (I use mini-css-extract-plugin to create css-files) also according to the docs:
However, since this is included as a dependency and used by default in vue-loader, in most cases you don't need to configure this loader yourself.
Will create a separate question regarding the glob import.
Make sure you have <style lang="scss"> in your SFC
You can also try deleting the package-lock and node_modules folder and do a clean install. Sometimes that can resolve an issue if the dependencies are not using compatible versions.
Update
To import using glob style imports you may need to use import-glob
https://www.npmjs.com/package/import-glob
You can also achieve similar result using global component registration. This is documented well in the official docs at:
https://v2.vuejs.org/v2/guide/components-registration.html#Automatic-Global-Registration-of-Base-Components

vue cli inspecting css from browser development console

Following various vue cli example to successfully implement scss file into the vue file, the page now loads with css imported but I cannot inspect from which file / line number the css declaration comes from, all it says in chrome console is within the not from the actual file like "margin.scss line 40".
here is my vue.config.js
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: ["#/src/css/index.scss"]
}
},
here is my App.vue
<template>
<div >
</div>
</template>
<style lang="scss" >
#import "./css/index.scss";
</style>
and here is what I see,
Activate sourcemaps:
module.exports = {
css: {
sourceMap: true,
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: ["#/src/css/index.scss"]
}
},
https://cli.vuejs.org/config/#css-sourcemap

SCSS alias in Vue SFC via Rollup

When using Webpack is pretty straight forward to add an alias for scss files in a Vue SFC, e.g:
<style lang="scss">
#import "~scss/config/config";
...
</style>
Would be the following in Webpack:
alias: {
sass: path.resolve(__dirname, '../scss/')
}
How would you add the same kind of alias in Rollup via rollup-plugin-vue?
I've tried adding a number of postcss plugins, e.g
import importer from 'postcss-import';
vue({
css: false,
style: {
postcssPlugins: [
importer({
path: null,
addModulesDirectories: [path.resolve(__dirname, '../shared')]
})
]
}
}),
I've also tried: rollup-plugin-alias, rollup-plugin-includepaths and some other postcss plugins.
I don't think you can use postcss plugins within the Vue plugin to accomplish this, because it compiles the scss before it gets passed to postcss.
Using rollup-vue-plugin I've been able to use style.preprocessOptions.scss.includePaths to alias directories, in my case pointing to node_modules:
//rollup.config.js
import VuePlugin from 'rollup-plugin-vue'
...
plugins: [
VuePlugin({
style: {
preprocessOptions: {
scss: {
includePaths: ['node_modules'],
}
}
})
]
...
// some .vue file
<style>
#import 'some-node-module' //resolves to 'node_modules/some-node-module'
</style