Svelte does not handle external CSS imports correctly when compiling components as custom elements (Web Components)
Describe the bug
If a component is exported as a custom element using
<svelte:options customElement="some-component" />
any foreign CSS imports that works in normal Svelte componentes don't work on the custom element:
-
Using
import './custom.css';works as expected in regular Svelte components, but when used in a custom element, the styles are not properly scoped or injected into the shadow root.
-
Adding a stylesheet with
<svelte:head> <link rel="stylesheet" href="/custom.css" /> </svelte:head>causes Svelte to insert the
<link>into the document's<head>, not into the custom element's shadow root. This means the styles are global and not encapsulated within the custom element, which causes them to not affect it at all (see AquaCounter for a manual workaround). -
Importing CSS via a
<style>tag:<style> @import "./custom.css"; </style>partially works, but if the imported CSS contains selectors not used in the component's markup, Svelte emits a
'css-unused-selector'warning. Additionally, the imported CSS is scoped only to the current component and is not available to child components, so it cannot act as a "global" style for the custom element.
Reproduction
I've created a repository to demonstrate the bug and some workarounds one can use to import foreign CSSs
https://github.com/GCastilho/svelte-css-imports-custom-elements-bug-demo
Instructions and details are on README
Logs
N/A
System Info
System:
OS: Windows 11 10.0.26100
CPU: (14) x64 Intel(R) Core(TM) Ultra 5 135U
Memory: 3.91 GB / 15.52 GB
Binaries:
Node: 22.16.0 - C:\Program Files\nodejs\node.EXE
pnpm: 10.11.1 - C:\Program Files\nodejs\pnpm.CMD
Browsers:
Edge: Chromium (134.0.3124.85)
npmPackages:
svelte: ^5.28.1 => 5.33.14
Severity
blocking all usage of svelte
I've created a preprocessor to workaround this issue (global CSS only). It's hacky but it get the job done
https://gist.github.com/GCastilho/edf7c6a96b6bfa8e9dabb58b3e88931f
Besides being highly interested in this, I think its also worth noting that even when using the component <style> Svelte fails to HMR the Shadow DOM component properly. The DOM updates if the HTML template is updated, but the styles aren't.