The problem: Editing tailwind.config.js — new colors, extended spacing, custom fonts — doesn't reflect in the browser at all, even with npm run dev still running.
Environment: Laravel 11, Tailwind CSS 3.x, Vite 5, npm run dev (watch mode)
The Error
No JS/build error thrown.
Added `brand: '#E8A33D'` to theme.extend.colors,
used class="text-brand" in Blade — element renders
with no color applied. Vite terminal shows no rebuild.
Why It Happens
Vite's dev server watches your source files (Blade, Vue, JS, CSS) for changes and triggers HMR automatically — but tailwind.config.js sits outside that normal watch scope in some setups, because it's a build-tool configuration file, not application source. Whether it's watched depends entirely on how the Tailwind Vite plugin (or PostCSS pipeline) is wired up:
1. Using the standalone @tailwindcss/postcss setup without Vite's config-file watching enabled means changes to tailwind.config.js require a full dev server restart — PostCSS re-reads the config only when its own process restarts, not on every file save.
2. A stale Vite dependency cache (node_modules/.vite) can hold onto an old compiled version of the Tailwind plugin's output, especially if the config change also touches something Vite pre-bundles.
3. Typo'd or misplaced theme keys — extending colors at the top level of theme instead of inside theme.extend silently replaces Tailwind's entire default color palette rather than adding to it, which can look like "the config isn't working" when actually it's working differently than expected.
The Fix
First, double-check the config structure — new values almost always belong inside extend, not as siblings of it:
module.exports = {
theme: {
- colors: {
- brand: '#E8A33D',
- },
extend: {
colors: {
brand: '#E8A33D',
},
},
},
}
Then fully restart the Vite dev server rather than trusting HMR to pick up config changes on its own:
# Ctrl+C to stop the running dev server, then:
rm -rf node_modules/.vite
npm run dev
If restarts still don't pick up the change, confirm your vite.config.js actually includes the Tailwind config file in its watched paths (needed in some custom multi-config-file setups):
export default defineConfig({
server: {
watch: {
ignored: ['!**/tailwind.config.js'],
},
},
});
Step-by-Step
- Open
tailwind.config.jsand confirm any custom values sit insidetheme.extend, not directly undertheme. - Stop the running
npm run devprocess completely (Ctrl+C) — don't rely on it picking up config changes live. - Delete Vite's cache:
rm -rf node_modules/.vite. - Restart with
npm run devand confirm the terminal shows a fresh compile with no errors. - Hard-refresh the browser and confirm the new class (e.g.
text-brand) now applies visually. - If it still doesn't work, run
npx tailwindcss --helpto confirm you're not accidentally running two conflicting Tailwind installations (a common leftover from Laravel Mix-to-Vite migrations).
Edge case: If you're using Tailwind's @config directive inside a CSS file to point to a non-default config path (multi-theme or multi-tenant setups), Vite's file watcher may only be tracking the default tailwind.config.js location — a config file at a custom path needs to be added explicitly to vite.config.js's watch list, or changes to it will never trigger a rebuild no matter how many times you restart the dev server.
Still not seeing config changes take effect? Reach out on the Contact page and I'll help debug your build setup.