9

I'm trying to create custom-element (web component) in svelte 3. I can't find any way to style nested components from css. Svelte removes styles before injecting them to <style> inside of ShadowDOM.

The problem is that I want to have nested components in my root element. For example:

  • RootComponent (svelte custom-element)
    • (imports) FooComponent
    • (imports) BarComponent

As stated here: svelte-custom-element

All the components imported to custom-element must have compiler option set to <svelte:options tag="component-name" />.

With this option set nested components works as expected and are injected into root's element ShadowDOM. The problem is that styles defined in nested components are not being injected. The workaround for this problem would be to inject them into root's element <style> as global styles within ShadowDom. (Un)fortunately svelte automatically removes all unused styles during compilation when custom elements not yet exist.

My goal is to create web component with svelte and then use it outside of svelte as native web-component.

Here is REPL

Custom elements do not really work on REPL as Conduitry wrote:

The compiler options in the REPL don't actually affect the code that >is run, just the code that is displayed. So enabling customElement >doesn't mean you are building and running a web component

So it's more like a code example than working one.

  1. I would like to know if there is another way to create svelte custom-element with nested component and proper styling.
  2. Is there a way to disable removing of unused css?

https://i.sstatic.net/FEeEC.jpg

from <div class="nested"> starts Nested component imported from Nested.svelte.

<style> element should have .nested class injected but it is removed by svelte compiler.

3 Answers 3

10

This is because when customElement option is on, each style in a component is injected into the shadowRoot of the custom element.

class YourComponent extends SvelteElement {
        constructor(options) {
            super();

            this.shadowRoot.innerHTML = `<style>.foo{color:red;}</style>`;
// continues

Thus, in order to make style appear, you must use svelte component as custom element, not as svelte component.

Your App.svelte should be like below.

<script>
    import Foo from './Foo.svelte'
    import Bar from './Bar.svelte'
</script>
<svelte:options tag="web-component" />

<foo-component/>
<bar-component/>

However, this neither solve the problems related with custom element.

  1. :global selector is not transformed into actual global selector.

  2. Every nested component will produce shadowRoot, whereas mostly you will want only top-level one.

Check out some issues below from svelte repository related to custom elements.

It seems like svelte does not fully support style cascading in custom element yet, should be handled in future.

Checked in svelte v3.12.1.

Sign up to request clarification or add additional context in comments.

Comments

0

Thanks to brunoalano for sending me this: svelte-custom-element-template. It solves the styling problem with custom build script.

Comments

0

You just have to prevent the compiler from removing unused CSS

Let's say we have a custom element : App.svelte

App.svelte imports a normal svelte component : ./components/Message.svelte

But when you do this, any styles inside Message.svelte will disappear.

Solution

  1. Move all content in the <style> tag of Message.svelte into the <style> tag of App.svelte
  2. Add this to script of App.svelte
let cssKeep: string = "";
  1. Add this to body of App.svelte
<span style="display: none;" class={cssKeep}><span class={cssKeep} /> </span>

This will prevent the compiler from removing any styles

Example

src/components/Message.svelte

<script lang="ts">
  export let content: string;
</script>

<p class="red"> {content} </p>

src/App.svelte

<svelte:options tag="my-element" />

<script lang="ts">
  import Message from "./components/Message.svelte";
  let cssKeep: string = "";
</script>

<Message content="hello" />

<span style="display: none;" class={cssKeep}><span class={cssKeep} /> </span>

<style>
  .red {
    color: red;
  }
</style>

vite.config.ts

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  build: {
    lib: {
      entry: './src/main.ts',
      name: 'MyElement'
    },
  },
  plugins: [
    svelte(
      {
        compilerOptions: {
          css: true,
        },
        exclude: "./src/App.svelte",
        emitCss: true,
      }
    ),
    svelte(
      {
        compilerOptions: {
          customElement: true,
          css: true,
        },
        exclude: "./src/components/**",
        emitCss: true,
      }
    ),
  ],
})


// guide: https://www.thisdot.co/blog/web-components-with-svelte

This may answer:

  • How to export web component in Svelte with nested Svelte components
  • Use Svelte components with custom elements
  • Styles missing when Svelte component imported into custom web component / custom element

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.