Vue3 Styling Nested Single File Components [duplicate] - vue.js

This question already has an answer here:
How do I create a Vue 3 custom element, including child component styles?
(1 answer)
Closed 11 months ago.
I am using Vue 3 to create some Web Components. I would like to use Single File Components and be able to nest them and deploy, but the styling is missing on the child component.
I have simplified what I am trying to do with the following example.
MDivElement.ce.vue - a div that wraps MButton
<template>
<div id="mdiv">
MDiv
<MButtonElement>MButton</MButtonElement>
</div>
</template>
<script>
import MButtonElement from "./MButtonElement.ce.vue";
export default {
components: {
MButtonElement,
},
};
</script>
<style scoped>
#mdiv {
color: blue;
}
</style>
MButtonElement.ce.vue - a simple Button
<template>
<button id="mbutton">MButton</button>
</template>
<script>
export default {};
</script>
<style scoped>
#mbutton {
color: red;
}
</style>
Example
My problem is that all the style is gone from the button when using <m-div></m-div>
Can SFC be nested (with styles) and used as Web Components?
I will compile using Vite and distribute.
Thanks!

As of April 2022 and Vue 3.2 there is open bug #4662 about this behavior, as in the question How do I create a Vue 3 custom element, including child component styles?. One of the workarounds suggested in the bug is to make sure you are consuming the custom element by name in your template rather than using Vue and import to compose your components in custom elements.
You may need to update your SFC build plugin version so that it properly understands your .ce.vue extensions as custom elements that treat their styles differently. See the SFC as Custom Element docs:
defineCustomElement also works with Vue Single-File Components (SFCs). However, with the default tooling setup, the <style> inside the SFCs will still be extracted and merged into a single CSS file during production build. When using an SFC as a custom element, it is often desirable to inject the <style> tags into the custom element's shadow root instead.
The official SFC toolings support importing SFCs in "custom element mode" (requires #vitejs/plugin-vue#^1.4.0 or vue-loader#^16.5.0). An SFC loaded in custom element mode inlines its <style> tags as strings of CSS and exposes them under the component's styles option. This will be picked up by defineCustomElement and injected into the element's shadow root when instantiated.
Finally, as another potential workaround, you can to put the <style> within the element itself in order to accommodate the Shadow DOM technique that powers Web Components, or otherwise include the styles in the defineCustomElement Vue call specific to Custom Elements.
As in the MDN web components documentation:
You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a <style> element. The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.
In contrast, Vue SFC scoping rolls up the styles into the element itself, but applies the styles to the whole document with an arbitrary data element (data-v-f3f3eg9 in the docs). Those styles are applied outside of the shadow dom, and consequently your styles set in SFC <style> tags aren't inherited inside of it.

Related

VueJS & Vuetify - How to use JS variables in Sass

Using VueJS, I need to display different colors for each user. The color depends on the user settings.
In my vuetify.js, I have:
export default new Vuetify({
theme: {
themes: {
light: {
primary: user.colorMain ? user.colorMain : '#F39200',
It works when I use:
$vuetify.theme.themes.light.primary
in my components.
But I would need to override the Sass variables too, in my variable.scss file:
$primary-color: #f39200;
Is there a way to override my sass variables dynamically from a JS variable?
tl:dr; no, it's not possible to change Sass variable values at runtime, because they no longer exist at runtime. They have been translated into plain (static) CSS.
However, like with any CSS values, you can override them.
Sass variables are only used to pre-process SCSS into CSS at compile time. The result of compilation is static CSS, loaded when the app is mounted. In simpler terms, the app doesn't know that CSS was preprocessed from an SCSS source. For it, it's static CSS.
Example:
$primary-color: #f39200;
.my-button { color: $primary-color; }
will output the following CSS code:
.my-button { color: #f39200; }
If you want runtime dynamic values, you have two options:
Use CSS variables.
Produce the following CSS, via your preferred method (from SCSS/CSS/Stylus, doesn't matter, as long as this is the output):
.my-button { color: var(--primary-color); }
... and, anywhere in the chain of parents or on the element itself:
<div :style="{'--primary-color': someDynamicColor }" />
With the above in place, when you change someDynamicColor, at runtime, the color changes in DOM.
Use Vue3's "reactive styles" feature:
<script>
export default {
data: () => ({ someDynamicColor: 'red' })
}
</script>
<style>
.my-button {
color: v-bind('someDynamicColor');
}
</style>
Again, this is dynamic. If you change/animate the value of someDynamicColor on the element, the CSS value will be applied in DOM. It doesn't have to be a data prop, it can be a prop, computed, ...
Important notes:
when using CSS variables (1.), the value of var(--primary-color) doesn't have to be set in the same component, but it has to be set on a direct ancestor of the current DOM element.
when using reactive styles (2.), the prop/computed referenced in CSS/SCSS has to be set in the current component's scope.
Under the hood, reactive styles also use CSS variables: they're uniquely named at compile time.
CSS variables don't use specificity. If you override the value set by some grand-parent at parent level, the child has no way of reading the grand-parent's value, regardless of specificity. If you have such a case, you probably want to manage the grandparent value in external state and provide it to both grand-parent and child.

Is Tailwind class binding possible through Storyblok?

I'm trying to develop some components that will be used by our content editors in Storyblok and there's a use case where we would like to define layout properties (using Tailwind's classes) through props that will be coming from Storyblok components.
As an example,
I am passing the width prop through storyblok giving a value of w-1/2 which is a Tailwind class. As you see on the right the class is applied just fine to the element but there's no actual impact on the page. I have tried the same with many other classes (either for background or border colors or for text styling etc, tried to use Tailwind classes as props coming from Storyblok but didn't work).
My only guess is that Nuxt is a server side application and the CSS gets compiled on build time, therefore any new class binding to the DOM will not reflect the actual CSS that they represent. Is this right? If yes, is there a way to make this happen and work?
The code for the widthSetter component is as simple as that
<template>
{{blok.width}}
<div v-editable="blok" :class="[ blok.width ]">
<component
v-for="value in blok.blocks"
:key="value._uid"
:is="value.component"
:blok="value"
/>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
blok: {
type: Object,
required: true,
},
})
</script>
You need to add Complete Class Names.
As there is no w-1/2 in your code, TW won't generate the class.
You can workaround the issue by adding the class to safelist.
Doc: https://tailwindcss.com/docs/content-configuration#safelisting-classes
module.exports = {
safelist: ['w-1/2'],
//...
}
Then w-1/2 utility will be generated regardless if it shows up in your code or not.

Vue3 scoped classes are not applied for elements not declared in the template

I have a single-file vue3 component.
Its template looks like this.
<template>
<div ref="elements"></div>
</template>
I have scoped styles in the same file:
<style scoped>
.el {
color: red;
}
</style>
Now I want to add element in the script.
<script>
export default {
name: "App",
mounted() {
const div = this.$refs.elements;
const el = document.createElement("div");
el.setAttribute("class", "el");
el.innerText = "Hello World!";
div.appendChild(el);
},
};
</script>
The result shows that the element is not styled according to the class in the scoped styles.
Is there a way to apply styling to the elements added through script, while keeping styles in the scope?
After some research this seems to be an answer.
Vue scoped styling is the internal Vue feature. So, there should be a way to distinguish same class names on different components. Vue does it by adding a special id to each class name. You can find it by inspecting elements in the browser (e.g. data-v-a2c3afe).
When building a component, Vue processes its template and properly tracks only nodes you have declared for rendering there. It does not know anything it might get from somewhere. It actually makes sense and pushes you to write everything you expect to see in the template, so that it does not happen that you suddenly see something wrong in the DOM (especially if you are taking someone's code).
I have rewritten code, so that I still have scoped styles, but with no elements appending from the script and the code now allows to clearly see what is being rendered. This is probably the property of any framework - it makes you to stick to some patterns, so that everyone in the team/community knows what behavior to expect and writes more consistent code.

Why is Vue stripping the style attribute in this one component

I have one Vue component in which any style attribute on any tag in the component template is stripped from the rendered output. Vue 2.6. No build step. The component template is defined in a js template string. Style is stripped whether it's bound or static. Style attributes on neighbouring components are rendered just fine. There are no carriage returns in my styles. Does this suggest anything to anyone ?
Here's the start of my component...
Vue.component("justif-choice", {
template: `
<div style="top:500">
qsdfqdf
</div>
`,
...
This is the line that calls my component...
<xsl:text disable-output-escaping="yes">
<justif-choice ref="justifChoice" ></justif-choice>
And here's the rendered output...
Ok found it. The style rule I was testing, top:500, needed 'px' to be valid. So it was the browser stripping it, not Vue? There may be more to this story because the same code works fine on another page, but adding 'px' has fixed it.

Angular 2 extending component - how to inherit styles

I want to extend component and override its html template only, but left all logic and styles. Unfortunately I see that all styles defined in parent component was not inherited. Is there the way to use styles of the component that was extended?
P.S. This is comoponent defined in node_modules, so I can edit source code.
If you want to bleed styles down to a component. You would put this on your target component.
#Component({
encapsulation: ViewEncapsulation.None
})