Vue3 Styling Nested Single File Components

Viewed 758

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.

enter image description here

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!

1 Answers

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