I'm currently working on a web project using Tailwind CSS v4 along with Sass without any framework. Since Tailwind CSS v4 no longer supports Sass directly, I’ve set up a build pipeline as a workaround.
I’ve split the styles into two files:
input.scss
– Contains my custom SCSS styles.
input.css
– Imports Tailwind and the compiled SCSS.
This structure is necessary because Sass does not support importing Tailwind directives (e.g., @import "tailwindcss";
) directly.
Here's how the build process works:
Write styles in input.scss
.
This file is compiled into dist/temp.css
by Sass.
input.css
imports both Tailwind and the compiled CSS (temp.css
).
Tailwind processes input.css
and outputs the final result to dist/output.css
.
I orchestrate the build using the following scripts in package.json
:
"scripts": {
"sass": "sass --no-source-map src/stylesheets/input.scss dist/temp.css",
"tailwind": "npx @tailwindcss/cli -i ./src/stylesheets/input.css -o ./dist/output.css --watch",
"watch:sass": "chokidar 'src/stylesheets/**/*.scss' -c 'npm run sass'",
"dev": "npm run sass && concurrently \"npm run watch:sass\" \"npm run tailwind\""
}
The issue arises when modifying input.scss
. Changes to HTML files trigger a live reload as expected. However, changes to input.scss
do not reflect on the webpage, even after manually reloading the browser.
To see updates, I must manually rerun npm run dev
in terminal, which is tedious and defeats the purpose of a development watch mode. I try including a browser-sync
setup but it seems to not work.
/dist/
└── output.css
└── temp.css
/src/stylesheets/
└── input.scss
└── input.css
/package.json
@import "tailwindcss";
@import "../../dist/temp.css";
How can I get changes in input.scss
to trigger a rebuild and reflect automatically in the browser during development? Is there a better way to structure the pipeline or improve the watch setup to make SCSS changes live-reload properly?
Starting with TailwindCSS v4, Sass, Less, and Stylus preprocessors are no longer supported. The reason for this is that Tailwind now uses LightningCSS under the hood, which does not support these preprocessors. TailwindCSS's long-term goal is to replace the need for such preprocessors and be fully capable on its own.
Since you compile them separately, they don't affect each other. TailwindCSS watches almost everything by default, except for paths like node_modules
and files listed in .gitignore
. Sass, on the other hand, does not watch everything automatically - it only watches your main .scss
file and any files that are imported into it.
To achieve hot-reload in the browser, you'll need something extra; like Vite, Webpack, or even just a lightweight tool like Browsersync.
Since I assume you're making things more complex because you don't want to use a full build tool for some reason, I'll highlight Browsersync as an example solution.
browser-sync
- npmjs.comnpm install -g browser-sync
If you're only using
.html
files, you˙'ll need to use the server mode. Browsersync will start a mini-server and provide a URL to view your site.
browser-sync start --server --files "css/*.css"
If you're already running a local server with PHP or similar, you'll need to use the proxy mode. Browsersync will wrap your vhost with a proxy URL to view your site.
browser-sync start --proxy "myproject.dev" --files "css/*.css"
I assume you're just working with a static site, so I would modify your current dev
script like this:
package.json
{
"scripts": {
"sass": "sass --no-source-map src/stylesheets/input.scss dist/temp.css",
"tailwind": "npx @tailwindcss/cli -i ./src/stylesheets/input.css -o ./dist/output.css",
"watch:sass": "chokidar 'src/stylesheets/**/*.scss' -c 'npm run sass'",
"serve": "browser-sync start --server --files 'dist/*.css' 'src/**/*.html'",
"dev": "npm run sass && concurrently \"npm run watch:sass\" \"npm run tailwind -- --watch\" \"npm run serve\""
},
}
You're now watching the CSS files generated in the dist folder (which works for both Sass and TailwindCSS), and you're also watching your own HTML files in the src folder.
npm run dev