Why the colors your AI coding agent wrote disappear the moment you build for production

AI coding agents love to build Tailwind class names with template literals like bg-${status}-500. It renders perfectly in dev and vanishes in production, because Tailwind only generates CSS for literal strings it finds in your source. Here is why, and the fix.

Your AI coding agent builds you a status badge, a priority tag, or a theme switcher: something like <span className={bg-${status}-500}>. It renders with the right colors immediately, in the dev server, in the agent’s own preview, in every screenshot you take while testing. You ship it, and in production every one of those badges is the same flat, colorless background. Nothing crashed. No error in the console. The class name is sitting right there in the DOM inspector, bg-red-500, exactly where you’d expect it. The color just isn’t there.

Why this happens

Tailwind doesn’t run your code to figure out which classes you use. It scans your source files as plain text, looking for complete, literal class name strings, and generates CSS only for the ones it finds. When your agent writes `bg-${status}-500`, that string doesn’t exist anywhere in your source: status is a variable, resolved only at runtime in the browser. Tailwind’s scanner sees the literal text bg-${status}-500, which matches no real utility class, and moves on. bg-red-500, bg-green-500, bg-yellow-500, whatever status could actually become, never gets generated, because none of those strings ever appear anywhere in your files.

This is easy to miss in development because a related component elsewhere in the codebase sometimes already spells out the same literal class (another badge, a button, a demo block), so the CSS exists by coincidence and the broken one rides along on it. Production builds run the same strict text scan, and once that unrelated literal gets refactored away or the build runs against a narrower file set, the coincidence that was covering for the bug stops holding.

Coding agents write this pattern constantly because it’s the obvious way to express “the color depends on a variable” in JSX, and it works in every styling approach that isn’t scanning your text for literal strings. Tailwind is the one place that instinct is actively wrong, and nothing in the agent’s own preview tells it so.

How to tell if this is your problem

  1. Search your codebase for template literals building class names: `bg-${, `text-${, `border-${, or the JSX equivalent className={`...${variable}...`}. Any hit is a candidate.
  2. Compare what the DOM inspector shows against what’s actually in your CSS. If the element has class="bg-red-500" but Chrome DevTools’ Styles panel shows no rule matching that selector at all (not overridden, just absent), the class was never generated.
  3. Check whether it “worked” in a component that reuses a color already spelled out elsewhere. If bg-red-500 also appears as a literal string in some unrelated button, that coincidence is why the broken component looked fine in dev.

The fix

Replace the template literal with a lookup object that spells out every complete class name Tailwind can actually see:

const statusStyles = {
  active: "bg-green-500 text-white",
  pending: "bg-yellow-500 text-black",
  failed: "bg-red-500 text-white",
};

function StatusBadge({ status }) {
  return <span className={statusStyles[status]}>{status}</span>;
}

Every string Tailwind needs to generate now exists literally in your source file, so the scanner finds all of them regardless of which one actually renders at runtime.

If you genuinely can’t enumerate every value ahead of time (colors coming from a CMS or user input, for example), add the exact classes to safelist in tailwind.config.js instead:

module.exports = {
  safelist: ["bg-red-500", "bg-green-500", "bg-yellow-500"],
};

Safelist should be the fallback, not the default: it grows unbounded as new values show up, and nothing warns you when a new one is missing from the list.

How to avoid this next time

Tell your agent explicitly, in the prompt or in your project’s rules file, that dynamic Tailwind class names built with string interpolation don’t work, full static class names in a lookup map do. It’s a one-line instruction that heads off a bug class that looks completely fine in every environment the agent can actually see, and only shows up once real users are looking at a page with no colors on it.

Comments