diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13acdc3183d1..43725914687e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure color opacity modifiers work with OKLCH colors ([#14741](https://github.com/tailwindlabs/tailwindcss/pull/14741))
- Ensure changes to the input CSS file result in a full rebuild ([#14744](https://github.com/tailwindlabs/tailwindcss/pull/14744))
- Add `postcss` as a dependency of `@tailwindcss/postcss` ([#14750](https://github.com/tailwindlabs/tailwindcss/pull/14750))
+- Ensure loading stylesheets via the `?raw` and `?url` static asset query works when using the Vite plugin ([#14716](https://github.com/tailwindlabs/tailwindcss/pull/14716))
- _Upgrade (experimental)_: Migrate `flex-grow` to `grow` and `flex-shrink` to `shrink` ([#14721](https://github.com/tailwindlabs/tailwindcss/pull/14721))
- _Upgrade (experimental)_: Minify arbitrary values when printing candidates ([#14720](https://github.com/tailwindlabs/tailwindcss/pull/14720))
- _Upgrade (experimental)_: Ensure legacy theme values ending in `1` (like `theme(spacing.1)`) are correctly migrated to custom properties ([#14724](https://github.com/tailwindlabs/tailwindcss/pull/14724))
diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts
index f56575c1a8ec..89814ccc6168 100644
--- a/integrations/vite/index.test.ts
+++ b/integrations/vite/index.test.ts
@@ -504,3 +504,64 @@ test(
})
},
)
+
+test(
+ `does not interfere with ?raw and ?url static asset handling`,
+ {
+ fs: {
+ 'package.json': json`
+ {
+ "type": "module",
+ "dependencies": {
+ "@tailwindcss/vite": "workspace:^",
+ "tailwindcss": "workspace:^"
+ },
+ "devDependencies": {
+ "vite": "^5.3.5"
+ }
+ }
+ `,
+ 'vite.config.ts': ts`
+ import tailwindcss from '@tailwindcss/vite'
+ import { defineConfig } from 'vite'
+
+ export default defineConfig({
+ build: { cssMinify: false },
+ plugins: [tailwindcss()],
+ })
+ `,
+ 'index.html': html`
+
+
+
+ `,
+ 'src/index.js': js`
+ import url from './index.css?url'
+ import raw from './index.css?raw'
+ `,
+ 'src/index.css': css`@import 'tailwindcss';`,
+ },
+ },
+ async ({ spawn, getFreePort }) => {
+ let port = await getFreePort()
+ await spawn(`pnpm vite dev --port ${port}`)
+
+ await retryAssertion(async () => {
+ // We have to load the .js file first so that the static assets are
+ // resolved
+ await fetch(`http://localhost:${port}/src/index.js`).then((r) => r.text())
+
+ let [raw, url] = await Promise.all([
+ fetch(`http://localhost:${port}/src/index.css?raw`).then((r) => r.text()),
+ fetch(`http://localhost:${port}/src/index.css?url`).then((r) => r.text()),
+ ])
+
+ expect(firstLine(raw)).toBe(`export default "@import 'tailwindcss';"`)
+ expect(firstLine(url)).toBe(`export default "/src/index.css"`)
+ })
+ },
+)
+
+function firstLine(str: string) {
+ return str.split('\n')[0]
+}
diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts
index abb13a613f26..9571342cdbda 100644
--- a/packages/@tailwindcss-vite/src/index.ts
+++ b/packages/@tailwindcss-vite/src/index.ts
@@ -5,6 +5,8 @@ import { Features, transform } from 'lightningcss'
import path from 'path'
import type { Plugin, ResolvedConfig, Rollup, Update, ViteDevServer } from 'vite'
+const SPECIAL_QUERY_RE = /[?&](raw|url)\b/
+
export default function tailwindcss(): Plugin[] {
let servers: ViteDevServer[] = []
let config: ResolvedConfig | null = null
@@ -261,9 +263,12 @@ function getExtension(id: string) {
function isPotentialCssRootFile(id: string) {
let extension = getExtension(id)
let isCssFile =
- extension === 'css' ||
- (extension === 'vue' && id.includes('&lang.css')) ||
- (extension === 'astro' && id.includes('&lang.css'))
+ (extension === 'css' ||
+ (extension === 'vue' && id.includes('&lang.css')) ||
+ (extension === 'astro' && id.includes('&lang.css'))) &&
+ // Don't intercept special static asset resources
+ !SPECIAL_QUERY_RE.test(id)
+
return isCssFile
}
diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json
index 846b96252efd..a5a953a14cc8 100644
--- a/playgrounds/vite/package.json
+++ b/playgrounds/vite/package.json
@@ -19,7 +19,6 @@
"@types/react": "^18.3.9",
"@types/react-dom": "^18.3.1",
"bun": "^1.1.29",
- "vite": "catalog:",
- "vite-plugin-handlebars": "^2.0.0"
+ "vite": "catalog:"
}
}
diff --git a/playgrounds/vite/src/animate.js b/playgrounds/vite/src/animate.js
deleted file mode 100644
index 0a5617399262..000000000000
--- a/playgrounds/vite/src/animate.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('tailwindcss-animate')
diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx
index 285bd41439de..8ec50298951f 100644
--- a/playgrounds/vite/src/app.tsx
+++ b/playgrounds/vite/src/app.tsx
@@ -1,11 +1,7 @@
-import { Foo } from './foo'
-
export function App() {
return (
Hello World
-
-
)
}
diff --git a/playgrounds/vite/src/bar.tsx b/playgrounds/vite/src/bar.tsx
deleted file mode 100644
index 1efa2c004851..000000000000
--- a/playgrounds/vite/src/bar.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export function Bar() {
- return (
-
-
Bar
-
- )
-}
diff --git a/playgrounds/vite/src/foo.tsx b/playgrounds/vite/src/foo.tsx
deleted file mode 100644
index 60ec68b5517e..000000000000
--- a/playgrounds/vite/src/foo.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Bar } from './bar'
-
-export function Foo() {
- return (
-
-
Foo
-
-
- )
-}
diff --git a/playgrounds/vite/src/forms.js b/playgrounds/vite/src/forms.js
deleted file mode 100644
index f288ddfa0763..000000000000
--- a/playgrounds/vite/src/forms.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('@tailwindcss/forms')
diff --git a/playgrounds/vite/src/app.css b/playgrounds/vite/src/index.css
similarity index 50%
rename from playgrounds/vite/src/app.css
rename to playgrounds/vite/src/index.css
index adbd8e19f9b0..d4b5078586e2 100644
--- a/playgrounds/vite/src/app.css
+++ b/playgrounds/vite/src/index.css
@@ -1,2 +1 @@
@import 'tailwindcss';
-@plugin "./plugin.js";
diff --git a/playgrounds/vite/src/index.html b/playgrounds/vite/src/index.html
index d4b60a84a8fe..858e0cf2fccf 100644
--- a/playgrounds/vite/src/index.html
+++ b/playgrounds/vite/src/index.html
@@ -4,7 +4,6 @@
≈ Playground
-
diff --git a/playgrounds/vite/src/main.tsx b/playgrounds/vite/src/main.tsx
index b8df681f3d23..6b81fa9158cb 100644
--- a/playgrounds/vite/src/main.tsx
+++ b/playgrounds/vite/src/main.tsx
@@ -2,6 +2,8 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import { App } from './app'
+import './index.css'
+
ReactDOM.createRoot(document.getElementById('app')!).render(
diff --git a/playgrounds/vite/src/plugin.js b/playgrounds/vite/src/plugin.js
deleted file mode 100644
index 0852126714ec..000000000000
--- a/playgrounds/vite/src/plugin.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = function ({ addVariant }) {
- addVariant('inverted', '@media (inverted-colors: inverted)')
- addVariant('hocus', ['&:focus', '&:hover'])
-}
diff --git a/playgrounds/vite/src/typography.js b/playgrounds/vite/src/typography.js
deleted file mode 100644
index 711af27f791f..000000000000
--- a/playgrounds/vite/src/typography.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('@tailwindcss/typography')
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a13e857113a4..ba225832c5a2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -435,9 +435,6 @@ importers:
vite:
specifier: 'catalog:'
version: 5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)
- vite-plugin-handlebars:
- specifier: ^2.0.0
- version: 2.0.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)
packages:
@@ -1941,11 +1938,6 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- handlebars@4.7.8:
- resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
- engines: {node: '>=0.4.7'}
- hasBin: true
-
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -2352,9 +2344,6 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
- neo-async@2.6.2:
- resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
-
next@14.1.0:
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
engines: {node: '>=18.17.0'}
@@ -3007,11 +2996,6 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
- uglify-js@3.19.1:
- resolution: {integrity: sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==}
- engines: {node: '>=0.8.0'}
- hasBin: true
-
unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
@@ -3039,9 +3023,6 @@ packages:
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
- vite-plugin-handlebars@2.0.0:
- resolution: {integrity: sha512-+J3It0nyhPzx4nT1I+fnWH+jShTEXzm6X0Tgsggdm9IYFD7/eJ6a3ROI13HTe0CVoyaxm/fPxH5HDAKyfz7T0g==}
-
vite@5.4.0:
resolution: {integrity: sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -3133,9 +3114,6 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
- wordwrap@1.0.0:
- resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
-
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -4739,15 +4717,6 @@ snapshots:
graceful-fs@4.2.11: {}
- handlebars@4.7.8:
- dependencies:
- minimist: 1.2.8
- neo-async: 2.6.2
- source-map: 0.6.1
- wordwrap: 1.0.0
- optionalDependencies:
- uglify-js: 3.19.1
-
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -5088,8 +5057,6 @@ snapshots:
natural-compare@1.4.0: {}
- neo-async@2.6.2: {}
-
next@14.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 14.1.0
@@ -5486,7 +5453,8 @@ snapshots:
source-map: 0.6.1
optional: true
- source-map@0.6.1: {}
+ source-map@0.6.1:
+ optional: true
source-map@0.8.0-beta.0:
dependencies:
@@ -5761,9 +5729,6 @@ snapshots:
typescript@5.5.4: {}
- uglify-js@3.19.1:
- optional: true
-
unbox-primitive@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -5805,20 +5770,6 @@ snapshots:
- supports-color
- terser
- vite-plugin-handlebars@2.0.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6):
- dependencies:
- handlebars: 4.7.8
- vite: 5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)
- transitivePeerDependencies:
- - '@types/node'
- - less
- - lightningcss
- - sass
- - sass-embedded
- - stylus
- - sugarss
- - terser
-
vite@5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6):
dependencies:
esbuild: 0.21.5
@@ -5920,8 +5871,6 @@ snapshots:
word-wrap@1.2.5: {}
- wordwrap@1.0.0: {}
-
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0