Hovering over a link loads CSS that affects the current page
Describe the bug
Under some conditions, hovering over a link preloads CSS that modifies the appearance of the current page.
https://user-images.githubusercontent.com/16927/216515321-3fecd177-0e28-4f24-8618-a4a9ec8f4056.mov
Preconditions
data-sveltekit-preload-data="hover"is set.- Either:
- CSS is loaded using an
importstatement in the<script>section. - Or there are
:globalCSS tags in the<style>section.
- CSS is loaded using an
I discovered this because I was using the @html tag to load Markdown-rendered HTML and I didn't want Svelte to remove the unused styles.
The only workaround I know of is for people not to use data-sveltekit-preload-data="hover".
Reproduction
- clone this repo: https://github.com/BMorearty/sveltekit-css-load-bug
pnpm installpnpm dev- Hit 'o' to open the browser
- Hover over the link on the page and watch the h1 grow in size when the CSS for the other page is loaded.
Logs
No response
System Info
System:
OS: macOS 13.1
CPU: (8) arm64 Apple M1
Memory: 148.63 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 18.12.1 - ~/.nvm/versions/node/v18.12.1/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v18.12.1/bin/yarn
npm: 8.19.2 - ~/.nvm/versions/node/v18.12.1/bin/npm
Browsers:
Chrome: 109.0.5414.119
Firefox: 109.0
Safari: 16.2
npmPackages:
@sveltejs/adapter-auto: ^1.0.0 => 1.0.2
@sveltejs/kit: ^1.0.0 => 1.3.10
svelte: ^3.54.0 => 3.55.1
vite: ^4.0.0 => 4.1.1
Severity
serious, but I can work around it
Additional Information
No response
See #5022 and #2783
Global styles, ie. ones marked with :global() or imported from a css file will stay on your page until your page is reloaded. Preloading another page's component preloads its imported css files as well making it affect your current page.
But you'd have to defend against unwanted styles with either css modules or another scoping mechanism even if you weren't preloading for when your users navigate back from the page that applies those styles.
Do we have a section in the documentation to clarify this behavior?
This also affects me. I understand from your comments why this is happening, but could you please elaborate on what scoping mechanisms are available to deal with this?
If I understand correctly, to use css modules I would have to rename bundled css files from other packages to .module.css.?
I suppose another way would be to import them with ?url and append them to head onMount as with legacy JS, but this again feels dirty.
What would you recommend to do with bundled css files?
EDIT: At the moment the only thing to isolate different parts of the routes that I have found is to use data-sveltekit-reload on their a links.
I also experienced this problem. Some pages had a +layout.svelte file that loaded a css file.
My workaround had to include both suggestions from @BMorearty and @Wtower:
- remove data-sveltekit-preload-data="hover"
- add data-sveltekit-reload to the 'a' tags for pages where said css would conflict
I realize that the best solution is to migrate all css into the svelte mechanism, but it wasn't feasible at the time.
For me setting data-sveltekit-preload-data="off" works
For imported CSS files, I am using this workaround:
<!-- src/routes/(template)/+layout.svelte -->
<script lang="ts">
import printFriendly from '/node_modules/print-friendly/dist/print-friendly.css?url';
</script>
<svelte:head>
<link rel="stylesheet" href={printFriendly} />
</svelte:head>
Preload the CSS file in a appropriate place to avoid layout shift when navigating.
<!-- src/routes/+layout.svelte -->
<script>
import printFriendly from '/node_modules/print-friendly/dist/print-friendly.css?url';
</script>
<svelte:head>
<link rel="preload" href={printFriendly} as="style" />
</svelte:head>
With this approach, anchor preload option does not have to be altered.
I'm afraid to say that the only viable workaround for Svelte's poor CSS management is to use something like Tailwind, because it ties the lifetime of element styles to the elements themselves, whereas Svelte's CSS management never unloads CSS ~because it's too hard~ by design.
@Wtower this is a basic usage of CSS Modules in SvelteKit (Vite)
/* src/routes/card.module.css */
article.card {
height: 50px;
width: 50px;
background-color: yellow;
}
article.card::after {
content: 'Hey!';
}
<!-- src/routes/+page.svelte -->
<script>
import cardStyles from './card.module.css';
</script>
<article class={cardStyles.card}></article>
<div class={cardStyles.card}></div>
The article.card style is only applied to the <article> element:
<!-- Rendered HTML -->
<article class="_card_805h2_3"></article>
<div class="_card_805h2_3"></div>
/* Generated CSS */
article._card_805h2_3 {
height: 50px;
width: 50px;
background-color: yellow;
}
To apply styles to a Svelte component: (e.g. +page.svelte)
/* src/routes/styles.module.css */
.root {
> article {
height: 50px;
width: 50px;
background-color: yellow;
}
> article::after {
content: 'Hey!';
}
}
<!-- src/routes/+page.svelte -->
<script>
import styles from './styles.module.css';
</script>
<!-- Limitation: wrapper element is needed. -->
<div class={styles.root}>
<article></article> <!-- styled -->
</div>
This can be used instead of the direct CSS import. (e.g. import './app.css)
[!NOTE]
Vite flattens nested CSS after build. Manual PostCSS setup is not needed.
- https://stackoverflow.com/a/77780440/12817553
- https://github.com/vitejs/vite/discussions/5393
/* 2.BY7NAjGJ.css */
._root_1b7bl_1 > article {
height: 50px;
width: 50px;
background-color: #ff0;
}
._root_1b7bl_1 > article:after {
content: 'Hey!';
}
Closing as duplicate of https://github.com/sveltejs/kit/issues/12933