The problem: Tailwind classes work perfectly with npm run dev, but after npm run build and deploying, half your styling disappears — no console error, no crash, just plain unstyled HTML.
Environment: Laravel 11.x, Tailwind CSS 3.x, Vite, Blade templates, deployed via standard npm run build in CI.
The Error
There's no exception here — that's exactly what makes this one annoying. The build succeeds cleanly and silently produces a much smaller CSS file than expected:
$ npm run build
vite v5.2.0 building for production...
✓ 38 modules transformed.
public/build/assets/app-3f7a1c2e.css 4.12 kB │ gzip: 1.10 kB
public/build/assets/app-9d2e5b41.js 62.48 kB │ gzip: 22.9 kB
✓ built in 812ms
// expected app.css around 18-25kB with the full utility set —
// 4kB means Tailwind only found a handful of classes to keep
[SCREENSHOT NEEDED: production page in browser devtools, showing a Blade element with class="bg-blue-600 rounded-lg px-4 py-2" in the Elements panel but no matching rule in the Computed/Styles panel]
Why It Happens
Tailwind doesn't ship every utility class by default — it scans your project files listed in the content array of tailwind.config.js, finds which class names you actually use, and generates CSS only for those. In dev mode with the Vite plugin, this scan re-runs on every file save, so it's forgiving. In a production build, it scans once, and if a file containing your classes isn't in that content array, those classes are simply never generated — not broken, just never written.
This almost always comes down to one of these:
- New Blade view directories added after the initial Tailwind setup (like
resources/views/livewire/orresources/views/components/) that were never added tocontent. - Classes built dynamically with string concatenation in PHP or Blade, like
class="text-{{ $color }}-500"— Tailwind's scanner only sees literal strings, so it never generatestext-red-500,text-blue-500, etc. from a variable. - Vendor or package Blade views (from a UI package or admin panel) that render Tailwind classes but live outside
resources/viewsentirely.
The Fix
Check your content array first — it needs to cover every file that contains a Tailwind class name, not just your main Blade folder:
content: [
'./resources/views/*.blade.php', // too narrow — misses subfolders
'./resources/views/**/*.blade.php',
'./resources/js/**/*.{js,jsx,ts,tsx,vue}',
'./app/View/Components/**/*.php',
],
For dynamic class names, Tailwind's scanner can't see variables — you have to give it the full literal class string somewhere it can find it. Use a lookup map instead of concatenation:
<div class="text-{{ $color }}-500"> // scanner can't see this
// in a helper or Blade component method:
$classes = [
'red' => 'text-red-500',
'blue' => 'text-blue-500',
];
<div class="{{ $classes[$color] }}">
After fixing the config, clear caches and rebuild — a stale view cache can keep serving old compiled Blade output even after your CSS is regenerated:
$ php artisan view:clear
$ npm run build
Step-by-Step
- Run
npm run buildand check the resulting CSS file size — if it's a few KB instead of the usual 15-25KB, Tailwind is under-scanning your project. - Open
tailwind.config.jsand confirm thecontentarray uses**/*.blade.php(with the double-star glob) so it recurses into subfolders likelivewire/andcomponents/. - Search your codebase for any Blade class attribute built with
{{ }}interpolation directly inside a class string — those are invisible to Tailwind's scanner. - Replace dynamic class construction with a PHP lookup array or match expression that contains the full literal class names.
- Add any package or vendor view paths (admin panels, third-party components) that render Tailwind classes to the
contentarray explicitly. - Run
php artisan view:clearthen rebuild, and confirm the CSS file size jumps back up before redeploying.
content glob can catch those at build time — the classes don't exist in any file Tailwind scans. The usual fix is a safelist array in tailwind.config.js listing those specific classes explicitly, so they're always generated regardless of whether Tailwind's scanner can "see" them.
Still missing styles after checking your config? Send me your tailwind.config.js on the Contact page and I'll take a look.