조건부 클래스를 표현하는데 용이한 라이브러리
# 설치
$ npm install --dev clsx
조건부로 클래스 네임을 추가할 때 undefined
, null
, false
등이 들어갈 수 있는데 자동으로 true 한 string 만 들어가도록 처리해준다.
import clsx from "clsx";
clsx({ foo: true, bar: false, baz: isTrue() });
//=> 'foo baz'
clsx(true, false, "", null, undefined, 0, NaN);
//=> ''
클래스이름 중복을 제거해주는 라이브러리
# 설치
npm i -D tailwind-merge
중복 되는 style 에 대해 뒤에 열거된 style 로 적용해준다.
import { twMerge } from 'tailwind-merge'
twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hover:bg-dark-red p-3 bg-[#B91C1C]'
클래스 이름을 자동 정렬해주는 라이브러리
# 설치
npm install -D prettier prettier-plugin-tailwindcss
Prettier config 파일의 plugins 배열 가장 마지막에 "prettier-plugin-tailwindcss"
을 추가해줘야 한다.
// .prettierrc
{
// ... 생략
"plugins": [ "prettier-plugin-tailwindcss" ] // 여러 플러그인을 설치했다면 가장 마지막에 적어야 한다
}
prettier 를 적용하면 자동 정렬 된다.
Prettier config 파일에 "tailwindAttributes": ['정렬 기준']
을 추가해 정렬 기준에 따라 정렬시킬 수도 있다.
컴포넌트의 상태에 따른 스타일을 달리 표현할때 도움이 되는 라이브러리
# 설치
npm i -D class-variance-authority
// Button.tsx 컴포넌트
import { ButtonProps } from "./types";
import { buttonStyle } from "./styles";
export default function Button ({ className, intent, size, ...props }: ButtonProps) {
return <button className={ buttonStyle({ intent, size, className }) } {...props} />;
}
intent, size 에 따라 다른 className을 적용하는 Button 컴포넌트를 만들어보자
intent, size를 props 로 받아서 className 에 cva 로 만든 buttonStyle 함수에 object 형식으로 넘겨준다.
// styles.ts
import { cva, type VariantProps } from "class-variance-authority";
export const buttonStyle = cva("button", {
variants: {
intent: {
primary: [ // intnet 가 primary 일 때 적용 될 className
"bg-blue-500",
"text-white",
"border-transparent",
"hover:bg-blue-600",
],
secondary: [ // intnet 가 secondary 일 때 적용 될 className
"bg-white",
"text-gray-800",
"border-gray-400",
"hover:bg-gray-100",
],
},
size: {
small: ["text-sm", "py-1", "px-2"], // size 가 small 일 때 적용 될 className
medium: ["text-base", "py-2", "px-4"], // size 가 medium 일 때 적용 될 className
},
},
compoundVariants: [{ intent: "primary", size: "medium", className: "uppercase" }],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
cva 함수의 첫 번째 인자에는 공통으로 적용할 className을 작성한다.
두 번째 인자에는 { variants, compoundVariants, defaultVariants } 가 들어갈 수 있다.
① variants 에는 className 을 다르게 적용할 값을 key, value 형식으로 작성한다.
복수의 클래스 이름에 대해 string, string[] 타입으로 작성할 수 있다.
export const buttonStyle = cva("button", {
variants: {
intent: {
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 : 다양한 조건의 조합에 따라 스타일을 적용해야 할 때 사용
③ defaultVariants : variants 의 key의 값이 없을 때 기본으로 적용할 값
import { type VariantProps } from "class-variance-authority";
import { buttonStyle } from "./styles"
// types.ts
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonStyle> { // ⭐️ VariantProps를 확장해서 사용
/* intent : "primary" | "secondary" | undefined // VariantProps 에서 정의되어 있으므로 생략할 수 있다. */
}
VariantProps 를 확장해서 사용해야 한다.
VariantProps 의 variants 에서 intent, size 를 이미 정의하고 있으므로, props type 에 추가로 작성하지 않아도 된다.