본문 바로가기
Utility

cva (Class Variance Authority)

by seyoonagain 2024. 12. 14.

cva Class Variance Authority

조건부 스타일링을 구조적으로 처리하기 위해 만들어진 CSS 클래스 관리 유틸리티 라이브러리로,
Tailwind CSS와 같이 유틸리티 클래스 기반의 스타일링을 사용할 때 유용하다.


cva는 컴포넌트의 여러 가지 형태와 상태를 정의하고, 이를 기반으로 동적인 CSS 클래스를 생성한다.

설치

npm install class-variance-authority
yarn add class-variance-authority
pnpm add class-variance-authority

API 설명

  • cva(baseClasses, options)
    • baseClasses: 항상 적용되는 기본 클래스
    • options: variants, compoundedVariantsdefaultVariants을 정의하는 객체
      • varaints: 상태나 변형에 따라 추가될 CSS 클래스 정의
      • compoundedVariants: 개별 변형뿐 아니라 여러 변형의 상태가 특정 조건을 만족할 때만 적용할 CSS 클래스 정의
      • defaultVariants: 기본적으로 적용될 변형을 지정
    • 리턴: options 객체를 전달 받아 조건에 맞는 CSS 클래스 반환

const button = cva(baseClasses, options)

위와 같은 형태로 cva를 정의하며, button(options) 형태로 호출하여 사용한다.
즉, cva가 할당된 변수 button은 함수처럼 사용되며,
해당 함수에 전달되는 매개변수는 cva의 두 번째 매개변수인 options의 속성인 variants와 매칭되어 CSS 클래스가 특정된다.

// components/button.ts
import { cva } from "class-variance-authority";

const button = cva(["font-semibold", "border", "rounded"], {
  variants: {
    intent: {
      primary: [
        "bg-blue-500",
        "text-white",
        "border-transparent",
        "hover:bg-blue-600",
      ],
      // **or**
      // primary: "bg-blue-500 text-white border-transparent hover:bg-blue-600",
      secondary: [
        "bg-white",
        "text-gray-800",
        "border-gray-400",
        "hover:bg-gray-100",
      ],
    },
    size: {
      small: ["text-sm", "py-1", "px-2"],
      medium: ["text-base", "py-2", "px-4"],
    },
  },
  compoundVariants: [
    {
      intent: "primary",
      size: "medium",
      class: "uppercase",
      // **or** if you're a React.js user, `className` may feel more consistent:
      // className: "uppercase"
    },
  ],
  defaultVariants: {
    intent: "primary",
    size: "medium",
  },
});

button();
// => "font-semibold border rounded bg-blue-500 text-white border-transparent hover:bg-blue-600 text-base py-2 px-4 uppercase"

button({ intent: "secondary", size: "small" });
// => "font-semibold border rounded bg-white text-gray-800 border-gray-400 hover:bg-gray-100 text-sm py-1 px-2"

설명

  • button에 아무런 매개변수가 전달되지 않으면,
    defaultVariants에 설정된 기본 상태 및 형태와 variants와 매칭된 스타일이 반환된다.
  • button에 전달된 매개변수 { intent: "secondary", size: "small" }
    각각 variants.intent.secondaryvariants.size.small에 해당하는 스타일을 가리킨다.

주의사항

  • 조합 조건의 충돌
    여러 compoundedVariants가 동시에 만족하는 경우,
    나중에 정의된 클래스가 덮어씌워질 수 있으므로, 조건을 신중히 작성해야 한다.

clsx vs. cva

clsx vs cva: 주요 기능 비교

기능 clsx cva
주요 목적 조건부 CSS 클래스를 간결하게 작성 조건부 CSS 클래스 및 스타일 변형 관리
클래스 병합 clsx로 다양한 조건의 클래스 병합 가능 base 속성으로 기본 클래스를 정의하고 변형을 조합
조건부 로직 단순한 조건부 클래스 적용 variants 속성을 사용해 구조화된 조건부 스타일 적용
유연성 CSS 클래스 병합에 특화 컴포넌트 스타일링을 구조적으로 관리
조합 가능성 배열, 객체, 문자열 등 다양한 입력 지원 compoundVariants로 복잡한 조합 스타일링 가능
사용 사례 빠르게 조건부 클래스 병합이 필요할 때 상태, 변형, 조건 등을 기반으로 컴포넌트 스타일을 관리해야 할 때

clsx 예제

import clsx from 'clsx';

function Button({ isPrimary, isDisabled }) {
  return (
    <button
      className={clsx(
        'px-4 py-2 rounded', // 기본 스타일
        isPrimary && 'bg-blue-500 text-white', // 조건부 스타일
        isDisabled && 'opacity-50 cursor-not-allowed' // 비활성화 스타일
      )}
    >
      Click Me
    </button>
  );
}
  • isPrimarytrue이면 bg-blue-500 text-white가 추가
  • isDisabledtrue이면 opacity-50 cursor-not-allowed가 추가

cva 예제

import { cva } from 'class-variance-authority';

const buttonStyles = cva(
  'px-4 py-2 rounded', // 기본 스타일
  {
    variants: {
      intent: {
        primary: 'bg-blue-500 text-white',
        secondary: 'bg-gray-500 text-black',
      },
      disabled: {
        true: 'opacity-50 cursor-not-allowed',
        false: '',
      },
    },
    defaultVariants: {
      intent: 'primary',
      disabled: false,
    },
  }
);

function Button({ intent, disabled }) {
  return <button className={buttonStyles({ intent, disabled })}>Click Me</button>;
}
  • intentdisabled 변형을 정의하여 구조화된 스타일 관리 가능
  • defaultVariants로 기본 상태를 지정
  • buttonStyles 호출 시 객체 형태로 변형을 전달하여 동적으로 클래스 적용

clsx와 cva 비교

  • clsx는 단순한 조건부 클래스 병합에 적합하며, 유연성과 간결함이 강점
  • cva는 컴포넌트 스타일을 구조적으로 관리하고 변형을 쉽게 적용할 수 있어 더 복잡한 스타일링에 유리함

'Utility' 카테고리의 다른 글

텍스트 에디터 Tiptap 커스터마이징  (0) 2025.01.19
useWebSocket으로 웹소켓 구현하기  (1) 2025.01.14
돌아가는 로딩 스피너 코드  (0) 2024.12.23
clsx  (0) 2024.12.13
Tailwind Merge  (0) 2024.12.13