Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Investigating Inconsistent Config Loading with @tailwindcss/vite #18469

Closed Answered by wongjn
philippedev101 asked this question in Help
Discussion options

Hello Tailwind Team,

First of all, thank you for all the incredible work on v4. The new engine and the dedicated Vite plugin are exciting developments.

I was recently setting up a new project to try out @tailwindcss/vite as an all-in-one replacement for the older PostCSS setup. My mental model, based on past experience, was that the plugin would automatically find my tailwind.config.js at the project root and use it to configure everything. During this process, I ran into some interesting behavior that I wanted to share.

To understand it better, I created a minimal reproduction repository, which can be found here:
➡️ https://github.com/philippedev101/tailwind-vite-bug-repro

To trace the issue, I also cloned the tailwindcss and vite repositories locally and used pnpm overrides to point the MRE to my local builds. I then instrumented several files with extensive logging. (As a side note, I did have to patch the exports map in tailwindcss/package.json to get the local package to import correctly in my project, changing the "import" field from src/index.ts to dist/lib.mjs.)

The minimal repo uses a standard setup, but with one key feature: the main CSS file is in a subdirectory (src/client/main.css). The pnpm overrides can be removed and the issue still persists with the official npm packages.

The Core Observation

When I run the MRE, I see very specific and seemingly contradictory behavior:

  • Classes from the safelist in my tailwind.config.js (like .rotate-12) are correctly generated in the final CSS.
  • Utility classes used in my content files (like .text-blue-500 in index.html) are not detected and are missing from the final CSS.

This suggests that the config file is being found and read, but that the content property is being handled differently or ignored by the content scanning pipeline.

The Debugging Trace

My logging within the local builds revealed a very specific chain of events that seems to cause this:

  1. Incorrect base path calculation in the Vite Plugin.
    In packages/@tailwindcss-vite/src/index.ts, the Root.generate() method calculates a base path from the directory of the CSS file being processed, not the Vite project root.

    • It correctly has this.base = "/path/to/repro".
    • But it calculates and uses inputBase = "/path/to/repro/src/client".
    • It then calls the core compile function with this incorrect inputBase.
  2. The compat Layer Receives the Wrong Path.
    The core engine's parseCss function (in packages/tailwindcss/src/index.ts) receives this incorrect base path and passes it down to applyCompatibilityHooks in packages/tailwindcss/src/compat/apply-compat-hooks.ts.

  3. Config Resolution Fails to Process User content.
    The applyCompatibilityHooks function is responsible for finding and merging configs.

    • Because our CSS doesn't use an explicit @config directive, the function enters a path to find an implicit tailwind.config.js.
    • However, its call to upgradeToFullPluginSupport and the subsequent calls to resolveConfig (in packages/tailwindcss/src/compat/config/resolve-config.ts) end up processing only the built-in default theme config.
    • Crucially, the user's tailwind.config.js is never added to the list of files for the resolveConfig function to process for its content key.
  4. An Empty content Array is Returned.
    Because the user's config was never processed for its content key in this pipeline, the resolveConfig function returns a final, merged config where content.files is an empty array.

  5. The vite Plugin's Fallback is Triggered.
    The @tailwindcss/vite plugin receives this resolved config with no content sources. It then activates its own fallback logic, setting the scanner to watch /* in the project root. This is a very broad pattern that doesn't respect the specific, more efficient globs defined in our actual tailwind.config.js.

Summary of Findings:

It appears there are two separate config-loading mechanisms at play. One process seems to correctly find tailwind.config.js and process the safelist, but a second, more complex process, initiated by the @tailwindcss/vite plugin, fails. This second process fails because the plugin passes an incorrect base path to the core engine's compatibility layer, which then doesn't correctly load the content property from the user's config file, causing the JIT scanner to not work as intended.

A Few Questions

This deep dive left me with a few questions about the intended design, which would be super helpful to understand:

  1. Is it the intended behavior for the @tailwindcss/vite plugin to use the CSS file's directory as the base for config resolution, instead of the Vite project root?
  2. The compat layer's resolveConfig doesn't seem to perform an upward search for a config file. Is that the expected logic for that part of the system?
  3. Most importantly, what is the recommended way to set up a project like the MRE? It would be incredibly helpful to see a reference example of @tailwindcss/vite being used without a PostCSS pipeline where the CSS entry point is in a subdirectory.

Thank you again for your time and for looking into this. I'm really excited about where the project is headed!

Environment

  • OS: Ubuntu 24.04.2 LTS
  • Node.js: v22.17.0
  • pnpm: 10.12.4
  • Rust: rustc 1.88.0 (6b00bc388 2025年06月23日)
  • Shell: zsh 5.9
  • Tailwind CSS Commit: b24457a9f4101f20a3c3ab8df39debe87564fe8a
  • Vite Commit: 2e8050e4cd8835673baf07375b7db35128144222
You must be logged in to vote

What is happening is that you've configured Tailwind v4 using v3 methods. In v4, the tailwind.config.js is phased out and is not the main container for configuration any more.

V4 has automatic source scanning. Thus, it finds rotate-12 as a string inside tailwind.config.js, not that it loads the safelist as configuration. So, it adds rotate-12 to the CSS output.

So then why does Tailwind not output the classes in index.html, text-3xl, font-bold and text-blue-500? This is because all of these classes rely on named Tailwind theme tokens for --font-size, --font-weight and --color. However, these do not exist in Tailwind compilation runs in your reproduction. This is because you haven't includ...

Replies: 2 comments

Comment options

What is happening is that you've configured Tailwind v4 using v3 methods. In v4, the tailwind.config.js is phased out and is not the main container for configuration any more.

V4 has automatic source scanning. Thus, it finds rotate-12 as a string inside tailwind.config.js, not that it loads the safelist as configuration. So, it adds rotate-12 to the CSS output.

So then why does Tailwind not output the classes in index.html, text-3xl, font-bold and text-blue-500? This is because all of these classes rely on named Tailwind theme tokens for --font-size, --font-weight and --color. However, these do not exist in Tailwind compilation runs in your reproduction. This is because you haven't included these in your input CSS.

If we followed the vite integration instructions and replaced your CSS file with:

@import "tailwindcss";

This is shorthand for:

@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);

Which imports the default theme tokens as mentioned previously.

Thus, having your src/index.css be:

@import "tailwindcss";

Should now see all the expected class names in the output CSS file.

You must be logged in to vote
0 replies
Answer selected by philippedev101
Comment options

Thanks @wongjn extremely helpful!

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Help
Labels
None yet
Converted from issue

This discussion was converted from issue #18468 on July 06, 2025 19:02.

AltStyle によって変換されたページ (->オリジナル) /