이 글에서 정리하는 내용
Tailwind CSS v4 기준으로 Arbitrary Value와 Arbitrary Variant를 구분합니다. 둘 다 대괄호 문법을 쓰지만 해결하는 문제는 다릅니다. Arbitrary Value는 기본 scale 밖의 값을 직접 넣는 방식이고, Arbitrary Variant는 기본 variant로 표현하기 어려운 선택자 조건을 직접 만드는 방식입니다. 이 글은 문법 암기보다 실제 화면에서 이 문법을 남겨도 되는지, theme 변수나 별도 CSS로 빼야 하는지를 판단하는 기준에 맞춥니다.
- Tailwind 대괄호 문법이 헷갈리는 이유
- Arbitrary Value는 값의 예외를 처리한다
- Arbitrary Variant는 선택자 조건의 예외를 처리한다
- 두 문법을 같이 보면 구분이 쉬워진다
- 실무에서 남겨둘 기준
- 정리
Tailwind 대괄호 문법이 헷갈리는 이유

Tailwind를 처음 쓸 때는 처럼 정해진 클래스만 조합해도 화면이 꽤 빠르게 잡힙니다. 버튼, 카드, 리스트 정도는 기본 유틸리티만으로도 큰 무리가 없습니다. 그런데 실제 화면을 맞추다 보면 기본 scale에 없는 값이 필요해지는 순간이 옵니다.
예를 들어 카드 썸네일 높이가 184px로 정해져 있거나, 관리자 화면에서 오른쪽 필터 패널만 320px로 고정해야 하거나, 외부 에디터에서 들어온 본문 안의 링크만 밑줄 처리해야 하는 상황입니다. 이때 대괄호 문법을 만나게 됩니다.
문제는 와 이 둘 다 대괄호를 쓰기 때문에 처음에는 같은 종류의 문법처럼 보인다는 점입니다. 하지만 두 문법이 해결하는 문제는 다릅니다. 는 어떤 값을 넣을 것인가의 문제이고은 어떤 대상을 골라 스타일을 줄 것인가의 문제입니다.
이 구분이 잡히지 않으면 Tailwind 코드를 읽을 때 대괄호가 나올 때마다 멈추게 됩니다. 반대로 값과 조건을 나눠서 보면 읽는 순서가 단순해집니다. 값이 낯설면 Arbitrary Value를 의심하고, 선택자가 낯설면 Arbitrary Variant를 보면 됩니다.
Arbitrary Value는 값의 예외를 처리한다
Arbitrary Value는 Tailwind가 미리 제공하는 scale 밖의 값을 직접 넣는 방식입니다.처럼 정해진 값이 아니라처럼 필요한 값을 대괄호 안에 바로 적습니다.
카드 UI를 만들 때 이미지 영역은 이런 문법이 자주 나옵니다. 디자인 시안에서 썸네일 높이가 애매하게 잡혀 있거나, 특정 배너만 고정 비율을 가져야 하는 경우입니다. 이 값이 페이지 전체에서 반복되지 않고 해당 컴포넌트에만 필요한 값이라면 Arbitrary Value로 처리해도 흐름이 크게 깨지지 않습니다.
<article class="overflow-hidden rounded-2xl border border-gray-200 bg-white"> <div class="h-[184px] bg-gray-100"> <img class="h-full w-full object-cover" src="/images/product.jpg" alt="상품 이미지"> </div> <div class="p-4"> <h3 class="text-base font-semibold">상품명</h3> <p class="mt-1 text-sm text-gray-500">간단한 상품 설명</p> </div>
</article>
여기서 는 높이 값 하나를 직접 넣은 것입니다. Tailwind의 기본 높이 scale에 딱 맞는 값이 없고이 높이가 특정 카드 썸네일에서만 필요한 값이라면 별도 CSS 클래스를 만들지 않아도 됩니다. 단같은 높이가 여러 카드, 배너, 리스트에서 반복되기 시작하면 더 이상 일회성 값이 아닙니다. 그때는 theme 변수나 공통 컴포넌트 기준으로 올리는 쪽을 검토해야 합니다.
Arbitrary Value는 단순 px 값에만 쓰이지 않습니다. grid template, CSS 변수처럼 기본 유틸리티로 표현하기 애매한 값에도 자주 사용합니다. 공백이 필요한 값은 대괄호 안에서 언더스코어를 공백처럼 쓰는 형태가 자주 보입니다.
<section class="grid grid-cols-[minmax(0,1fr)_320px] gap-6"> <main class="min-w-0">본문 영역</main> <aside class="w-full">필터 영역</aside>
</section>
이 예시는 관리자 화면이나 상품 목록 페이지에서 자주 볼 수 있는 구조입니다. 왼쪽은 남은 공간을 차지하고, 오른쪽 필터 패널은 320px로 고정합니다. 나 처럼 균등 분할이 아니기 때문에 Arbitrary Value가 오히려 더 직접적입니다. 이때 대괄호 안의 는 공백이 필요한 값을 Tailwind class 안에서 표현하기 위한 장치로 보면 됩니다.
Arbitrary Value와 inline style의 차이
로도 같은 결과를 만들 수 있습니다. 그래도 Tailwind 안에서 를 쓰는 이유는 상태 variant, 반응형 prefix, 다크 모드 같은 Tailwind의 조합 규칙을 계속 활용할 수 있기 때문입니다.
<div class="w-full md:w-[320px] lg:w-[360px]"> 필터 패널
</div>
이 코드는 모바일에서는 전체 너비를 쓰고, 중간 화면부터 고정 너비를 적용합니다. inline style로 처리하면 반응형 조건을 다시 CSS나 로직으로 빼야 합니다. 값 하나만 보면 inline style과 비슷해 보여도, Tailwind class로 남기면 기존 유틸리티 흐름 안에서 관리할 수 있습니다.
Arbitrary Variant는 선택자 조건의 예외를 처리한다
Arbitrary Variant는 값을 직접 넣는 문법이 아닙니다. 특정 상태, 특정 자식, 특정 내부 태그처럼 스타일을 적용할 조건을 직접 작성하는 방식입니다. 일반적인가 이미 준비된 조건이라면, Arbitrary Variant는 개발자가 필요한 조건을 대괄호 안에 직접 적는 방식입니다.
가장 먼저 봐야 할 문자는 입니다. 는 현재 class가 붙은 요소를 가리킵니다. CSS nesting에서 현재 선택자를 의미할 때 쓰는 감각과 비슷하게 읽으면 됩니다.
<ul class="[&>li]:border-b [&>li]:border-gray-200"> <li class="py-3">첫 번째 항목</li> <li class="py-3">두 번째 항목</li> <li class="py-3">세 번째 항목</li>
</ul>
는 현재 의 직접 자식 에 를 적용한다는 뜻입니다. 여기서 대괄호 안은 값이 아니라 선택자 조건입니다. 뒤에 붙은 가 실제로 적용할 유틸리티입니다.
이 방식은 리스트 아이템을 직접 모두 수정하기 어렵거나같은 규칙을 부모에서 한 번에 내려야 할 때 유용합니다. 다만 선택자가 길어질수록 HTML 구조와 스타일이 강하게 묶입니다. 구조가 중간에 로 감싸지면 기대한 스타일이 적용되지 않을 수 있습니다.
외부 HTML이나 에디터 콘텐츠를 다룰 때
Arbitrary Variant가 가장 실감나는 상황은 내부 마크업을 직접 제어하기 어려울 때입니다. 마크다운 본문, CMS 에디터 콘텐츠, 외부에서 내려온 약관 HTML처럼 내부 태그는 들어오지만 각 태그에 Tailwind class를 붙이기 어려운 경우가 있습니다.
<section class="[&_h2]:mt-8 [&_h2]:text-xl [&_p]:mt-3 [&_p]:text-gray-600 [&_a]:underline"> <h2>배송 안내</h2> <p>상품별 배송 일정은 주문 상태에 따라 달라질 수 있습니다.</p> <p>자세한 내용은 <a href="/help">고객센터</a>에서 확인할 수 있습니다.</p>
</section>
는 현재 section 안의 h2, p, a를 대상으로 합니다. 여기서 는 공백처럼 읽으면 됩니다. 즉 은 현재 요소 안쪽의 모든 링크에 underline을 적용하는 식입니다.
이런 코드는 외부 콘텐츠를 빠르게 정리할 때 유용합니다. 하지만 본문 스타일 규칙이 많아지면 이 지나치게 길어질 수 있습니다. 그때는 계열 스타일, 별도 CSS 레이어, 콘텐츠 전용 컴포넌트 중 무엇이 더 읽기 쉬운지 다시 판단해야 합니다.
상태나 속성을 직접 조건으로 쓸 때
Arbitrary Variant는 data 속성이나 aria 상태와도 함께 볼 수 있습니다. 탭, 아코디언, 필터 버튼처럼 상태가 DOM 속성으로 드러나는 컴포넌트에서 자주 쓰입니다.
<button class="border px-4 py-2 text-sm [&[data-active=true]]:border-black [&[data-active=true]]:bg-black [&[data-active=true]]:text-white" data-active="true"> 전체
</button>
이 코드는 현재 버튼이 상태일 때만 테두리, 배경, 글자색을 바꿉니다. 상태 클래스 이름을 따로 만들지 않고도 Tailwind 유틸리티를 그대로 쓸 수 있습니다. 다만 이 정도 조건이 여러 버튼에서 반복된다면 컴포넌트에서 active 상태에 따라 class 조합을 나누는 방식이 더 읽기 쉬울 수 있습니다.
두 문법을 같이 보면 구분이 쉬워진다
대괄호가 보이면 먼저 질문을 나누면 됩니다. “지금 대괄호 안에 들어간 것이 값인가, 선택자인가?” 이 질문 하나로 대부분 구분됩니다.
| 구분 | Arbitrary Value | Arbitrary Variant |
|---|---|---|
| 해결하는 문제 | 기본 scale에 없는 값을 넣고 싶을 때 | 기본 variant에 없는 조건이나 선택자를 쓰고 싶을 때 |
| 읽는 기준 | 어떤 CSS 값을 넣는가 | 어떤 대상에 유틸리티를 적용하는가 |
| 예시 | , | , |
| 주의할 점 | 반복되면 토큰으로 올릴지 검토 | 선택자가 길어지면 구조 의존성 점검 |
예를 들어 는 의 값을 직접 넣는 것이므로 Arbitrary Value입니다. 반대로 는 라는 유틸리티를 어느 대상에게 적용할지 정하는 것이므로 Arbitrary Variant입니다.
둘을 섞어서 쓰는 경우도 있습니다. 이 지점에서 대괄호 문법이 더 복잡해 보입니다.
<ul class="[&>li]:grid [&>li]:grid-cols-[80px_minmax(0,1fr)] [&>li]:gap-4"> <li> <span>상태</span> <strong>주문이 접수되었습니다.</strong> </li>
</ul>
여기에는 Arbitrary Variant와 Arbitrary Value가 같이 있습니다. 는 자식 에게 grid를 적용하는 조건입니다. 는 자식 에게 grid column 값을 직접 넣는 코드입니다. 앞쪽 대괄호는 대상 조건이고, 뒤쪽 대괄호는 값입니다.
이 예시는 실제 코드에서 자주 생기는 혼합 형태입니다. 부모에서 자식 목록의 레이아웃을 통제하고, 그 자식에게는 또 임의 grid 값을 넣고 있습니다. 한 번 읽고 바로 이해된다면 유지해도 되지만, 동료가 코드를 열었을 때 구조를 다시 추적해야 한다면 컴포넌트 분리를 고민할 시점입니다.
실무에서 남겨둘 기준

Arbitrary 문법은 금지할 문법이 아닙니다. 오히려 Tailwind 안에서 예외를 처리할 수 있게 해주는 장치입니다. 문제는 예외가 반복될 때입니다. 한 화면에서만 필요한 값은 대괄호로 끝내도 괜찮지만, 여러 컴포넌트에서 반복되는 값은 디자인 기준이 된 것입니다.
한 번 쓰는 값인지 먼저 본다
가 특정 카드 하나에서만 필요하다면 그대로 둘 수 있습니다. 하지만 상품 카드, 추천 카드이벤트 카드에서 모두 같은 높이를 쓴다면 이라는 컴포넌트 기준을 만들거나 theme 변수로 올리는 편이 낫습니다.
@theme { --spacing-card-thumbnail: 11.5rem;
}
<div class="h-card-thumbnail"> 카드 썸네일
</div>
이렇게 바꾸면 값의 의미가 코드에 남습니다. 는 결과만 보이지만은 왜 그 높이를 쓰는지 이름으로 드러냅니다. 값이 한 번만 필요할 때는 숫자가 직접 보여도 괜찮고, 반복되는 순간부터는 이름이 필요해집니다.
선택자가 길어지면 구조를 다시 본다
Arbitrary Variant는 부모에서 자식 스타일을 한 번에 정리할 수 있어 편합니다. 하지만 처럼 깊어지기 시작하면 Tailwind 문법 문제가 아니라 마크업 구조 문제일 수 있습니다. 이런 코드는 나중에 HTML 구조가 한 단계만 바뀌어도 깨질 가능성이 큽니다.
컴포넌트를 직접 수정할 수 있는 상황이라면 자식 요소에 class를 직접 붙이는 방식이 더 명확할 때가 많습니다. 반대로 외부 HTML처럼 내부 구조를 제어할 수 없다면 Arbitrary Variant를 제한적으로 쓰는 쪽이 현실적입니다.
동적 class 조합은 별도로 조심한다
Arbitrary Value를 쓸 때 처럼 런타임에서 문자열을 조합하고 싶어질 수 있습니다. 하지만 Tailwind는 프로젝트 파일 안에서 class처럼 보이는 문자열을 기준으로 CSS를 생성합니다. 빌드 시점에 완성된 class 문자열을 확인할 수 없는 방식이면 스타일이 생성되지 않을 수 있습니다.
<div class="w-[320px]">감지 가능한 클래스</div>
<div className={`w-[${width}px]`}> 빌드 시점에 예측하기 어려운 클래스
</div>
값이 사용자 입력이나 서버 데이터에 따라 계속 바뀌는 구조라면 Tailwind class보다 inline style이나 CSS 변수 조합이 더 맞을 수 있습니다. 반대로 가능한 값이 몇 개로 정해져 있다면 class map을 만들어 명시적인 문자열로 남기는 방식이 안정적입니다.
const sizeClassMap = { sm: "w-[240px]", md: "w-[320px]", lg: "w-[400px]"}; <aside className={sizeClassMap[size]}> 필터 패널
</aside>
이 방식은 가능한 class가 코드에 그대로 남기 때문에 Tailwind가 감지하기 쉽고, 팀원이 보더라도 어떤 크기가 허용되는지 파악할 수 있습니다.
정리
Tailwind의 대괄호 문법은 하나로 묶어 외우면 오히려 헷갈립니다. 는 값의 예외이고는 선택자 조건의 예외입니다. 값이 문제인지, 대상이 문제인지 먼저 나누면 Arbitrary Value와 Arbitrary Variant의 차이는 비교적 선명해집니다.
실제 작업에서는 대괄호 문법을 쓰느냐 마느냐보다, 그 예외가 계속 반복되는지를 보는 쪽이 더 중요합니다. 한 번 쓰는 픽셀 값은 대괄호로 처리해도 됩니다. 같은 값이 여러 UI에서 반복되면 token이나 컴포넌트 이름이 필요합니다. 선택자도 마찬가지입니다. 외부 콘텐츠를 제어해야 한다면 Arbitrary Variant가 유용하지만, 직접 관리하는 컴포넌트라면 구조를 나누는 쪽이 더 읽기 쉬울 수 있습니다.
다음에 Tailwind 코드에서 대괄호를 만나면 먼저 이렇게 보면 됩니다. 대괄호 안이 값이면 Arbitrary Value, 조건이나 선택자면 Arbitrary Variant입니다. 그리고 한 번 더 봐야 할 것은 그 코드가 예외로 끝나는지, 앞으로 계속 반복될 기준이 되는지입니다.
참고 자료
Tailwind CSS v4 공식 문서의 adding custom styles, arbitrary values, arbitrary variants, theme variables, hover and other states 설명을 기준으로 개념과 문법 기준을 확인했습니다.
같이 읽으면 좋은 글
- Tailwind CSS 실무 로드맵: 기본 클래스부터 반응형·상태·동적 클래스까지
- Tailwind CSS 클래스 감지 원리: 동적 className이 빠지는 이유
- Tailwind CSS 클래스 적용 오류 해결: 스타일이 보이지 않을 때 확인할 것
“Tailwind CSS arbitrary value 차이: 대괄호 문법 사용 기준 잡기”에 대한 5개의 생각