Why utilities
How class detection works
Understand why Tailwind scans complete class names in source files and why building class names dynamically can leave required CSS out of the build.
Tailwind scans your source files as plain text. It looks for complete class names, and it generates CSS only for the names it finds. Understanding this one fact saves you hours of debugging.
This works:
const colors = {
success: 'bg-green-600 text-white',
error: 'bg-red-600 text-white',
}
This does not:
const className = `bg-${color}-600`
Tailwind does not run your JavaScript. It doesn’t know what color will be. It only sees the text bg- and -600, and neither is a valid class. So bg-green-600 never gets generated, and the element renders with no background.
Map states to complete classes
The fix is to write every class in full and pick one at runtime:
const colorClasses = {
success: 'bg-green-600 text-white hover:bg-green-700',
error: 'bg-red-600 text-white hover:bg-red-700',
}
const className = colorClasses[state]
Now both strings exist in the file, so Tailwind finds them. I think this is also a better component API. The caller passes a meaning like success or error, and the component owns the visual decision.
What Tailwind ignores
Tailwind v4 automatically skips node_modules, binary files, lockfiles, and anything in your .gitignore. That’s what you want most of the time. But if you use a component library that ships classes in node_modules, register it in your CSS:
@import "tailwindcss";
@source "../node_modules/@acme/ui";
In a monorepo, you can set the base path with @import "tailwindcss" source("../src") or add explicit @source lines. There is also @source inline("bg-green-600") for a class that truly cannot appear in static source. Use it sparingly. A long safelist hides design mistakes and bloats your CSS.
When a class is missing
When a utility has no effect, open the compiled CSS and search for its selector. In the output, hover:bg-green-700 appears escaped as .hover\:bg-green-700. If the rule is missing, the problem is detection. If it’s there but doesn’t apply, the problem is the selector, a variant condition, or the cascade, and detection is fine.
Try this: reproduce the missing dynamic color, replace it with a complete mapping, and confirm the rule shows up in the built CSS. Then find one ignored path in your project and decide whether it should stay ignored or become an explicit @source.
Lesson completed