Search code examples
tailwind-csssveltevitepostcsssvelte-3

Style leak from Svelte component


Question:

I'd like to use a plugin (daisyUI) for TailwindCSS in one of my Svelte components. It looks like style information leaks from this component and affects the entire site. How can I avoid this?

I don't think this is related to daisyUI specifically. Below I'm describing a minimal reproducible example based on sveltekit. But the problem is not related to sveltekit. I'm encountering this in the development of a webextension which doesn't use sveltekit. The sveltekit setup is only to make the smallest possible demonstration for this question.

To illustrate the problem, I've set up a sveltekit skeleton project, then added one single additional svelte component which uses Tailwind. When I add the plugin, the background color of my page turns from white to gray. I don't understand how this can happen, as far as I can see, I'm only using Tailwind within that component. But the style seems to leak.

Minimal example on github:

Fastest way to reproduce:

git clone [email protected]:lhk/minimum_example.git
cd minimum_example
npm install
npm run dev -- -- open

Now you can edit tailwind.config.cjs and add/remove the plugin:

plugins: [
  //require("daisyui")
],

Step-by-step explanation

I'd like to use Svelte together with Tailwind and DaisyUI.

Here's a minimal project setup

# choose the skeleton project, typescript syntax and no to everything else
npm create svelte@latest minimum_example

cd minimum_example
npm install
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init tailwind.config.cjs -p
npm i --save-dev daisyui

Now edit tailwind.config.cjs:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,js,svelte,ts}'], theme: {
    extend: {},
  },
  plugins: [
    //require("daisyui")
  ],
}

Add a new Svelte component under src/components/Problem.svelte:

<p class="bg-blue-700">Using Tailwind class</p>

<style lang="postcss">
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
</style>

And include it in src/routes/+page.svelte:

<script lang="ts">
    import Problem from "./../components/Problem.svelte";
</script>

<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

<Problem></Problem>

You can run the project with

npm run dev -- -- open

If you open the website you'll see the sveltekit skeleton app, plus one paragraph with a blue background (this is my test that Tailwind is working). Now you can uncomment the plugin in tailwind.config.cjs. The background of the page will turn gray. I think this is a theme that somehow leaks from the Tailwind plugin to the entire site.


Solution

  • The way you use tailwind with svelte is quite wrong. The tldr answer is remove @tailwind directives and use @apply instead.

    <p class="my-element">Using Tailwind class</p>
    
    <style lang="postcss">
        .my-element {
          @apply bg-blue-700;
        }
    </style>
    

    The way how svelte scopes styles is by using a unique class name alongside your custom selector, e.g. .my-element becomes .my-element.svelte-x57u2q. This also means you must use a selector so that this scoping mechanism can kick in.

    But with vanilla tailwind, those builtin class names have to be global in order to be useful, in other word “leaked”. This is by design, not bug.

    So if you want to use tailwind but also leverage svelte’s scoped style, @apply is the only solution.

    The official doc has a section titled Using @apply with per-component CSS that reveals more technical details, I think it’s worth reading.