테마 변수

테마 변수 (Theme variables)

"Tailwind 색 팔레트가 마음에 안 드는데, 어떻게 바꾸지?" 하는 고민에서 테마 변수가 출발해요. Tailwind는 프로젝트마다 다른 타이포그래피·색·그림자·브레이크포인트가 필요하다는 걸 전제로 해요. 이런 저수준 설계 결정들은 보통 디자인 토큰이라 부르고, Tailwind에서는 이를 테마 변수로 저장해요. 테마 변수를 이해하면 유틸리티 클래스가 왜 생기고 사라지는지, 그리고 어떻게 내 취향대로 만드는지가 한 번에 보여요.

출처: Theme variables — Tailwind CSS 공식문서

테마 변수란

테마 변수는 @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를 쓰면 돼요.

유틸리티와의 관계

flexobject-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)