Next.js에서 간단한 프로젝트를 하면서 Tailwindcss를 도입하게 되었는데, 이때 styled-components에서 처럼 props를 활용해 동적 스타일링을 하려 했습니다.
하지만 분명 class의 이름은 제대로 들어가 있는데 동적 스타일링이 되지 않아서 분석하게 되었고, 이를 포스팅하게 되었습니다.
문제 상황


function CardDescription({
text,
size = 'sm',
}: {
text: string;
size?: 'xs' | 'sm' | 'lg';
}) {
return (
<p
className={`w-full m-0 ${'text-' + size} ${
size === 'lg' ? 'opacity-' + 85 : 'opacity-' + 50
}`}
>
{text}
</p>
);
}
<CardLinkWrapper link="https://map.naver.com/p/entry/place/1426094200?c=15.00,0,0,0,dh">
<CardTitle title="오시는 길" />
<address className="m-0 text-sm opacity-50">
울산 남구 문수로335번길 6 <br /> 길상 빌딩 5층
</address>
<CardDescription text="클릭 시 네이버지도로 이동" size="xs" />
</CardLinkWrapper>
현재 작성된 코드들은 위와 같습니다.
카드안에 들어갈 상세 설명들은 중요도에 따라서 크기와 투명도를 다르게 주려고 합니다.
이때 'xs', 'sm', 'lg' 세가지 타입이 있고 기본값은 sm로 등록하였습니다.
현재 상황은 addres 태그에는 sm의 크기로, 세부 설명은 xs크기로 주고 있는데, text-xs가 제대로 적용되고 있지 않습니다.
이를 해결하기 위해서 공식문서를 참고하였고, 몇 가지 유의사항을 확인하게 되었습니다.
https://tailwindcss.com/docs/content-configuration#dynamic-class-names
Content Configuration - Tailwind CSS
Configuring the content sources for your project.
tailwindcss.com
테일윈드 공식문서에 따르면 테일윈드가 어떻게 css 적용에 사용될 class들을 추출해 내는지를 아래와 같이 말합니다.
The way Tailwind scans your source code for classes is intentionally very simple — we don’t actually parse or execute any of your code in the language it’s written in, we just use regular expressions to extract every string that could possibly be a class name.
모든 코드의 언어들이 아니라, 단지 정규 표현식을 사용하여 클래스 이름으로 가능성이 있는 모든 문자열을 추출합니다.
그렇기 때문에 동적 스타일링을 할 때 유의할 점이 생깁니다.
1. 완벽한 잘리지 않은 단어들
The most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files.
If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS:
//잘못된 예시
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
// 제대로 된 예시
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
2. props로 스타일을 넘기지 않기
단순하게 props가 클라스 그 자체가 아니라, 완성된 클라스를 이미 등록해놓고, props를 통해서 선택만 하도록 작성해야 합니다.
// 잘못된 예시
function Button({ color, children }) {
return (
<button className={`bg-${color}-600 hover:bg-${color}-500 ...`}>
{children}
</button>
)
}
아래와 같이 변수에 등록을 해야지 tailwind 가 해당 class를 추출할 수 있습니다.
// 맞는 예시
function Button({ color, children }) {
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500',
red: 'bg-red-600 hover:bg-red-500',
}
return (
<button className={`${colorVariants[color]} ...`}>
{children}
</button>
)
}
이제 위의 원칙들을 유의해서 제 코드를 수정해보겠습니다.
function CardDescription({
text,
size = 'sm',
}: {
text: string;
size?: 'xs' | 'sm' | 'lg';
}) {
const sizeVariants = {
xs: 'text-xs opacity-50',
sm: 'text-sm opacity-50',
lg: 'text-lg opacity-85',
};
return <p className={`w-full m-0 ${sizeVariants[size]}`}>{text}</p>;
}

제대로 반영된 것을 확인할 수 있습니다.