-
Tailwind CSS 잘 활용하기 with Clxs, CVA, twMergeStyle/Libraries 2024. 3. 31. 14:45
최근 팀 내에서 Tailwind CSS Class를 사용할 때 좀 더 효율성 있게 작성하는 방식에 대해 논의하게 되었습니다. 기존 작업 중인 프로젝트에서는 Styled Component와 Tailwind CSS를 같이 쓰는 방식을 채택하여 사용 중인데 새롭게 들어가는 프로젝트에선 clsx, class-variance-authority, tailwind-merge를 조합하여 사용하는 방식을 사용해 보려고 합니다.
필요 라이브러리
※ React에서 Tailwind CSS 설정이 되어 있는 환경을 가정합니다.
clsx는 조건부 className 처리를 위해 사용되며 라이브러리 크기가 매우 작은 장점을 가지고 있습니다.
import { clsx } from 'clsx'; clsx('w-full', true && 'h-full', { 'px-4': true, 'py-2': false }, [ 'flex', 'items-center', ]); // => w-full h-full px-4 flex items-center
cva(class-variance-authority)는 props에 따른 className 처리를 위해 사용됩니다.
import { cva } from 'class-variance-authority'; export const ComponentVariants = cva( ` flex items-center justify-center `, { variants: { variant: { default: '', full: 'w-full h-full', }, }, defaultVariants: { variant: 'default', }, }, ); // <div className={ComponentVariants({ variant })} />
tailwind-merge는 className 충돌을 막아주어 겹치는 className의 경우 마지막에 선언된 className 기준 오버라이드 처리됩니다.
import { twMerge } from 'tailwind-merge' twMerge('px-2 py-1 bg-red-600 p-3 bg-grey-800') // => p-3 bg-grey-800
npm install clsx class-variance-authority tailwind-merge --dev # or yarn add clsx class-variance-authority tailwind-merge --dev
util 함수 작성
clsx, class-variance-authority, tailwind-merge를 조합하여 사용하기 위해 유틸함수를 만듭니다.
// utils/style.ts import { ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; export const cn = (...inputs: ClassValue[]) => { return twMerge(clsx(inputs)); };
clsx와 tailwind-merge를 통해 조건부 className과 className 충돌을 방지하는 유틸함수 입니다.
cva와 작성된 유틸함수 cn을 활용한 Button 예제입니다.
// components/Button.tsx import { cva, VariantProps } from 'class-variance-authority'; import { cn } from '@/utils/style'; export const ButtonVariants = cva( ` flex items-center justify-center bg-gray-950 text-white rounded-sm text-base font-medium `, { variants: { variant: { default: 'shadow-none', grey: 'bg-gray-150 text-gray-950', red: 'bg-red-600', }, size: { default: 'px-2 py-1', md: 'px-4 py-2', lg: 'px-6 py-3 text-lg', xl: 'px-8 py-4 text-xl', }, }, defaultVariants: { variant: 'default', size: 'default', }, }, ); interface ButtonProps extends VariantProps<typeof ButtonVariants> { className?: string; children?: React.ReactNode; } function Button({ variant, size, className, children }: ButtonProps) { return ( <button className={cn(ButtonVariants({ variant, size, className }))}> {children && children} </button> ); } export default Button;
cva에서는 선언된 variants 외에도 className을 추가로 받을 수 있습니다. 추가할 className이 없다면 이 부분은 생략하셔도 됩니다.
작성된 Button Component 사용 예제입니다.
import Button from '@/components/Button'; function Component() { return ( <div className="w-full h-full p-10 space-y-2"> <Button size="md">btn1</Button> <Button className="text-red-600" variant="grey" size="lg"> btn2 </Button> <Button variant="red" size="xl"> btn3 </Button> </div> ); } export default Component;
VSCode 추가 설정
VSCode에서 Tailwind CSS IntelliSense extension을 사용할 경우 cva 내부에서 작동을 안 하는 것을 확인할 수 있습니다. cva 내부에서 작동할 수 있도록 setting.json에 해당 항목을 추가합니다.
{ ... "tailwindCSS.experimental.classRegex": [ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] ] }
마무리
Tailwind CSS와 같이 사용할 수 있는 라이브러리 덕분에 Tailwind CSS를 잘 활용할 수 있는 방법이 계속 나오고 있습니다. 이전에는 Styled Components와 Tailwind CSS를 같이 쓰는 방식을 포스팅했었는데 작업 환경에 맞게 여러 방식을 선택하여 사용하면 되겠습니다. 단순히 Tailwind CSS를 쓰는 것에 그치지 않고 좀 더 활용할 수 있는 방안을 계속 찾아봐야겠습니다.
참고 자료
https://xionwcfm.tistory.com/328
https://xionwcfm.tistory.com/325
'Style > Libraries' 카테고리의 다른 글
Styled Components with Tailwind CSS (0) 2023.09.24