Non-scoped styling in components applied only once when switching routes - vue.js

Vue.js documentation for Scoped CSS mentions that
You can include both scoped and non-scoped styles in the same component
I built the example application for vue-router and used two single file components instead of the string templates of the example - the rendering is as expected.
I then tried to apply both scoped and non-scoped styles in the components. In the first one I have
<style scoped>
div {
color: white;
background-color: blue;
}
</style>
<style>
body {
background-color: green;
}
</style>
and the second one
<style scoped>
div {
color: white;
background-color: red;
}
</style>
<style>
body {
background-color: yellow;
}
</style>
The idea is to have the whole body background switch when choosing a specific route.
The scoped styles are OK - they change depending on the route.
The non-scoped ones do not (screenshots are from Chrome Dev Tools):
on initial application load (non routed yet) the background is white (which is OK - this is the default one and there is no route for /).
when choosing a route, the style for the body is applied correctly (say, green from the first component)
when switching routes and loading the second component the background changes to the new color, it looks like from Chrome Dev Tools that the current style for background-color is overwritten. All the other components elements are correctly rendered (content and scoped styling)
further switches keep the same background (and again, other elements of the relevant component are rendered correctly). There are no changes in Chrome Dev Tools (the last view above is unchanged)
In other words, it looks like the style is stacked and previously overwritten properties are not updated Is this expected behaviour?

I opened a bug report for this and it ended up being expected behaviour. The summary from the report comments:
Thorsten Lünborg:
Yes, this is expected. Vue (or rather, webpack) does not insert and
remove these styles, as you seem to think. They are injected into the
head once the component renders, and never removed.
A common pattern is to extarct all CSS into a single .css file in
production, which would have the same result.
My summary in the context of the question:
initially (no route, no component rendered) nothing was injected
the first component is rendered on route switch, its style is injected
the second component is rendered on route switch, its style is injected and overwrites the previous style
further route switches do not inject anything as each component was already rendered once. The last style used therefore stays as the authoritative one.
I will therefore fallback on binding the body class to the current component's data

Related

How to build web component with styling library using vite and vue 3?

I am able to build vue web component and load it in other pages, but I can't find document how to correctly include a UI framework. It seems the web component is under shadowDOM and import css using style tag won't work.
(Add the CDN link in the template and style is applied)
Any hint on any framework, Vuetify or Ant Design or Tailwind CSS will be appreciated.
Similar question: Vuetify build as Web component style not showing
Using custom elements without Shadow DOM is trivial. Just add like the way you do traditionally. However, with Shadow DOM, things are tricky. Only inheritable CSS styles pass through the Shadow DOM. Everything else is blocked. No straight forward integration with existing design systems (Vuetify, Ant, etc.) is not directly possible if that library is only exposing global CSS.
If the design system or a component library is exposing styles i.e. css files for individual components, then you can that with some effort.
The best solution is to use constructable stylesheet. You can use a bundler like Webpack to load the stylesheet for individual component (if and only if it is provided) as a string and feed it to the stylesheet constructor method as illustrated here.
// Read SCSS file as a raw CSS text using Webpack/Rollup/Parcel
import styleText from './my-component.scss';
const sheet = new CSSStyleSheet();sheet.replaceSync(styleText);
// Use the sheet inside the web component constructor
shadowRoot.adoptedStyleSheets = [sheet];
However, Firefox and Safari are yet to implement it.
If you need a fallback, then there are ways that are not so clean. Approach is same. Import the CSS/SCSS as a string and using the template literal, add it to the element's inner style tag.
import styleText from 'ant/button.css';
class FancyComponent extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<!-- Styles are scoped -->
<style>
${styleText}
</style>
<div>
<p>Hello World</p>
</div>
`;
}
}
customElements.define('fancy-comp', FacyComponent);
This all relies on the assumption that ant/material/veutify is exposing styles as individual files instead of one global file.
Alternately, the browsers have started supporting the link tag inside the Shadow DOM. But again it is really useful if you have styles for individual components. Read more about that here.

SFC styles not being applied with testing-library

I'm adding unit tests to our vue codebase and finding difficult to test an element visibility.
I render the component as usual and as recommended in the examples of the testing-library documentation, but the styles are not being applied to the DOM debugging (using screen.debug). Any clue on this?
Example:
Inside the component
<template>
<span class="error-message>content here</span>
</template>
<style lang="scss" scoped>
.error-message {
display: none
}
In the (failing) test file:
render(Component);
expect(screen.getByText(/content here/i)).not.toBeVisible();
screen.debug();
The test fails because the element is visible. The debug prints the DOM as expected, except for the data-v-* classes that we usually see on browser inspect.
I ended up changing the way the assertion was being made.
As i understand, the styles being applied through the class error-message is not going to be seen by the jest-dom because it wont be inject to the DOM's header in the test environment. So what i did was to assert the class names in the element.

Nuxt how to override global css for a page

I have a project with multiple layouts, I have added the CSS globally in nuxt.config.js file like this:
css: [
'#/assets/plugins/fontawesome-free/css/all.min.css',
'#/assets/plugins/ionicons/css/ionicons.min.css',
'#/assets/plugins/feather/feather.css',
'#/assets/css/style.css',
'#/assets/css/custom-style.css',
'#/assets/css/skins.css',
]
but I want for just 1 layout to remove all css imported because the file being served is a static HTML file with all the styles inline.
Is this possible in nuxt and if not, what is the best possible workaround?
You could add the css files in your layout in a normal style block and not in nuxt.config.js.
<style lang="scss">
#import ...;
</style>
Then you can use another layout without these css files.
Remove the scope from your style on the .vue file where you want to override the global style:
<style s̶c̶o̶p̶e̶d̶>
.ProseMirror p {
color: rgb(236, 10, 10);
}
</style>
I wonder too, if you would be better off creating new vue layout files in the layout folder and applying those styles to the pages you want to affect globally, instead of pulling the css through nuxt.config.js file. Then, on the specific page that you want to override the global style, just remove the scope as I mention above. Just a thought.

Vue.js showing content that should be hidden by a v-if tag when user refreshes page

I have a Vue.js app that loads content in the created() method. I use a v-if tag to hide all of my UI until that content is loaded and ready to go. It works fine on the initial load, but if a user were to hit refresh in Chrome then the app displays (flashes momentarily) content that would not otherwise be displayed (based on the data being loaded in created).
From what I understand using the v-if tag, with a flag from my vuex store that indicates when the load has completed, is the proper way to hide content until I am ready to display it.
How can I avoid having the content flash on the refresh?
Vue.JS has solved this using the v-cloak directive. (see docs)
You add the v-cloak directive to your application's root element:
<div id="app" v-cloak>
...
</div>
Then add this CSS rule to your application's stylesheet:
[v-cloak] {
display: none;
}
Everything within the app div will then be hidden until Vue has initialized.

How to change css styles of Google Plus Pages Badge?

I configuring standart badge on https://developers.google.com/+/plugins/badge/config
For it Google+ rendering iframe content.
How i can change classes styles inside iframe?
At this time it seems there is currently no way to do it. I tried editing the CSS to get rid of the annoying border and white background:
.yeb {
background-color: none !important;
border: 0px solid gainsboro !important;
}
But since the CSS is in the Google server you can't override the iFrame. It is a current feature request in the Google forums.
You can do a little trick:
The HTML for the iframe:
<div id="social_gplus_circle">
<div id="social_gplus_circle_inner"><g:plus href="https://plus.google.com/105379671042990608528" size="smallbadge"></g:plus></div>
</div>
The CSS for the trick:
#social_gplus_circle{overflow: hidden;width: 105px;height: 45px;padding-top: 7px;}
#social_gplus_circle_inner{overflow: hidden;position: relative;top: -20px;left: -130px;height: 50px;width: 276px;}
iframe{display: block!important;}
I did not made this myself, I got it from a google forum.
You can't change it , because the google plus code is not on your server.
You can only modify the iframe CSS , if your page and the iframe code is on the same server.