Skip to content

Conversation

@elliott-with-the-longest-name-on-github
Copy link
Contributor

@elliott-with-the-longest-name-on-github elliott-with-the-longest-name-on-github commented Jan 10, 2026

Summary

Fixes #13878 - CSS styles for conditionally-rendered components are not applied when inlineStyleThreshold > 0 in production builds (regression since v2.21.3).

Problem

When inlineStyleThreshold is set, SvelteKit inlines CSS below the threshold into the HTML. The previous implementation (from #13723) used server CSS content for inlining, but server builds tree-shake CSS for components that aren't rendered during SSR (e.g., components inside {#if} blocks where the condition is initially false).

This caused styles to be missing when those components were later rendered client-side, because:

  1. The server CSS didn't include their styles (tree-shaken)
  2. The client thought styles were already loaded (since the parent's CSS was inlined)

Solution

Use client CSS content directly instead of mapping client CSS files to server CSS files. The client build includes CSS for all components in the dependency tree, including conditionally-rendered ones.

To handle asset URLs (fonts, images) that use relative paths in client CSS, the fix rewrites url() references to absolute paths using the configured assets base path.

This approach is simpler than the previous implementation - we no longer need the server_bundle parameter that was added in #13723, since we're not doing any client-to-server CSS mapping anymore.

Changes

  • build_server.js: Use client CSS content directly with URL rewriting, remove server bundle dependency
  • index.js: Stop passing server bundle to build_server_nodes
  • analyse.js: Update call site to match new signature
  • Added test case for conditionally-rendered components with inlineStyleThreshold

Testing

  • Added new test: "styles conditionally rendered components when parent has styles"
  • All existing inlineStyleThreshold tests pass
  • All options test app tests pass

… rendered components

Server builds tree-shake CSS for components inside conditional blocks (e.g., {#if})
that aren't rendered during SSR. This caused styles to be missing when those
components were later rendered client-side.

The fix uses client CSS content directly (which includes all component styles)
and rewrites relative url() references to absolute paths for proper asset loading.

Fixes #13878
@changeset-bot
Copy link

changeset-bot bot commented Jan 10, 2026

🦋 Changeset detected

Latest commit: 600928d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@svelte-docs-bot
Copy link

The server bundle is no longer needed since we now use client CSS content
directly for inlining, rather than mapping client to server stylesheets.
@elliott-with-the-longest-name-on-github
Copy link
Contributor Author

@teemingc What do you think of this? I picked this one as a pesky, hard-to-diagnose bug to test OpenCode / Opus 4.5 with. AFAICT it works (the test fails on main and passes here), but I don't know if there are any negative downstream affects here. My main points of concern are the rewriting of the url references and the possibility of including too much CSS (as the client can't treeshake as much CSS, which is... actually the cause of the problem in the first place FWIW).

@teemingc
Copy link
Member

teemingc commented Jan 10, 2026

You're right about both downsides to this approach. As an alternative, I was trying to figure out how/why Vite bundles the CSS for each page because I can see that vite-plugin-svelte emits the CSS for each Svelte component individually for both client and server builds. I was hoping and looking for a way to tell Vite to emit all CSS from components in that page for the server output.

@elliott-with-the-longest-name-on-github
Copy link
Contributor Author

I guess that solution would have half of the issues of the above, right? As in, we'd still potentially ship "more" CSS than necessary (basically purposefully disabling some level of treeshaking) but we'd avoid having to hackily rewrite URLs?

@teemingc
Copy link
Member

Actually, treeshaking would still work as intended. It’s only suppose to happen for dynamically imported components anyway.

There’s two ways to go about this:

  1. Align server CSS with client CSS. You’ll notice the repro produces the correct amount of css for the page (all statically imported components). We want the server css to do the same.

  2. Align client CSS with server CSS. The only reason the conditionally rendered component’s CSS isn’t loaded when it first renders is because the page was suppose to have already loaded it (it’s present in the client styles). But this isn’t the case since we inlined the server styles instead and those don’t include it although they are styles for the same entrypoint

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Some CSS styles not applied with non-zero inlineStyleThreshold since v2.21.3

2 participants