match
Exhaustive variant selection: map a discriminant to a class value where a missing case is a compile error. Needs no build integration.
Every case covered
The lookup is a plain object literal: its keys are the values the discriminant can take, and each one holds the class value that case should produce. Nothing is prefixed or assembled — the helper only chooses between classes you already wrote.
function Button({ size }: { size: "sm" | "md" | "lg" }) { const sizing = match(size, { sm: "px-2 py-1 text-sm", md: "px-3 py-2 text-base", lg: "px-4 py-3 text-lg", // omit one and it won't compile });}A missing case does not compile
Extra cases are allowed, and that matters more than it sounds. TypeScript narrows a const size: "sm" | "lg" = "sm" down to "sm" at the call site, so a lookup parameter typed as a full record of the union would reject the lgentry as an excess property. Inferring the object's own type keeps the superset legal while still demanding every case be covered.
The runtime fallback
A third argument is returned when the discriminant is not one of the keys at runtime — the case TypeScript cannot rule out, because the value arrived from outside the type.
match(tone, { primary: "bg-blue-600", danger: "bg-red-600" }, "bg-gray-200");// unknown tone at runtime → the fallbackUse it wherever the discriminant comes from an API response, a URL parameter or a CMS field. Where the value is a union you control, leave the fallback off and let the exhaustiveness check do the work.
No build integration
Every class in a match lookup is already a literal in your source, so Tailwind finds it on its own scan. match builds no prefix at runtime and needs no build integration — with cn, it is one of the two helpers that work whether or not the plugin is installed.
That makes it the standard workaround for a class the tailess scannercannot see — a value held in a variable, an interpolated template, a computed key. Move the decision into a lookup and the full class is back in the source, where Tailwind's own scan reaches it without any help.
const size = match(scale, { sm: "text-sm", lg: "text-2xl" });The result is a plain class string, so it composes with everything else: ss and cn both take it as an argument.