Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create and style svelte 3 custom elements with nested components?

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://imgur.com/a/zZia566

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.

like image 201
Buk1m Avatar asked Sep 05 '19 09:09

Buk1m


1 Answers

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.

  • nested component in custom element does not inherit style #2605
  • :global(...) not working in custom elements #2969

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.

like image 177
Moon Avatar answered Nov 19 '22 09:11

Moon