Repository: tailwindlabs/tailwindcss

Problem statement:
V4 Alpha 27/28 - `--watch` command does not work on the input css file (config file)
**What version of Tailwind CSS are you using?**

v4.0.0.alpha28 (also tried on alpha27)

**What build tool (or framework if it abstracts the build tool) are you using?**

Tailwind CLI executable only.

**What version of Node.js are you using?**

None. I'm using the Standalone CLI

**What browser are you using?**

Not relevant

**What operating system are you using?**

macOS Sonoma 14.6.1

**Reproduction URL**

Uploading the whole executable to a Git repo doesn't make much sense but here are the instructions to reproduce the issue locally:

1. Create a new directory for the test
2. `cd` into that directory
3. [Download the version of the executable](https://github.com/tailwindlabs/tailwindcss/releases/tag/v4.0.0-alpha.28) appropriate for your OS. Assuming your version is `macos-arm64`
4. run `chmod +x tailwindcss-macos-arm64`
5. run `mv tailwindcss-macos-arm64 tailwindcss4alpha28`. I've named the executable with the specific version to make it more explicit
6. Create an empty input.css file
7. Create an empty output.css file
8. Add `@import "tailwindcss";` to the `input.css` file
9. Run `./tailwindcss4alpha28 --input input.css --output output.css --watch`
10. 🚨 Check the `output.css` file. Tailwind classes should be present but are not.
11. Run `./tailwindcss4alpha28 --input input.css --output output.css`. Note that this time it's not using the `--watch` flag
12. ✅ Check the `output.css` file. Tailwind classes are present.
 
**Describe your issue**

We've been trying to make the Tailwind 4 alphas work in Rails but noticed that the watch command isn't working when using the Standalone CLI.

https://github.com/rails/tailwindcss-rails/issues/419

After some debugging we realised that the problem is not coming from our Rails implementation but from the executable provided by Tailwind. 


Hints:
I've updated the title to also include the regular CLI which also doesn't support --watch.

I went back to [the article announcing v4](https://tailwindcss.com/blog/tailwindcss-v4-alpha) and realised it doesn't mention the `--watch` flag. Is there a reason for it? Is watch mode not supported just yet?
I can't seem to reproduce this, `--watch` is working as expected for me:

https://github.com/user-attachments/assets/f4ceba79-1258-488e-8c1c-85efc7b63ea8

Trying to figure out what might be different in our setups — is there any chance you could throw up a whole project that's not working for you so I can test here and try to narrow it down?

Thanks to your video I've narrowed down the problem a bit more and I have a few new clues.

If I change the input css file, the watcher kicks off, says it's "Done in X ms" but the changes are not present in the output file. However, if I create an html file and add an html class to it, the watcher kicks off and the new classes are present in the output.css.

**So, the watcher is specifically not working on the input file**

Check out this video (it was too big for GItHub): https://youtu.be/bdup23ipD4c

Also I tried this on a different computer and I get the same results.

I've put up a repo for ease of reproduction here:
https://github.com/pinzonjulian/tailwind_css_cli_watch_bug

You'll just need to add the executables


Patch:
diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts
--- a/packages/@tailwindcss-cli/src/commands/build/index.ts
+++ b/packages/@tailwindcss-cli/src/commands/build/index.ts
@@ -122,11 +122,12 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
     env.DEBUG && console.timeEnd('[@tailwindcss/cli] Write output')
   }
 
-  let inputBasePath =
-    args['--input'] && args['--input'] !== '-'
-      ? path.dirname(path.resolve(args['--input']))
-      : process.cwd()
-  let fullRebuildPaths: string[] = []
+  let inputFilePath =
+    args['--input'] && args['--input'] !== '-' ? path.resolve(args['--input']) : null
+
+  let inputBasePath = inputFilePath ? path.dirname(inputFilePath) : process.cwd()
+
+  let fullRebuildPaths: string[] = inputFilePath ? [inputFilePath] : []
 
   async function createCompiler(css: string) {
     env.DEBUG && console.time('[@tailwindcss/cli] Setup compiler')
@@ -201,7 +202,7 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
                   @import 'tailwindcss';
                 `
             clearRequireCache(resolvedFullRebuildPaths)
-            fullRebuildPaths = []
+            fullRebuildPaths = inputFilePath ? [inputFilePath] : []
 
             // Create a new compiler, given the new `input`
             compiler = await createCompiler(input)


Test patch:
diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts
--- a/integrations/cli/index.test.ts
+++ b/integrations/cli/index.test.ts
@@ -210,6 +210,23 @@ describe.each([
           }
         `,
       ])
+
+      await fs.write(
+        'project-a/src/index.css',
+        css`
+          @import 'tailwindcss/utilities';
+          @theme {
+            --color-*: initial;
+          }
+        `,
+      )
+
+      await fs.expectFileToContain('project-a/dist/out.css', [
+        css`
+          :root {
+          }
+        `,
+      ])
     },
   )