Vue invalid CSS - expected 1 selector or at-rule, - vue.js

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

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

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 !

Can't load global scss variables in Vue SFCs

I tried to follow this tutorial to get access to a global file called variables.scss in all SFC files:
https://vueschool.io/articles/vuejs-tutorials/globally-load-sass-into-your-vue-js-applications/
My project uses vue cli 3, so I added the following property to vue.config.js:
{
css: {
loaderOptions: {
sass: {
data: `#import "src/assets/scss/variables.scss";`
}
}
},
}
the variables.scss file looks like this:
// variables.scss
$purple: #5D2D8B;
My component style looks like this:
<!-- Datepicker.vue -->
<style lang="scss" scoped>
.picker {
color: $purple!important;
}
</style>
But I got this error when trying to use the $purple variable
color: $purple!important;
^
Undefined variable: "$purple"
I've also tried to add style-resources-loader vue-cli plugin, but got the same error.
(added this when tried to use it):
{
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: [
path.resolve(__dirname, 'src/assets/scss/variables.scss')
]
}
}
}
It seems that on .vue files the #import statement is not applied, but in .scss files it is.
Anyone has an idea what is that about?

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'

Vue.js webpack - How to use themify-icons?

I added the package
yarn add themify-icons-sass
then in my component , I imported it in the script and in the style
<script>
....
import 'themify-icons-sass'
<style lang="scss" scoped>
#import 'themify-icons-sass/themify-icons';
...
but I get a build error
This dependency was not found:
* themify-icons-sass in ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/HelloWorld.vue
To install it, you can run: npm install --save themify-icons-sass
Where am I wrong in the import ? thanks for feedback
UPDATE
First, if I want o import it from ./node_modules , then the #import should NOT BE in a scoped style...
So I moved it to a global style in my App.vue
<style lang="scss">
#import "~themify-icons-scss/scss/themify-icons.scss";
#app { ...
Then I got an erro , the node-sass/vendor directory was not bulit.. so I add to rebuild node_sass
yarn add node-sass --force
Now it's taking in account the package but I get another error related to the relative path to the fonts:
These relative modules were not found:
* ../fonts/themify.eot in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-
loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.eot?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasIn
lineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.svg?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasIn
lineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.ttf?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.woff?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
the themify-icons-scss file structure is the following :
node_modules/themify-icons-scss
fonts
themify.eot
themify.svg
themify.ttf
themify.woff
scss
_core.scss
_extras.scss
_icons.scss
_mixins.scss
_paths.scss
_variables.scss
themify-icons.scss
node_modules/themify-icons-scss/scss/themify-icons.scss
#import "variables";
#import "mixins";
#import "path";
#import "core";
#import "extras";
#import "icons";
themify-icons-scss/scss/_path.scss
#font-face {
font-family: 'themify';
src:url('#{$ti-font-path}/themify.eot?-fvbane');
src:url('#{$ti-font-path}/themify.eot?#iefix-fvbane') format('embedded-opentype'),
url('#{$ti-font-path}/themify.woff?-fvbane') format('woff'),
url('#{$ti-font-path}//themify.ttf?-fvbane') format('truetype'),
url('#{$ti-font-path}/themify.svg?-fvbane#themify') format('svg');
font-weight: normal;
font-style: normal;
this is where there is some issue... with the $ti-font-path as defined in the variables, relative to the scss directory in the package..
themify-icons-scss/scss/_variables.scss
$ti-font-path: "../fonts" !default;
$ti-class-prefix: "ti" !default;
}
SOLVED...
there is a nice recent package ( updated 3 months go, I forked it as an archive for me ..). see themify-icons-scss
yarn add git+http://github.com/Frolki1-Dev/themify-icons-sass
to solve the issue with the relative font path variable in the package _path.scss , I added the package resolve-url-loader. ( see resolve-url-loader on github
yarn add resolve-url-loader --dev
And as per the resolve-url-loader readme, as I want to use webpack loaders I inserted into build/utils
build/utils.js
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// added resolve-url-loaderr with sourceMap true
const resolveUrlLoader = {
loader: 'resolve-url-loader',
options: {
sourceMap: options.sourceMap
}
}
In my App.vue I can import (global , cannot be scoped)
<style lang="scss">
#import "~themify-icons-scss/scss/themify-icons.scss";
#app {
...
And I can check the themify-icons in my component template
<h2>Test Themify Icons</h2>
<div class="row" style="margin-bottom: 30px;">
<div class="col-4"></div>
<div class="col-4">
<span class="ti-email"> Email Me</span>
</div>
</div>