Search code examples
tailwind-csssvelteweb-component

Is it possible to embed tailwind classes in a Svelte custom component isolated inside the Shadow DOM?


I am building a custom component (web component) in Svelte and Tailwind.

My goal is to make this component have self-contained styling, but I have trouble with including the Tailwind stylesheet inside my custom component as it is isolated inside a Shadow DOM.

MyComponent.svelte

<svelte:options
  customElement={{
    tag: "my-component",
  }}
/>

<script>
  import Child from "./Child.svelte";
</script>

<span class="text-blue-500 p-4">
  Foo
  <Child />
</span>

Child.svelte

<span class="p-4 text-red-500">Bar</span>

When using <my-component></my-component>, it is unstyled, as the component is inside the ShadowDOM, it does not have access to the global Tailwind stylesheet.

Is there a way to embed the full Tailwind stylesheet for my component inside the Shadow DOM?

I have had some success with using the svelte preprocessor

svelte.config.js

export default {
  preprocess: [preprocess(), vitePreprocess()],
};

and including a link to my tailwind stylesheet in my component

<style lang="postcss" src="./../app.css"></style>

but this is quite cumbersome, as I need to add this line for EVERY child component of MyComponent.svelte


Solution

  • The solution we opted for, was to add a script that imports the css into the Web Components. Inside the html document, we added a <template>:

        <my-component></my-component>
        <template id="my-compontent-styling">
          <style>
            @import url("/assets/app.css")
          </style>
        </template>
    

    And then in MyComponent.svelte, we inject the css

    <svelte:options
      customElement={{
        tag: "my-component",
      }}
    />
    
    <script>
      import Child from "./Child.svelte";
    
      function applyStyling(divElement) {
        if (!divElement) return;
        const template = document.getElementById("my-component-styling");
        const node = document.importNode(template.content, true);
        divElement.parentNode.appendChild(node);
      }
    
      let divElement;
      $: applyStyling(divElement);
    </script>
    
    <div bind:this={divElement}>
      <span class="text-blue-500 p-4">
        Foo
        <Child />
      </span>
    </div>