`@tailwindcss/vite` discards `resolve.conditions` when resolving CSS `@import`, but honours them for JS
What version of Tailwind CSS are you using?
v4.3.3 (@tailwindcss/vite 4.3.3; also reproduces on 4.1.18)
What build tool are you using?
Vite 8 (the relevant code is unchanged across the plugin's ^5.2.0 || ^6 || ^7 || ^8 peer range)
Describe your issue
@tailwindcss/vite builds two resolvers. The JS one inherits the user's
resolve config as-is; the CSS one spreads that config and then overwrites
conditions with a hardcoded list:
// dist/index.mjs (4.3.3), lightly de-minified
let cssResolver = createIdResolver(config, {
...config.resolve, // inherits alias, dedupe, …
extensions: [".css"],
mainFields: ["style"],
conditions: ["style", "development|production"], // ← replaces user conditions
tryIndex: false,
preferRelative: true,
})
let jsResolver = createIdResolver(config, config.resolve) // ← conditions preserved
Because conditions is assigned after the spread, any custom condition set via
resolve.conditions is silently dropped for CSS @import, while continuing to
work for JS. The two resolvers therefore disagree about which copy of a package
they are looking at.
This is the standard monorepo setup where a library's exports map carries a
custom condition so a workbench/dev app consumes source while published
consumers get the build output:
// packages/my-lib/package.json
{
"exports": {
".": { "source": "./src/index.ts", "default": "./dist/index.js" },
"./style.css": { "source": "./src/style.css", "default": "./dist/style.css" }
},
"files": ["dist", "package.json"]
}
// apps/workbench/vite.config.js
export default {
resolve: { conditions: ["source", ...] },
plugins: [tailwindcss()],
}
/* apps/workbench/style.css */
@import "tailwindcss";
@import "my-lib/style.css";
/* packages/my-lib/src/style.css — copied verbatim to dist/ at build time */
@import "tailwindcss";
@source "./**/*.{ts,tsx,js}"; /* relative: src/ in the workspace, dist/ once published */
Expected
my-lib/style.css resolves through the source condition to
packages/my-lib/src/style.css, so its relative @source scans src/.
Actual
The source condition is discarded, resolution falls through to default, and
packages/my-lib/dist/style.css is loaded — so @source scans dist/.
The app therefore renders components from src/ while scanning dist/ for
class candidates. Any utility added since the last build is missing from the
generated CSS, and the only symptom is a component that renders unstyled. It
looks exactly like a broken component rather than a stale-cache problem, so it
is unusually expensive to diagnose — a rebuild makes it disappear, which also
makes it hard to reproduce reliably.
Worth noting there is no way to work around this from the exports map. Adding
a "style" condition (which the hardcoded list would match) does not help:
export conditions cannot distinguish in-repo from published consumption, and
src/ is not in files, so every published consumer would resolve to a path
that does not exist.
Suggested fix
Append rather than replace, so user conditions survive alongside the CSS-specific ones:
conditions: ["style", "development|production", ...(config.resolve.conditions ?? [])],
If overriding is deliberate, it would help to document it — the asymmetry with the JS resolver two lines away reads as accidental.
Reproduction
Current workaround for anyone hitting this: bypass the export map and import the library's CSS entry by relative path, so no condition resolution is involved.
Source: tailwindlabs/tailwindcss