
Tailwind Variant 톺아보기
Tailwind CSS를 위한 API Tailwind Variant, 과연 Tailwind CCS의 단점을 완벽히 보완할 수 있을까?
Tailwind CSS 란?
tailwind CSS 란 CSS 프레임워크 중 하나로, 이미 만들어진 유틸리티 클래스를 사용해 스타일을 적용한다.
공식 문서에서 설명하는 주 특징은 다음과 같다.
- Utility-First Fundamentals
- 제한된 기본 유틸리티 집합에서 복잡한 컴포넌트를 구축하는 것이 가능.
- Handling Hover, Focus, and Other States
- hover, foucs등의 가상 selector를 tailwind의 모든 유틸리티 클래스에 조건부로 적용할 수 있다.
- 원하는 조건을 대상으로 클래스 이름의 시작 부분에 prefix를 추가하여 적용
- Responsive Design
- tailwind의 모든 유틸리티 클래스에 반응형을 적용할 수 있다.
가상 selector와 마찬가지로 원하는 조건을 대상으로 클래스 이름의 시작 부분에 breakpoint를 추가하여 적용
config 파일에서 원하는 미디어 크기를 등록할 수 있다.
- tailwind의 모든 유틸리티 클래스에 반응형을 적용할 수 있다.
- Dark Mode
- 다크 모드가 활성화되었을 때 사이트를 다르게 스타일링할 수 있는 값이 있다.
- 마찬가지로 원하는 조건을 대상으로 클래스 이름의 시작 부분에 'dark:' prefix 를 추가하여 적용한다.
- Reusing Style
- Tailwind는 디자인을 낮은 수준의 유틸리티 클래스만 사용하여 구현하는 utility-first 워크플로우를 권장한다.
- 반복되는 조합이 생길 경우에는 반복 렌더링, 컴포넌트 분리, @apply를 사용하여 클래스 추출 할 것을 권장하고 있다.
- Adding Custom Styles
- Tailwind는 확장 가능하고 사용자 정의 가능하도록 처음부터 설계되었다.
- 색상 팔레트, 간격 척도, 타이포그래피 척도 등의 설정을 tailwind.config.js 에서 커스텀 할 수 있다.
인라인 스타일로 적용할 수 있고 tailwind CSS의 유연성이 Next.js의 구조와 잘 호환되기 때문에 많이 사용된다.
하지만 단점 또한 존재한다.
1. 학습의 어려움
Tailwind CSS에는 필요한 모든 스타일들이 유틸리티 클래스로 이미 구현되어 있다. 때문에 프로젝트에 적용하기 위해선 클래스들을 알고있을 필요가 있다.
아니면 공식문서를 항상 켜놓는다거나..
2. 못생긴 코드
인라인에서 작업하다보니 적용해야할 스타일이 많아지면, 자연스레 코드도 길어진다. 가독성도 떨어지고 수정도 어려운건 덤.
이러한 이슈 때문에 Tailwind를 싫어하고 css module, vanilla-extra 등 다른 라이브러리를 선택하는 개발자들도 많다.
나 또한 이런 이유때문에 다른 라이브러리를 고려해보았으나 그러다 발견한 라이브러리
Tailwind Variants
Tailwind Variants란?
TailwindCSS를 위한 일급 변형(API) 라이브러리라고 한다.
예시를 보자
import { tv } from 'tailwind-variants';
const button = tv({
base: 'font-medium bg-blue-500 text-white rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 text-white',
secondary: 'bg-purple-500 text-white'
},
size: {
sm: 'text-sm',
md: 'text-base',
lg: 'px-4 py-3 text-lg'
}
},
compoundVariants: [
{
size: ['sm', 'md'],
class: 'px-3 py-1'
}
],
defaultVariants: {
size: 'md',
color: 'primary'
}
});
return (
<button className={button({ size: 'sm', color: 'secondary' })}>
Click me
</button>
);
tv의 옵션에 Tailwind의 Utility Class 와 Variants를 전달하여 생성한 함수의 반환값을 className으로 사용해 스타일링을 해주는 것 처럼 보인다.
Tailwind Variants에서 주장하는 주요 기능은 다음과 같다.
Variants
tv로 만든 함수의 인자에 variants를 전달할 수 있다. Stitches 에서 영향을 받았다는듯
전달하는 variants에 제한은 없으며, 이를 사용해 동일한 스타일 구성 요소의 여러 버전을 만들 수 있다.
Responsesive Variants
import { tv } from 'tailwind-variants';
const button = tv(
{
base: 'font-semibold text-white py-1 px-3 rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 hover:bg-blue-700',
secondary: 'bg-purple-500 hover:bg-purple-700',
success: 'bg-green-500 hover:bg-green-700',
error: 'bg-red-500 hover:bg-red-700'
}
}
},
{
responsiveVariants: ['xs', 'sm', 'md'] // `true` to apply to all screen sizes
}
);
button({
color: {
initial: 'primary',
xs: 'secondary',
sm: 'success',
md: 'error'
}
});
/**
* Result:
* font-semibold text-white py-1 px-3 rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700
* xs:bg-purple-500 xs:hover:bg-purple-700 sm:bg-green-500 sm:hover:bg-green-700 md:bg-red-500
* md:hover:bg-red-700
*/
스타일 생성시 responsiveVariants 에 Tailwind CSS 에서 정의된 미디어 쿼리 breakpoint를 넣으면 variants를 사용하는 것처럼 반응형 스타일링이 가능하다.
Split components into multiple slots
import { tv } from 'tailwind-variants';
const card = tv({
slots: {
base: 'md:flex bg-slate-100 rounded-xl p-8 md:p-0 dark:bg-gray-900',
avatar:
'w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto drop-shadow-lg',
wrapper: 'flex-1 pt-6 md:p-8 text-center md:text-left space-y-4',
description: 'text-md font-medium',
infoWrapper: 'font-medium',
name: 'text-sm text-sky-500 dark:text-sky-400',
role: 'text-sm text-slate-700 dark:text-slate-500'
}
});
const { base, avatar, wrapper, description, infoWrapper, name, role } = card();
return (
<figure className={base()}>
<img
className={avatar()}
src="/intro-avatar.png"
alt=""
width="384"
height="512"
/>
<div className={wrapper()}>
<blockquote>
<p className={description()}>
“Tailwind variants allows you to reduce repeated code in your project
and make it more readable. They fixed the headache of building a
design system with TailwindCSS.”
</p>
</blockquote>
<figcaption className={infoWrapper()}>
<div className={name()}>Zoey Lang</div>
<div className={role()}>Full-stack developer, NextUI</div>
</figcaption>
</div>
</figure>
);
슬롯 속성을 사용하여 여러 구성 요소를 한 번에 스타일링할 수 있다.
Override
import { tv } from 'tailwind-variants';
const button = tv({
base: 'font-semibold text-white py-1 px-3 rounded-full active:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 hover:bg-blue-700',
secondary: 'bg-purple-500 hover:bg-purple-700',
success: 'bg-green-500 hover:bg-green-700',
error: 'bg-red-500 hover:bg-red-700'
}
}
});
button({
color: 'secondary',
class: 'bg-pink-500 hover:bg-pink-500' // overrides the color variant
});
/**
* Result:
* font-semibold text-white py-1 px-3 rounded-full active:opacity-80 bg-pink-500 hover:bg-pink-500
*/
Tailwind Varaint는 모든 className 요소를 재정의 할 수 있다.
위 예시의 버튼의 경우 className={`${button({color: 'red'})} bg-black-500`}
이런식으로도 오버라이딩이 가능하다.
Components composition
import { tv } from 'tailwind-variants';
const baseButton = tv({
base: [
'font-semibold',
'dark:text-white',
'py-1',
'px-3',
'rounded-full',
'active:opacity-80',
'bg-zinc-100',
'hover:bg-zinc-200',
'dark:bg-zinc-800',
'dark:hover:bg-zinc-800'
]
});
const buyButton = tv({
extend: baseButton,
base: [
'text-sm',
'text-white',
'rounded-lg',
'shadow-lg',
'uppercase',
'tracking-wider',
'bg-blue-500',
'hover:bg-blue-600',
'shadow-blue-500/50',
'dark:bg-blue-500',
'dark:hover:bg-blue-600'
]
});
return (
<div className="flex gap-3">
<button className={baseButton()}>Button</button>
<button className={buyButton()}>Buy button</button>
</div>
);
/**
* buyButton();
*
* Result:
* font-semibold dark:text-white py-1 px-3 active:opacity-80 text-sm text-white rounded-lg
* shadow-lg shadow-blue-500/50 uppercase tracking-wider bg-blue-500 hover:bg-blue-600
* dark:bg-blue-500 dark:hover:bg-blue-600
*/
extend 매개변수를 사용하여 컴포넌트를 구성할 수 있다.
이는 확장된 컴포넌트의 클래스, 슬롯, 변형, defaultVariants 및 compoundVariants를 자동으로 병합한다.
tv로 스타일을 분리하여 인라인 코드가 길어지는 문제도 방지했고, 오버라이딩과 합성으로 스타일의 재사용성 또한 높였다. variant를 사용하여 버전을 관리하고 반응형 스타일링을 손쉽게 할 수 있는점은 강력해 보이기까지 한다.
이렇게만 보면 Tailwind Variant는 Tailwind CSS의 단점은 어느정도 상쇄하는것으로 보인다.
하지만 아직 완벽하진 않다. 사용하면서 느낀 단점들을 정리해보겠다.
단점
1. 아직 정식버전이 아님
Tailwind Varaint는 2024년 4월 기준 v0.2.1로 정식 버전이 나오지 않았다.
때문에 아직 불안정하고 관련 자료도 많지 않다.
2. 자동완성 미지원
Tailwind 는 공식 익스텐션을 제공하여 VSC, Webstom 등 대부분의 IDE 에서 자동완성을 할 수 있다.
하지만 Tailwind Variant를 사용하여 스타일을 분리할 경우 이 기능을 사용할 수 없다.
나는 이 블로그를 Tailwind Varaint를 사용하여 개발했다. 참고할 자료는 거의 공식 문서 뿐이고 혼란스러운 점도 많았지만 확실히 코드 가독성은 향상되었다.
하루빨리 정식 버전이 출시하길 고대하고있다.