Skip to content

@tailwindcss/vite errors on Svelte component <style> blocks: 'Invalid declaration: <imported-name>' #20000

@dceprano

Description

@dceprano

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions