Primitive · Utility

cn() — class name composer

A 3-line helper combining clsx (conditional class names) and tailwind-merge (deterministic resolution of conflicting Tailwind utilities). Same primitive shadcn ships.

Why

When you build components with Tailwind, two problems show up constantly:

  • You want to conditionally include classes based on state (disabled, active, etc.) — clsx solves this.
  • You want consumers to override your base classes — tailwind-merge resolves p-2 p-4 to p-4, instead of letting both win randomly.

cn() combines both in one call. Every ShadNG component uses it internally.

Import

typescript
import { cn } from '@shadng/core';

Usage

typescript
// Conditional classes
const className = cn(
  'inline-flex items-center rounded-md',
  disabled && 'opacity-50 pointer-events-none',
  pressed && 'bg-accent',
);

// Tailwind conflict resolution
cn('p-2 p-4');                            // → 'p-4'
cn('text-sm text-base');                  // → 'text-base'
cn('bg-red-500', condition && 'bg-blue-500');  // → 'bg-blue-500' (if condition)

// Inside a component
@Component({
  template: `<div [class]="hostClass()">…</div>`,
})
export class Card {
  variant = input<'default' | 'ghost'>('default');

  hostClass = computed(() => cn(
    'rounded-md border',
    this.variant() === 'ghost' && 'border-transparent bg-transparent',
    this.variant() === 'default' && 'border-border bg-card',
  ));
}

Source

The whole implementation — type signature plus body. Steal it directly into your own code if you don't want the dependency. The peer deps (clsx, tailwind-merge) are small and well-maintained.

cn.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]): string {
  return twMerge(clsx(inputs));
}

When NOT to use

  • Static class strings — if your classes never change, a plain string literal is faster and clearer.
  • Non-Tailwind class namestailwind-merge only understands Tailwind utilities. For BEM or other systems, use clsx directly.
  • Inline stylescn() is for the class attribute. For dynamic CSS variables, use [style.--var] bindings.
MIT © Kalvnerv0.1.0 · pre-release