sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.76k stars 1.95k forks source link

Importing CSS file in route applies it on hover, and the style is not cleaned up when navigating away from the route #12933

Open khromov opened 3 weeks ago

khromov commented 3 weeks ago

Describe the bug

If you do something like import '$lib/layout1.css'; in a +layout or +page, the CSS is incorrectly applied on hover (probably due to the preload functionality).

Additionally when navigating away from the route, the style tag is not properly unmounted, leaving the CSS in place.

For reference, I also included a css import using svelte-preprocess, which shows that scoped CSS does work as intended.

This is a question that has come up many times in the Svelte Discord, and is usually unexpected by the user.

https://github.com/user-attachments/assets/aef78df0-4c56-4551-b1e4-05605199c340

Reproduction

https://github.com/khromov/sveltekit-css-import-repro

npm i && npm run dev

Logs

-

System Info

System:
    OS: macOS 15.0
    CPU: (12) arm64 Apple M2 Pro
    Memory: 212.53 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.8.0 - ~/.nvm/versions/node/v22.8.0/bin/node
    npm: 10.8.3 - ~/node_modules/.bin/npm
    bun: 1.1.25 - ~/.bun/bin/bun
  Browsers:
    Safari: 18.0
  npmPackages:
    @sveltejs/adapter-auto: ^3.0.0 => 3.3.1 
    @sveltejs/kit: ^2.0.0 => 2.7.3 
    @sveltejs/vite-plugin-svelte: ^4.0.0 => 4.0.0 
    svelte: ^5.0.0 => 5.1.9 
    vite: ^5.0.3 => 5.4.10

Severity

annoyance

Additional Information

No response

Conduitry commented 3 weeks ago

Is there anything reasonable to do here besides document it? The many other times this has come up (e.g. #10595), our answer has been that you shouldn't be loading global styles from a component. @benmccann What exactly are you thinking would happen in 3.0?

khromov commented 3 weeks ago

Right now workaround seems to be to use svelte-preprocess instead if you want to use a normal css file scoped, but this requires you to change to svelte-preprocess instead of @sveltejs/vite-plugin-svelte which is (imho) not very well documented, and it's not clear what are the upsides and downsides of doing this.

Adding support for the <style src=".."> syntax in @sveltejs/vite-plugin-svelte would go a long way I think, as well as documenting common workarounds, eg if my .css file has a body selector, what should I do with it when migrating vanilla CSS to scoped CSS?

Rich-Harris commented 3 weeks ago

Pretty sure this is a wontfix. Importing a .css file in Vite is side-effectful, as it needs to be. If we didn't use Vite's handling and instead injected the styles ourselves (and added a mechanism for tracking which currently mounted components use which styles) the styles would have to exist in JS which is no good.

theetrain commented 3 weeks ago

The workaround is to use <svelte:head> and Vite's ?url import hint.

<script>
  import style from '$lib/style1.css?url'
</script>

<svelte:head>
  <link rel="stylesheet" href={style} />
</svelte:head>

This will add and remove stylesheets as you switch in and out of pages using the above code. Also agreed with Vite behaviour since all modules become part of application state the moment they're loaded. Unless there's a way to 'unload' ESM, I don't think that behaviour should change.

Rich-Harris commented 3 weeks ago

Note that this workaround will result in FOUC (for client-side navigation) and duplication

benmccann commented 3 weeks ago

I feel like we could probably at least catch this during build and issue a warning pointing to some documentation on the issue

I believe we already store a list of which CSS files are used in which layout files. I would kind of expect that we could use that to detect which should be removed when navigating away from a layout, but maybe I didn't catch why that wouldn't work or we don't even want to go down the path of supporting global CSS in components in the first place