Conflicting utilities and tailwind-merge
cn() above joins; it does not decide between p-4 and p-2 when a caller overrides a default.
cn() above joins; it does not decide between p-4 and p-2 when a caller overrides a default. In a Tailwind
stylesheet the order of rules, not the order in the attribute, decides which one wins. The shadcn/ui cn() solves that
with tailwind-merge. This repository does not install tailwind-merge, so no snippet imports it; the plug point is
a function from a class string to a class string, because that is all class needs. createCn shows the shape with a
small stand-in merge (it keeps the last class of each padding family, nothing else):
src/lib/create-cn.tsts
import { type ClassValue, cn } from "./variants";
/** Builds a `cn` that post-processes the joined string, for example with `twMerge`. */
export function createCn(merge: (classes: string) => string): (...inputs: readonly ClassValue[]) => string {
return (...inputs) => merge(cn(...inputs));
}
/** A stand-in for `tailwind-merge`: last `p-*` wins. It exists to show the shape, not to replace the library. */
export function keepLastPadding(classes: string): string {
const all = classes.split(" ");
const lastPadding = all.findLastIndex((name) => /^p-\d+$/.test(name));
return all.filter((name, index) => !/^p-\d+$/.test(name) || index === lastPadding).join(" ");
}
export const merged = createCn(keepLastPadding);
With the real library installed, the replacement is createCn(twMerge): the call sites do not change.