webpack: scoping all SCSS under vue app selector - css

I am writing a vue app that renders inside a page on a "traditional" server-rendered site, in this container:
<div id="account-summary-container"></div>
Things work great when developing locally. But when run within the context of the website there is alot of style collision, because both my app and the website's styles are global. My app screws up styles on the entire site.
How can I scope all styles in my app to be local to the selector my app is rendered within?
My app uses bootstrap 4 styles which I am loading with css-loader.
I have webpack.config.js entrypoints like this:
entry: {
app: ["./src/scss/styles.scss", "./src/app.js"]
},
and styles.scss looks like:
#import '~bootstrap/scss/bootstrap';
#import '../css/feather.min.css';
#import '../css/icomoon-spinners.css';
#import url('https://fonts.googleapis.com/css?family=Maven+Pro:400,500,700,900');
#import 'helpers/variables';
#import 'helpers/mixins';
#import 'helpers/placeholders';
...
I am thinking css-modules might be the answer, but I can't figure out how to tell css-loader to make ALL styles local to #account-summary-container. I tried this in styles.scss:
:local(#account-summary-container) {
composes: "~bootstrap/scss/bootstrap";
composes: "../css/feather.min.css";
...
}
And it scoffed at my lame attempt with:
Error: composition is only allowed when selector is single :local class name not in ":local(#account-summary-container)", "#account-summary-container" is weird
I'm wondering if I am approaching it totally wrong. I'm hoping to not have to do alot of rewriting of styles.

In order to target only components inside of your Vue instance you have to use scoped styles. It is kind of a pain because you can't scope the css on the "main" App.vue file and let all children inherit it - you have to use scoped styles on each component that need it, which may result in more requests than you'd like..
As an example (I tested this and it should work)..
<template>
<!--
VUE/COMPONENT TEMPLATE
-->
</template>
<script>
export default {
/* ***********************
VUE/COMPONENT CODE
*********************** */
};
</script>
<!--
*********************************************************
***** SCOPE CSS PER COMPONENT ****************************
***** IMPORT VIA 'src="./path/to/cssfile.css"' ***********
**********************************************************
-->
<style scoped src="../css/style.css">
</style>

Found the answer. Pretty simple. Should had a V8 (conk).
.pony {
#import 'sub';
font-size: 100px;
}

Related

Bootstrap overrides scoped component styles

I have a nuxt project, my project scss structure is that I have a main.scss which is my global scss file for variables and font imports and such.
Primary scss I write inside components under the scoped tag.
My global styles and bootstrap seems to work fine, but when I try to
add scoped styles to my component, they are overwritten by bootstrap.
For example, This is my navbar.vue component:
<style lang="scss" scoped>
nav {
padding: 3rem;
}
</style>
The components bootstrap elements work fine but my added padding is overwritten.
Here is my nuxt.config:
css: [
// '#/assets/scss//bootstrap.css',
'#/assets/scss/main.scss',
],
modules: [
...
'bootstrap-vue/nuxt'
],
bootstrapVue: {
bootstrapCSS: false,
bootstrapVueCSS: false
},
styleResources: {
scss: ['./assets/scss/*.scss']
},
Here is my main.scss:
#import "variables";
#import "fonts";
#import "~bootstrap/scss/bootstrap.scss";
If i remove my bootstrap.scss import I ofcourse lose my bootstrap styling, but the styling I've added in my scoped component works.
As I know the attribute scoped is actual not supported well by browsers. The status of the element is more experimental as I remember. So if you use scoped styles they may be not prioritised over the other stylesheets.
Maybe you may have a look to canIUse.com:
https://caniuse.com/?search=scoped

Carbon design system global scss takes forever to build

I'm trying to apply g10 theme from Carbon Design System(Vue). But it takes about 12-15 mins to compile every time I save a file.
Is there a way to add styles for only the components I use?
If not, how do I reduce the build time?
Is there a way to remove watch on scss files so I can save compile time when working in Vue files?
#import "#carbon/themes/scss/themes";
$carbon--theme: $carbon--theme--g10;
// Use the gray 10 theme
#include carbon--theme();
#import "carbon-components/scss/globals/scss/styles";
#import "#carbon/grid/scss/grid";
We workaround this by having all the Carbon related styling in the App.vue file, which imports another component (e.g. AppContent.vue) which contains the content for the entire app.
This means that the Carbon styling is only compiled when you make changes to the App.vue, and as everything is in AppContent.vue, you'll only need to make changes to this file rather than App.vue.
App.vue:
<template>
<app-content></app-content>
</template>
<script>
import AppContent from "./AppContent.vue";
export default {
components: { AppContent }
};
</script>
<style lang="scss">
#import "./styles/carbon";
#import "./styles/_carbon.scss";
</style>
AppContent.vue:
<template>
<!-- the rest of your app -->
</template>
<script>
// the rest of your app
</script>
<style>
/* the rest of your app */
</style>

How to isolate Vuetify global styles

I've started to use Vue.js with Vuetify within an old existing project. So I did not rewrite all frontend, I just imported Vue and replaced some parts.
And then I've noticed quite an unexpected behavior - Vuetify has global styles for common classes like .title and it effects the whole page, not only Vue part.
So, the questions is, how can I isolate vuetify styles inside Vue components?
UPD: As suggested #DigitalDrifter I tried to use stylus block-level import. So I removed
import 'vuetify/dist/vuetify.min.css'
from main.js and created a new .styl file (which was imported instead css) with the following content:
.vuetify-styles
#import '~vuetify/src/stylus/main'
And then added this class to the root component: <App class="vuetify-styles">
UPD2: After that you can get bug related to stylus compilation. More about it -> https://github.com/vuetifyjs/vuetify/issues/4864
UPD3: less also works fine for me.
# vuetify-styles.less
.vuetify-styles {
#import (less) '../../node_modules/vuetify/dist/vuetify.min.css';
}
And then just import it in your main.js
import './vuetify-styles.less'
Stylus supports block level imports.
If you've got the following:
// bar.styl
.bar
width 10px
// foo.styl
.foo
#import 'bar.styl'
The end result will be:
.foo .bar {
width: 10px;
}

Not entirely sure where am I supposed to write my CSS in a Vue SPA

Up until now, I've always used a single CSS file when creating multiple page applications which would store all my CSS rules.
Now that I'm using Vue.js and components, I am not sure where to write my CSS.
I could write the CSS in the <style></style> tags of a component but to me this only makes sense if the CSS is only used for this specific component. This leaves me wondering where should I write CSS which I would like to be applied globally to everything.
Imagine I have some CSS which I want to be applied to everything like this snippet:
*, *:after, *:before {
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
text-decoration: none;
}
Where would I write it?
Until a better solution I can offer two suggestions:
1. In App.vue file
If it's just a small amount of code, put it in your App.vue file
As you said:
"I could write the CSS in the tags of a component but to me this only makes sense if the CSS is only used for this specific component."
If you put CSS in the in the <style></style>in any .vue files that's part of your project it's going to be global.
<style></style> get's applied to the whole project unless you add scoped to the tag.
<style scoped>
body {
background: green; /* This won't effected your main <body> tag unless you actually have a `<body>` inside the `<template>` of that file */
}
</style>
2. Import a separate file containing only CSS (updated and easier)
From #Fabjan in the comments.
Just import the .css file in your main.js file:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './assets/lib/css/reset.css' // my own reset file
import './assets/lib/css/myMain.css' // global stylesheet
Make sure to put it before your run your app. new Vue({})
(3. My previous answer:)
You can create a styles.vue and add all your global styles there inside <styles></styles> without scoped. (this worked for me)
Then inside your main.js file you write:
import GlobalStyles from "./assets/lib/css/styles.vue"
Vue.use(GlobalStyles)
Make sure your styles.vue contains at least on tag inside for it to import properly.
<template>
<div class="empty">
</div>
</template>
<style>
body {
background: red;
/* The rest of your Global CSS code */
}
</style>
This feels really tacky but it worked for me now.
Hope it helps and I'd love some feedback or comments if people have better solutions.
Here is my solution to configure global scss with my project that using Nuxt.
Assume that you already have node sass and sass-loader installed.
In nuxt.config.js file add your SCSS path from static or assets folder
css: [
'#/assets/scss/main.scss'
]
Bonus: if you don't like this way maybe you can get a try nuxt-sass-resources-loader

Sharing common CSS across VueJS components

I'm working on the VueJS 2 project and I'm trying to clean the code but struggle with scoped styling.
Here is my requirements. :)
I have 3 components those are very similar to each others, so I decide to use mixins to merge the code into one file. Each component will use that mixins of both template and vuejs. When I want to customize the conditions of a particular component, I can simply override the code in it and it is working fine in this part.
However, one thing that I want to do more is to move the scoped style to the mixins as well. At the moment, the style is wrapped in <style lang="scss" scoped></style> tag and this style works very well on its component but I have to duplicate the styling codes into all 3 components.
I know I can add these styles to the global css file but I don't want some styles to the global scope, only one these 3 components will apply for these.
Is it any way to add these styles and apply to mixins?
What is the best practice to code this particular case?
Vue makes this easy.
Solution
To use shared styles in a component you can do this.
MyComponent.js
<template>
</template>
<script>
</script>
<style lang="scss" scoped>
#import '#/scss/shared-styles.scss';
#import 'styles.scss'; // this is the regular CSS used just by the component
</style>
Alternative
You can also import the shared CSS files in the component CSS file instead like below.
MyComponent.js
<template>
</template>
<script>
</script>
<style lang="scss" scoped>
#import 'styles.scss';
</style>
styles.scss
#import '#/scss/shared-styles.scss'
// rest of your component CSS
Automatically import global styles
If you want certain styles to be available in ALL your components you can do this.
vue.config.js
module.exports = {
...
css: {
loaderOptions: {
sass: {
prependData: `
#import "#/scss/global.scss";
`
},
},
},
}
I just found out the scoped style also affect on the child components as well.
Therefore, I found the solution, not sure is it the best practice but I feel very nice for it.
Create a WrapperComponent and I put the scoped style here and a small template.
<template>
<div>
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
/* css style that will apply to all children */
</style>
What happen here is that, when we wrap whatever components with this WrapperComponent, the template will pass on the HTML via slot without any modification and style will be able to apply from now on.
In mixins, I import this wrapper and wrap the component template with the WrapperComponent. Here is the example.
import WrapperComponent from './WrapperComponent'
let MyMixins = {
template: `<wrapper-component>
<div>
Whatever HTML code here
</div>
</wrapper-component>`,
components: {
WrapperComponent,
},
};
When we use this mixins or a child component, the style from WrapperComponent will be automatically applied and also can be used with other groups of components those want to use the same parent style.
Perhaps use modules instead of setting up a style section with a scoped attribute.
https://vue-loader.vuejs.org/en/features/css-modules.html
This way your CSS will still be scoped and not part of your global styling.
I just dropped my fadeTransition.css file into my vue app /assets folder, and import like this:
<template>
<transition name="fade">
<div v-if="showContent">test</div>
</transition>
</template>
<script>
import '#/assets/fadeTransition.css';
// component definition
</script>
fadeTransition.css
/* fade menus in, not out */
.fade-enter-active {
transition: opacity .5s;
}
.fade-enter {
opacity: 0;
}
Clean and simple. Should work for scss, too.
Cheers!

Resources