Bug
@tailwindcss/vite@4.1.18 (with tailwindcss@4.1.18) fails to compile component-local <style> blocks in Svelte SFCs when the same SFC's <script> contains a import { x } from "svelte" statement. The plugin appears to receive the entire .svelte file content (script + template + style) instead of just the <style> block, then chokes parsing the JS as CSS.
The CSS for the affected component is silently dropped — only Tailwind utility classes (in the global pipeline) work; component-local .foo { ... } rules in the <style> block never reach the browser.
Reproduction
pkg.json:
{
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tailwindcss/vite": "^4.1.18",
"tailwindcss": "^4.1.18",
"svelte": "^4.2.20",
"vite": "^5.4.21"
}
}
MyCard.svelte:
<script>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
</script>
<div class="card"><slot/></div>
<style>
.card { height: 450px; background: #f5f1ea; }
</style>
Then in dev mode, request the CSS chunk Vite produces:
curl -s "http://localhost:5173/src/components/MyCard.svelte?type=style&lang.css"
Expected
Returns the compiled CSS for the <style> block (.card { height: 450px; ... }).
Actual
Returns a Vite error overlay HTML page. The error payload:
{
"message": "Invalid declaration: `createEventDispatcher`",
"plugin": "@tailwindcss/vite:generate:serve",
"pluginCode": "<script>\n import { createEventDispatcher } from \"svelte\";\n ...\n</script>\n\n<div class=\"card\">...</div>\n\n<style>\n .card { height: 450px; ... }\n</style>"
}
Stack:
at $e (tailwindcss/dist/chunk-CT46QCH7.mjs:1:3316)
at rf (tailwindcss/dist/chunk-CT46QCH7.mjs:38:1384)
at lu (@tailwindcss/node/dist/index.mjs:10:3464)
at F.generate (@tailwindcss/vite/dist/index.mjs:1:4355)
at TransformPluginContext.handler (@tailwindcss/vite/dist/index.mjs:1:2548)
at PluginContainer.transform (vite/...)
The pluginCode field confirms the plugin is being handed the whole .svelte file source, including the <script> block, when Vite asks it to generate CSS for the ?type=style&lang.css virtual module. The plugin's CSS parser then fails on the first import { ... } line it sees.
Impact
- Every Svelte SFC with both an
import in its <script> and styles in its <style> block silently loses its CSS.
- The error appears only when explicitly hitting the
?type=style&lang.css URL (Vite's HMR path); the initial wails dev / vite dev page load may show stale or no CSS for affected components.
- Tailwind utility classes (
class="h-[450px]" etc.) still work because they're processed via a different path.
- Inline
style="..." attributes still work for the same reason.
Workaround
Move all sizing rules out of <style> and into either:
- Tailwind utility classes on the markup, or
- Inline
style="..." attributes
Both bypass the broken pipeline.
Environment
- macOS 15 (arm64)
- Node 24
- Vite 5.4.21
- @sveltejs/vite-plugin-svelte 3.1.2
- Svelte 4.2.20
- @tailwindcss/vite 4.1.18
- tailwindcss 4.1.18
Why I think this is on the Tailwind side
@sveltejs/vite-plugin-svelte is responsible for parsing the SFC and exposing the <style> block as a separate virtual module via ?type=style&lang.css. When a CSS-language plugin (@tailwindcss/vite here) registers a transform hook for *.css, Vite passes it the contents of that virtual module — which should be just the .card { ... } body, not the surrounding <script> + template.
Either the Tailwind plugin is matching too loosely (intercepting the transform when the underlying file is .svelte and re-reading the full file) or it's not respecting the Vite virtual-module convention. The pluginCode payload (full SFC body) confirms whichever it is.
A working fix would be: only run the Tailwind transform when the virtual module's id matches *.css AND the source content actually parses as CSS — bail early on any chunk that contains a <script> or <template> tag.
Bug
@tailwindcss/vite@4.1.18(withtailwindcss@4.1.18) fails to compile component-local<style>blocks in Svelte SFCs when the same SFC's<script>contains aimport { x } from "svelte"statement. The plugin appears to receive the entire.sveltefile content (script + template + style) instead of just the<style>block, then chokes parsing the JS as CSS.The CSS for the affected component is silently dropped — only Tailwind utility classes (in the global pipeline) work; component-local
.foo { ... }rules in the<style>block never reach the browser.Reproduction
pkg.json:{ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^3.1.2", "@tailwindcss/vite": "^4.1.18", "tailwindcss": "^4.1.18", "svelte": "^4.2.20", "vite": "^5.4.21" } }MyCard.svelte:Then in dev mode, request the CSS chunk Vite produces:
Expected
Returns the compiled CSS for the
<style>block (.card { height: 450px; ... }).Actual
Returns a Vite error overlay HTML page. The error payload:
{ "message": "Invalid declaration: `createEventDispatcher`", "plugin": "@tailwindcss/vite:generate:serve", "pluginCode": "<script>\n import { createEventDispatcher } from \"svelte\";\n ...\n</script>\n\n<div class=\"card\">...</div>\n\n<style>\n .card { height: 450px; ... }\n</style>" }Stack:
The
pluginCodefield confirms the plugin is being handed the whole.sveltefile source, including the<script>block, when Vite asks it to generate CSS for the?type=style&lang.cssvirtual module. The plugin's CSS parser then fails on the firstimport { ... }line it sees.Impact
importin its<script>and styles in its<style>block silently loses its CSS.?type=style&lang.cssURL (Vite's HMR path); the initialwails dev/vite devpage load may show stale or no CSS for affected components.class="h-[450px]"etc.) still work because they're processed via a different path.style="..."attributes still work for the same reason.Workaround
Move all sizing rules out of
<style>and into either:style="..."attributesBoth bypass the broken pipeline.
Environment
Why I think this is on the Tailwind side
@sveltejs/vite-plugin-svelteis responsible for parsing the SFC and exposing the<style>block as a separate virtual module via?type=style&lang.css. When a CSS-language plugin (@tailwindcss/vitehere) registers atransformhook for*.css, Vite passes it the contents of that virtual module — which should be just the.card { ... }body, not the surrounding<script>+ template.Either the Tailwind plugin is matching too loosely (intercepting the transform when the underlying file is
.svelteand re-reading the full file) or it's not respecting the Vite virtual-module convention. ThepluginCodepayload (full SFC body) confirms whichever it is.A working fix would be: only run the Tailwind transform when the virtual module's
idmatches*.cssAND the source content actually parses as CSS — bail early on any chunk that contains a<script>or<template>tag.