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:
// 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.
What version of Tailwind CSS are you using?
v4.3.3 (
@tailwindcss/vite4.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 || ^8peer range)Describe your issue
@tailwindcss/vitebuilds two resolvers. The JS one inherits the user'sresolveconfig as-is; the CSS one spreads that config and then overwritesconditionswith a hardcoded list:Because
conditionsis assigned after the spread, any custom condition set viaresolve.conditionsis silently dropped for CSS@import, while continuing towork 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
exportsmap carries acustom condition so a workbench/dev app consumes source while published
consumers get the build output:
Expected
my-lib/style.cssresolves through thesourcecondition topackages/my-lib/src/style.css, so its relative@sourcescanssrc/.Actual
The
sourcecondition is discarded, resolution falls through todefault, andpackages/my-lib/dist/style.cssis loaded — so@sourcescansdist/.The app therefore renders components from
src/while scanningdist/forclass 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
exportsmap. Addinga
"style"condition (which the hardcoded list would match) does not help:export conditions cannot distinguish in-repo from published consumption, and
src/is not infiles, so every published consumer would resolve to a paththat does not exist.
Suggested fix
Append rather than replace, so user conditions survive alongside the CSS-specific ones:
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.