테마 변수
테마 변수 (Theme variables)
"Tailwind 색 팔레트가 마음에 안 드는데, 어떻게 바꾸지?" 하는 고민에서 테마 변수가 출발해요. Tailwind는 프로젝트마다 다른 타이포그래피·색·그림자·브레이크포인트가 필요하다는 걸 전제로 해요. 이런 저수준 설계 결정들은 보통 디자인 토큰이라 부르고, Tailwind에서는 이를 테마 변수로 저장해요. 테마 변수를 이해하면 유틸리티 클래스가 왜 생기고 사라지는지, 그리고 어떻게 내 취향대로 만드는지가 한 번에 보여요.
테마 변수란
테마 변수는 @theme 디렉티브로 정의하는 특별한 CSS 변수예요. 일반 CSS 변수와 달리, 프로젝트에 어떤 유틸리티 클래스가 존재할지를 결정해요. 예를 들어 --color-mint-500을 정의하면 bg-mint-500, text-mint-500, fill-mint-500 같은 클래스가 생겨요.
@import "tailwindcss";
@theme {
--color-mint-500: oklch(0.72 0.11 178);
}
<div class="bg-mint-500"></div>
Tailwind는 테마 변수를 일반 CSS 변수로도 생성하므로, 임의 값이나 인라인 스타일에서 var(--color-mint-500)처럼 참조할 수도 있어요.
왜 :root 대신 @theme일까요
테마 변수는 단순한 CSS 변수가 아니라, 새 유틸리티를 만들라는 지시이기도 해요. 일반 CSS 변수보다 하는 일이 많으니 명시성을 위해 특수 문법을 쓰고, 최상위(다른 셀렉터나 미디어 쿼리 안에 중첩되지 않게)에 정의하도록 강제해요. 유틸리티와 연결할 디자인 토큰이면 @theme, 연결할 필요가 없는 그냥 CSS 변수면 :root를 쓰면 돼요.
유틸리티와의 관계
flex나 object-cover처럼 프로젝트를 가리지 않고 항상 같은 정적 유틸리티도 있어요. 하지만 많은 유틸리티는 테마 변수에 의해 존재해요. 예를 들어 --font-* 네임스페이스의 변수가 font-family 유틸리티를 결정해요. 기본 테마에 --font-sans, --font-serif, --font-mono가 정의돼 있기 때문에 font-sans 같은 클래스가 기본 제공되죠. --font-poppins를 추가하면 font-poppins라는 클래스가 새로 열려요.
변형자도 테마 변수로 만들어져요. --breakpoint-* 네임스페이스의 변수는 반응형 브레이크포인트 변형자를 결정해요. --breakpoint-3xl: 120rem;을 정의하면 3xl:* 변형자를 쓸 수 있어요.
테마 변수 네임스페이스
테마 변수는 네임스페이스로 나뉘고, 각 네임스페이스가 하나 이상의 유틸리티·변형자 API에 대응해요. 자주 쓰는 것만 골라 보면 이래요.
| 네임스페이스 | 대응 유틸리티 |
|---|---|
--color-* |
bg-red-500, text-sky-300 같은 색 유틸리티 |
--font-* |
font-sans 같은 폰트 패밀리 |
--text-* |
text-xl 같은 글자 크기 |
--font-weight-* |
font-bold 같은 글자 굵기 |
--tracking-* |
tracking-wide 같은 자간 |
--leading-* |
leading-tight 같은 줄 높이 |
--breakpoint-* |
sm:* 같은 반응형 변형자 |
--container-* |
@sm:* 같은 컨테이너 쿼리 변형자와 max-w-md 같은 크기 |
--spacing-* |
px-4, max-h-16 같은 간격·크기 |
--radius-* |
rounded-sm 같은 테두리 반경 |
--shadow-* |
shadow-md 같은 그림자 |
--blur-* |
blur-md 같은 흐림 필터 |
--ease-* |
ease-out 같은 전환 타이밍 |
--animate-* |
animate-spin 같은 애니메이션 |
기본 테마 변수
@import "tailwindcss"는 사실 @layer theme, base, components, utilities; 다음에 ./theme.css(레이어 theme), ./preflight.css(레이어 base), ./utilities.css(레이어 utilities)를 순서대로 가져와요. theme.css에 기본 색 팔레트·타입 스케일·그림자·폰트가 담겨 있어요. 그래서 bg-red-200, font-serif, shadow-sm 같은 유틸리티가 기본 제공되는 거고요 — flex-col처럼 프레임워크에 하드코딩된 게 아니라 기본 테마에 의해 만들어진 거예요.
테마 커스터마이징
기본 테마 확장하기
@theme로 새 변수를 정의하면 기본 테마를 확장해요. --font-script를 추가하면 font-script 유틸리티가 생기죠.
@theme {
--font-script: Great Vibes, cursive;
}
기본 테마 오버라이드하기
기본 변수 이름을 다시 정의하면 그 값을 덮어써요. --breakpoint-sm: 30rem;으로 바꾸면 sm:* 변형자가 기본 40rem이 아니라 30rem에서 발동해요. 네임스페이스 전체를 아예 갈아엎으려면 별표 문법으로 initial을 줘요.
@theme {
--color-*: initial;
--color-white: #fff;
--color-midnight: #121063;
}
이러면 bg-red-500 같은 기본 색 유틸리티는 사라지고, 내가 정의한 bg-midnight 같은 것만 남아요.
커스텀 테마만 쓰기
기본 테마를 통째로 끄고 커스텀 값만 쓰려면 전역 네임스페이스를 initial로 두면 돼요.
@theme {
--*: initial;
--spacing: 4px;
--font-body: Inter, sans-serif;
--color-lagoon: oklch(0.72 0.11 221.19);
}
이제 테마 변수 기반의 기본 유틸리티는 없어지고, font-body, text-dusk 같은 내 커스텀 변수에 맞는 것만 쓸 수 있어요.
애니메이션 keyframes 정의
--animate-* 테마 변수의 @keyframes 규칙은 @theme 안에 함께 정의하면 생성된 CSS에 포함돼요.
@theme {
--animate-fade-in-scale: fade-in-scale 0.3s ease-out;
@keyframes fade-in-scale {
0% { opacity: 0; transform: scale(0.95); }
100% { opacity: 1; transform: scale(1); }
}
}
--animate-* 변수 추가 없이 항상 keyframes를 넣고 싶다면 @theme 밖에 정의하면 돼요.
다른 변수 참조하기
다른 변수를 참조하는 테마 변수를 정의할 땐 inline 옵션을 써요. 이러면 유틸리티가 테마 변수를 참조하지 않고 그 값을 직접 사용해요.
@theme inline {
--font-sans: var(--font-inter);
}
모든 CSS 변수 생성하기
기본적으로 최종 CSS에는 사용된 변수만 생성돼요. 항상 모든 변수를 만들어 두려면 static 테마 옵션을 쓰면 됩니다.
@theme static {
--color-primary: var(--color-red-500);
}
프로젝트 간 공유
테마 변수는 CSS로 정의되기 때문에, 별도 CSS 파일에 담아 @import로 여러 프로젝트에서 공유할 수 있어요. 모노레포에서 공용 패키지에 담거나 NPM으로 배포해 서드파티 CSS처럼 가져올 수도 있고요.
테마 변수 활용하기
컴파일을 거치면 모든 테마 변수는 :root의 일반 CSS 변수가 돼요. 그래서 커스텀 CSS에서 var(--text-base)처럼 같은 값을 손쉽게 참조할 수 있어요. JavaScript에서도 Motion 같은 라이브러리로 CSS 변수 값을 애니메이션에 쓰거나, getComputedStyle(document.documentElement).getPropertyValue("--shadow-xl")처럼 해석된 값을 가져올 수 있어요.
더 알아보기 (Learn more)
- Adding custom styles — 테마 커스터마이징과 커스텀 유틸리티 실전 적용
- Functions and directives —
@theme포함 전체 디렉티브 레퍼런스 - Styling with utility classes — 유틸리티 클래스 기본 개념
- Responsive design —
--breakpoint-*로 브레이크포인트 커스터마이징