From harness-claude
Resolves Tailwind class conflicts using tailwind-merge and clsx for safe className composition and overrides in React components. Use when merging base styles with conditional or user-provided classes.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Resolve Tailwind class conflicts intelligently with tailwind-merge for safe className composition and overrides
Applies Tailwind CSS utility-first patterns to style React components consistently without custom CSS. Groups utilities by concern, uses design tokens, orders logically for rapid prototyping and maintenance.
Provides Tailwind CSS best practices for frontend apps including layout utilities like v-stack, responsive stacks, color schemes, and cn() className merging.
Generates responsive, performant Tailwind CSS styles for React components. Use when styling components, building design systems, or implementing responsive layouts.
Share bugs, ideas, or general feedback.
Resolve Tailwind class conflicts intelligently with tailwind-merge for safe className composition and overrides
className prop for consumer overridespx-4 should be overridable by px-8text-red-500 and text-blue-500)tailwind-merge and clsx: npm install tailwind-merge clsx.cn() utility that combines both. Use it everywhere instead of raw className concatenation.cn('px-4', 'px-8') outputs px-8, not px-4 px-8.tailwind-merge understands Tailwind's class groups — it knows p-4 and px-4 conflict partially, text-red-500 and text-blue-500 conflict fully.cn() in all component className props to allow safe overrides.// lib/utils.ts — the cn() helper
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Component that supports className overrides
function Card({ className, children }: { className?: string; children: React.ReactNode }) {
return (
<div
className={cn(
'rounded-lg border border-gray-200 bg-white p-6 shadow-sm', // defaults
className // consumer can override any of these
)}
>
{children}
</div>
);
}
// Usage — override padding and remove shadow
<Card className="p-4 shadow-none">Compact card</Card>;
// Output: "rounded-lg border border-gray-200 bg-white p-4"
// p-6 was replaced by p-4, shadow-sm was replaced by shadow-none
// Conditional classes with cn()
function Alert({ variant, className }: { variant: 'info' | 'error'; className?: string }) {
return (
<div
className={cn(
'rounded-md p-4 text-sm',
variant === 'info' && 'bg-blue-50 text-blue-700 border-blue-200',
variant === 'error' && 'bg-red-50 text-red-700 border-red-200',
className
)}
></div>
);
}
// Override just the background
<Alert variant="info" className="bg-blue-100" />;
// bg-blue-50 is replaced by bg-blue-100, text and border unchanged
Why not just string concatenation? CSS specificity is not determined by class order — class="px-4 px-8" applies whichever rule comes last in the stylesheet, which depends on Tailwind's generated order, not your class string order. tailwind-merge resolves this by removing the conflicting class.
What tailwind-merge knows:
p-4 and px-8 conflict on the x-axis padding (px-8 wins for x, p-4 applies to y)text-red-500 and text-blue-500 conflict (same property: color)text-lg and text-red-500 do NOT conflict (font-size vs color)rounded-lg and rounded-none conflict (border-radius)hover:bg-red-500 and hover:bg-blue-500 conflict (same pseudo + property)clsx role in cn(): clsx handles conditional logic (booleans, arrays, objects). twMerge handles conflict resolution. Together:
cn(
'base-class', // always applied
isActive && 'bg-blue-500', // conditional (clsx)
{ 'font-bold': isBold }, // object syntax (clsx)
className // override (twMerge resolves conflicts)
);
Custom tailwind-merge config: If you extended Tailwind with custom utilities, tell tailwind-merge about them:
import { extendTailwindMerge } from 'tailwind-merge';
const customTwMerge = extendTailwindMerge({
extend: {
classGroups: {
'custom-size': ['size-sm', 'size-md', 'size-lg'],
},
},
});
Performance: tailwind-merge caches results. The first call for a given input is ~0.1ms; subsequent calls are ~0.001ms. No measurable impact on render performance.
https://github.com/dcastil/tailwind-merge