A class name composition utility that combines conditional class names and resolves Tailwind CSS conflicts using left-to-right merge order.
Wraps clsx for conditional class handling and tailwind-merge for conflict resolution, so later classes override earlier ones (same behavior as inline styles).
1import { cx } from "@ngrok/mantle/cx";1import { cx } from "@ngrok/mantle/cx";2 3// Strings4cx("px-4 py-2", "text-sm");5// → "px-4 py-2 text-sm"6 7// Conditional classes (object syntax)8cx("base", { active: isActive, disabled: isDisabled });9 10// Conditional classes (falsy values are ignored)11cx("base", isActive && "active", isDisabled && "disabled");12 13// Tailwind conflict resolution — later class wins14cx("text-red-500", "text-blue-500");15// → "text-blue-500"16 17// Arrays18cx(["px-4", "py-2"], "text-sm");cx uses tailwind-merge to resolve conflicts between Tailwind utility classes. When two classes target the same CSS property, the last one wins:
1// Without cx — both classes are applied, browser resolves by specificity (unpredictable, stinky)2<div className={`text-red-500 ${override}`} />3 4// With cx — later class always wins (predictable, like inline styles)5<div className={cx("text-red-500", override)} />This makes cx safe to use for component prop overrides:
1import { cx } from "@ngrok/mantle/cx";2 3type ButtonProps = {4 className?: string;5};6 7function Button({ className }: ButtonProps) {8 return <button className={cx("bg-blue-500 text-white px-4 py-2 rounded", className)} />;9}10 11// The consumer's class overrides the default12<Button className="bg-red-500" />;13// → "text-white px-4 py-2 rounded bg-red-500"1function cx(...inputs: ClassValue[]): string;2 3// ClassValue comes from clsx, looks like this 👇4type ClassValue =5 | ClassArray6 | ClassDictionary7 | string8 | number9 | bigint10 | null11 | boolean12 | undefined;13type ClassDictionary = Record<string, any>;14type ClassArray = ClassValue[];Accepts any combination of:
The return value is a single deduplicated, conflict-resolved class string.
All className values in Mantle must be built with cx.
String interpolation, +, ternaries, or conditional expressions directly on className are not allowed — see CONVENTIONS.md.