Sass global variables and mixins not working - vue.js

I've set up a project using Vue 3.2.33 and Vite 2.9.5
When I try to access any global variable or mixin from within any vue component, I get an undefined error. This problem doesn't occur in scss files.
The import itself seems working correctly because any css rules in it are working.
vite.config.ts:
import { fileURLToPath, URL } from 'url';
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url)),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: '#use "#/styles/variables";',
},
},
},
});
src/styles/_variables.scss:
// breakpoints
$breakpoints: (
"sm": 576px,
"md": 768px,
"lg": 992px,
"xl": 1200px,
"xxl": 1400px,
);
#mixin test {
border: 3px solid red;
}
Example use:
<style scoped lang="scss">
#use 'sass:map';
.container {
max-width: 100%;
width: 100%;
margin: 0 auto;
#include test; // <- undefined
&--fluid {
max-width: 100%;
width: 100%;
}
}
$widths: (
'sm': 540px,
'md': 720px,
'lg': 960px,
'xl': 1140px,
'xxl': 1320px,
);
#each $breakpoint, $width in $widths {
#media (min-width: map.get($breakpoints, $breakpoint)) { // <- $breakpoints undefined
.container {
max-width: $width;
}
}
}
</style>

use
#import
in your vite config instead of
#use
vite.config.ts:
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: '#import "./src/styles/variables.scss";',
},
},
},
});
keep in mind that you cannot import the same file variables.scss again in your main.ts file otherwise, you will get this error
[sass] This file is already being loaded.
by the way, you can also import the scss file in every single component manually as you mentioned but that would be really tedious so using a global import in preprocessorOptions in vite.config.ts is a much better option for files used globally like a variables.scss file.

I've managed to "fix" the issue. Turns out, when I replace all #use rules for file imports, the sass code is imported correctly and works. But this produces a new problem as the #import rules cannot be placed before #use, so I had to remove the additionalData key from config and include the imports manually.

Related

Vite - Separate chunk for fonts

I am building a library that contains components and some shared CSS, like a design system.
The problem is that it is not separating the fonts in different chunks, instead, it is inlining them in the font as base 64, so the CSS file gets huge!
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'design-system',
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue',
},
},
},
},
resolve: {
alias: [
{ find: '#', replacement: '/src' },
],
},
});
I already tried to place it in root/public, but it didn't work.
I have a file _fonts.css which import fonts like so
#font-face {
font-family: Inter;
src: url('/public/fonts/Inter-Regular.ttf') format('truetype');
font-weight: 400;
}
And then I have a main.scss that imports it
// main.scss
#import 'normalize';
#import 'themes';
#import 'fonts';
Any idea on how to split it?
I was able to accomplish this in a library we're working on by placing the fonts in the special public directory (see: https://vitejs.dev/guide/assets.html#the-public-directory).
Long story short placing assets in there makes Vite copy them over without any actual transformation and/or renaming.
First of all place your font files in public/fonts/.
Then add the following to vite.config.ts:
{
root: './',
publicDir: 'public',
}
This was necessary although it should be a default setting.
Then my font declaration looks like this:
#font-face {
font-family: MyFont;
src: url('./fonts/myfont.woff2');
font-weight: 200;
}
But - this is for the library mode. In case you're building an app just start the font URL path with / instead of ./.

How to use sass global variables from other components in vite/vue3

I want to use and change global variables from other components, my files structure looks like this...
I have my variables in global.sass file, but I can't access variables in other components.
You need to set configuration file vite.config.js :
css: {
preprocessorOptions: {
scss: {
additionalData: `#import "#/assets/global.scss";`
}
}
},
also for local fonts you can add another configuration there, set alias:
resolve: {
alias: {
'#': path.resolve(__dirname, 'src'),
}
},
and then use it something like:
#font-face {
font-family: 'Opensans-Bold';
font-style: normal;
src: local('Opensans-Bold'), url(#/assets/fonts/OpenSans-Bold.woff2) format('woff2');
}
Nikola's answer is correct. For any Nuxt3 users out there I'd like to add you need to make sure you don't have explicit css config in your nuxt config file at the same time. Otherwise you might get file already imported error
A new sass module system
Note: The Sass team discourages the continued use of the #import rule. Sass
will gradually phase it out over the next few years, and eventually remove it from the language entirely. Prefer the #use rule instead. (Note that only Dart Sass currently supports #use. Users of other implementations must use the #import rule instead.)
More details: Here
Below is the best way to global scss at that time.
vite.config.js
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `#use "~/styles/_main.scss" as *;`,
},
},
},
plugins: [vue()],
});
styles/abstracts/_colors.scss
$default: #000000;
$default-light: #333333;
$default-dark: #000000;
styles/abstracts/index.scss
#forward './colors';
#forward ...
styles/_main.scss
#forward './abstracts';
#forward './components';
#forward './layouts';
src/index.scss => don't forget to add this import "./index.scss" in App.vue
#forward './styles/abstracts';
#use './styles/abstracts' as *;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
body {
font-family: $font-primary;
font-size: 1.6rem;
line-height: 1.5;
text-rendering: optimizespeed;
color: $text;
overflow-y: overlay;
}

How to disable Vuetify's style?

I want to parse markdown to html and use syntax highlighting.
My SFC is as follows:
<template>
<div v-html="html"></div>
</template>
<script>
import marked from 'marked'
import hljs from 'highlightjs';
export default {
name:"Article",
props:['md'],
computed:{
html(){
return marked(this.md)
}
},
created: function () {
marked.setOptions({
langPrefix: '',
highlight: function(code, lang) {
return hljs.highlightAuto(code, [lang]).value
}
})
},
}
</script>
<style src='highlightjs/styles/github-gist.css'></style>
The resulting code blocks are look like this:
This is Vuetify's style.
https://vuetifyjs.com/en/styles/content/#code
I want to disable or override it.
The following code does not work for code blocks:
<style scoped>
.v-application code {
background-color: unset !important;
color: unset !important;
box-shadow: unset !important;
}
.myclass {
color:red !important;
}
</style>
Result:
Vuetify has the following CSS specified for the code tags:
.v-application code {
background-color: #f5f5f5;
color: #bd4147;
box-shadow: 0 2px 1px -1px rgba(0,0,0,.2),
0 1px 1px 0 rgba(0,0,0,.14),
0 1px 3px 0 rgba(0,0,0,.12);
}
You can see this if you open developer tools and inspect a code tag on their website.
Either override those values to your own, or just set them all to unset or unset !important. For example:
.v-application code {
all: unset;
color: #eee
}
/* Or with increased specificity */
.v-application code.code--custom {
all: unset;
color: #eee
}
Actualy the style override you suffer from wouldn't be a problem if you just import your HighlightJS CSS directly after Vuetify in your main.js.
//main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import '<your_path>/highlight.min.css'
Consider also using a Vue Directive for global usage.
//main.js
Vue.directive('highlightjs', {
deep: true,
bind: function(el, binding) {
// highlight all targets
let targets = el.querySelectorAll('code')
targets.forEach((target) => {
// override this in case of binding
if (binding.value) {
target.textContent = binding.value
}
hljs.highlightBlock(target)
})
},
})
Then you can simply use it like this:
<pre v-highlightjs>
<code class="javascript">
// your code goes here //
</code>
</pre>
I made a JSFIDDLE for this, which is a modified version of a vue HighlightJS example by Chris Hager.
https://jsfiddle.net/b8jontzr/2/

Vue Cli 3 Local fonts not loading

When trying to load custom local fonts in Vue CLI 3 the fonts still will not appear. I am not receiving any error messages. The inspector shows the correct rule being loaded, but fonts are falling back to serif on #app. Fonts are not showing up in my dist folder anywhere.
I have tried adding loaders in vue.config.js, changing url paths, and moving the #font-face rules around to different locations, changing the public path to ' ' and '/', importing scss into main.js.
Font loading:
#font-face {
font-family: 'OpenSans-Regular';
src: url('/assets/fonts/OpenSans-Regular.eot');
src: url('/assets/fonts/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
url('/assets/fonts/OpenSans-Regular.otf') format('font-opentype'),
url('/assets/fonts/OpenSans-Regular.woff') format('font-woff'),
url('/assets/fonts/OpenSans-Regular.ttf') format('font-truetype'),
url('/assets/fonts/OpenSans-Regular.svg#OpenSans-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
And use within App.vue:
<style lang="scss">
#app {
font-family: 'OpenSans-Regular', serif;
}
</style>
That styling is placed within my main.scss file. The file structure as follows:
src
assets
fonts
OpenSans-Regular.eot
OpenSans-Regular.woff
etc
styles
main.scss
App.vue
vue.config.js
vue.config.js file is as follows:
module.exports = {
publicPath: '/',
css: {
sourceMap: true,
loaderOptions: {
sass: {
data: `#import "#/styles/main.scss";`
}
}
},
configureWebpack: {
module: {
rules: [{
test: /\.(ttf|otf|eot|woff|woff2)$/,
use: {
loader: "file-loader",
options: {
name: "fonts/[name].[ext]",
},
},
}]
}
}
}
I have also tried a chainWebpack in vue.config.js to no avail:
chainWebpack: config => {
config
.module
.rule("file")
.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/,)
.use("url-loader")
.loader("url-loader")
.options({
limit: 10000,
name: 'assets/fonts/[name].[ext]'
})
.end();
}
Did you try
#font-face {
font-family: 'OpenSans-Regular';
src: url('~#/assets/fonts/OpenSans-Regular.eot');
src: url('~#/assets/fonts/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
url('~#/assets/fonts/OpenSans-Regular.otf') format('font-opentype'),
url('~#/assets/fonts/OpenSans-Regular.woff') format('font-woff'),
url('~#/assets/fonts/OpenSans-Regular.ttf') format('font-truetype'),
url('~#/assets/fonts/OpenSans-Regular.svg#OpenSans-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
Works for me Vue CLI 3, no vue.config.js settings.
I'm loading my styles like this:
import Vue from 'vue';
import router from './router';
import store from './store';
// eslint-disable-next-line
import styles from './scss/app.scss';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
Not sure if that is good practice.
What I ended up doing was moving to a file loader method to get the fonts to package over and set the the public path.
vue.config.js
module.exports = {
assetsDir: 'assets/',
publicPath: '/', // Base directory for dev
css: {
sourceMap: true,
loaderOptions: {
sass: {
data: `#import "#/styles/main.scss";`
}
}
},
chainWebpack: config => {
config.module
.rule("fonts")
.test(/\.(ttf|otf|eot|woff|woff2)$/)
.use("file-loader")
.loader("file-loader")
.tap(options => {
options = {
// limit: 10000,
name: '/assets/fonts/[name].[ext]',
}
return options
})
.end()
}
};
File-loader doesn't see the files unless called in the js so I imported them in main.js The console log is to navigate around the linter flagging unused imports
// Fonts need to be called in js for webpack to see and copy over
import OpenSansReg from './assets/fonts/OpenSans-Regular.ttf';
import OpenSansLight from './assets/fonts/OpenSans-Light.ttf';
import OpenSansBold from './assets/fonts/OpenSans-Bold.ttf';
console.log(OpenSansReg, OpenSansBold, OpenSansLight);
then in one of my scss files
#font-face {
font-family: 'OpenSans-Regular';
src: url('/assets/fonts/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
url('/assets/fonts/OpenSans-Regular.otf') format('opentype'),
url('/assets/fonts/OpenSans-Regular.woff') format('woff'),
url('/assets/fonts/OpenSans-Regular.ttf') format('truetype'),
url('/assets/fonts/OpenSans-Regular.svg#OpenSans-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
For me, I just took out that 'format()' thing and works... Finally..
I stuck my custom icon-font in the head tags of my initial index.html page Which also has a custom font import. The same page that you would stick your <div id="vue-app"></div>. All the other pages /components can use the font-family for me.
<head>
...
<link rel="stylesheet" href="icon-font/styles.css" />
</head>
But if i try any other location in the project it fails. and the Scss doesnt even compile.
This helped me
In src folder main.js just added:
import '../src/fonts/fonts.css'
in the font the following code:
#font-face {
font-family: 'Conv4240';
src: url('~#/fonts/Conv4240/4240.eot');
src: local('☺'),
url('~#/fonts/Conv4240/4240.woff') format('woff'),
url('~#/fonts/Conv4240/4240.ttf') format('truetype'),
url('~#/fonts/Conv4240/4240.otf') format('opentype'),
url('~#/fonts/Conv4240/4240.svg') format('svg');
font-weight: normal;
font-style: normal;
}
And everything started to work

Override vuetify without !important

I want to apply custom CSS and override some default Vuetify colors. For example, success button can be easily overridden:
.success-button {
background-color: $sb--color-success !important;
}
But is there a way to do the same without using !important? I tried both:
body .success-button {
background-color: $sb--color-success;
}
button .success-button {
background-color: $sb--color-success;
}
How to do it without !important?
You can try something along this lines
// src/index.js
// Libraries
import Vue from 'vue'
import Vuetify from 'vuetify'
// Helpers
import colors from 'vuetify/es5/util/colors'
Vue.use(Vuetify, {
theme: {
primary: colors.red.darken1, // #E53935
secondary: colors.red.lighten4, // #FFCDD2
accent: colors.indigo.base // #3F51B5
}
})
Or something like this
<h1 class="subheading grey--text">DASHBOARD</h1>