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

Tailwind를 처음 쓸 때는 p-4, text-sm처럼 정해진 클래스만 조합해도 화면이 꽤 빠르게 잡힙니다. 버튼, 카드, 리스트 정도는 기본 유틸리티만으로도 큰 무리가 없습니다. 그런데 실제 화면을 맞추다 보면 기본 scale에 없는 값이 필요해지는 순간이 옵니다.
예를 들어 카드 썸네일 높이가 184px로 정해져 있거나, 관리자 화면에서 오른쪽 필터 패널만 320px로 고정해야 하거나, 외부 에디터에서 들어온 본문 안의 링크만 밑줄 처리해야 하는 상황입니다. 이때 대괄호 문법을 만나게 됩니다.
문제는 Arbitrary Value와 Arbitrary Variant가 둘 다 대괄호를 쓰기 때문에 처음에는 같은 종류의 문법처럼 보인다는 점입니다. 하지만 두 문법이 해결하는 문제는 다릅니다. Arbitrary Value는 어떤 값을 넣을 것인가의 문제이고 Arbitrary Variant는 어떤 대상을 골라 스타일을 줄 것인가의 문제입니다.
이 구분이 잡히지 않으면 Tailwind 코드를 읽을 때 대괄호가 나올 때마다 멈추게 됩니다. 반대로 값과 조건을 나눠서 보면 읽는 순서가 단순해집니다. 값이 낯설면 Arbitrary Value를 의심하고, 선택자가 낯설면 Arbitrary Variant를 보면 됩니다.
Arbitrary Value는 값의 예외를 처리한다
Arbitrary Value는 Tailwind가 미리 제공하는 scale 밖의 값을 직접 넣는 방식입니다. h-40처럼 정해진 값이 아니라 h-[184px]처럼 필요한 값을 대괄호 안에 바로 적습니다.
카드 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>
여기서 h-[184px]는 높이 값 하나를 직접 넣은 것입니다. Tailwind v4에서는 h-46처럼 기본 spacing 변수의 배수도 사용할 수 있습니다. 테마 배수 대신 184px라는 고정 픽셀 값을 특정 카드에 직접 지정하려는 경우라면 별도 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로 고정합니다. grid-cols-2나 grid-cols-3처럼 균등 분할이 아니기 때문에 Arbitrary Value가 오히려 더 직접적입니다. 이때 대괄호 안의 _는 공백이 필요한 값을 Tailwind class 안에서 표현하기 위한 장치로 보면 됩니다.
Arbitrary Value와 inline style의 차이
style="width: 320px"로도 같은 결과를 만들 수 있습니다. 그래도 Tailwind 안에서 w-[320px]를 쓰는 이유는 상태 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는 값을 직접 넣는 문법이 아닙니다. 특정 상태, 특정 자식, 특정 내부 태그처럼 스타일을 적용할 조건을 직접 작성하는 방식입니다. 일반적인 hover:, focus:, md:가 이미 준비된 조건이라면, 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>
[&>li]:border-b는 현재 ul의 직접 자식 li에 border-b를 적용한다는 뜻입니다. 여기서 대괄호 안은 값이 아니라 선택자 조건입니다. 뒤에 붙은 border-b가 실제로 적용할 유틸리티입니다.
이 방식은 리스트 아이템을 직접 모두 수정하기 어렵거나 같은 규칙을 부모에서 한 번에 내려야 할 때 유용합니다. 다만 선택자가 길어질수록 HTML 구조와 스타일이 강하게 묶입니다. 구조가 중간에 div로 감싸지면 기대한 스타일이 적용되지 않을 수 있습니다.
외부 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>
[&_h2], [&_p], [&_a]는 현재 section 안의 h2, p, a를 대상으로 합니다. 여기서 _는 공백처럼 읽으면 됩니다. 즉 [&_a]:underline은 현재 요소 안쪽의 모든 링크에 underline을 적용하는 식입니다.
이런 코드는 외부 콘텐츠를 빠르게 정리할 때 유용합니다. 하지만 본문 스타일 규칙이 많아지면 class 속성이 지나치게 길어질 수 있습니다. 그때는 prose 계열 스타일, 별도 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>
이 코드는 현재 버튼이 data-active="true" 상태일 때만 테두리, 배경, 글자색을 바꿉니다. 상태 클래스 이름을 따로 만들지 않고도 Tailwind 유틸리티를 그대로 쓸 수 있습니다. 다만 이 정도 조건이 여러 버튼에서 반복된다면 컴포넌트에서 active 상태에 따라 class 조합을 나누는 방식이 더 읽기 쉬울 수 있습니다.
두 문법을 같이 보면 구분이 쉬워진다
대괄호가 보이면 먼저 질문을 나누면 됩니다. “지금 대괄호 안에 들어간 것이 값인가, 선택자인가?” 이 질문 하나로 대부분 구분됩니다.
| 구분 | Arbitrary Value | Arbitrary Variant |
|---|---|---|
| 해결하는 문제 | 기본 scale에 없는 값을 넣고 싶을 때 | 기본 variant에 없는 조건이나 선택자를 쓰고 싶을 때 |
| 읽는 기준 | 어떤 CSS 값을 넣는가 | 어떤 대상에 유틸리티를 적용하는가 |
| 예시 | h-[184px], w-[320px] |
[&>li]:border-b, [&_a]:underline |
| 주의할 점 | 반복되면 토큰으로 올릴지 검토 | 선택자가 길어지면 구조 의존성 점검 |
예를 들어 h-[184px]는 height의 값을 직접 넣는 것이므로 Arbitrary Value입니다. 반대로 [&_a]:underline는 underline이라는 유틸리티를 어느 대상에게 적용할지 정하는 것이므로 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가 같이 있습니다. [&>li]:grid는 자식 li에게 grid를 적용하는 조건입니다. [&>li]:grid-cols-[80px_minmax(0,1fr)]는 자식 li에게 grid column 값을 직접 넣는 코드입니다. 앞쪽 대괄호는 대상 조건이고, 뒤쪽 대괄호는 값입니다.
이 예시는 실제 코드에서 자주 생기는 혼합 형태입니다. 부모에서 자식 목록의 레이아웃을 통제하고, 그 자식에게는 또 임의 grid 값을 넣고 있습니다. 한 번 읽고 바로 이해된다면 유지해도 되지만, 동료가 코드를 열었을 때 구조를 다시 추적해야 한다면 컴포넌트 분리를 고민할 시점입니다.
실무에서 남겨둘 기준

Arbitrary 문법은 금지할 문법이 아닙니다. 오히려 Tailwind 안에서 예외를 처리할 수 있게 해주는 장치입니다. 문제는 예외가 반복될 때입니다. 한 화면에서만 필요한 값은 대괄호로 끝내도 괜찮지만, 여러 컴포넌트에서 반복되는 값은 디자인 기준이 된 것입니다.
한 번 쓰는 값인지 먼저 본다
h-[184px]가 특정 카드 하나에서만 필요하다면 그대로 둘 수 있습니다. 하지만 상품 카드, 추천 카드, 이벤트 카드에서 모두 같은 높이를 쓴다면 card-thumbnail이라는 컴포넌트 기준을 만들거나 theme 변수로 올리는 편이 낫습니다.
부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.
@theme {
--spacing-card-thumbnail: 11.5rem;
}
부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.
<div class="h-card-thumbnail"> 카드 썸네일 </div>
이렇게 바꾸면 값의 의미가 코드에 남습니다. h-[184px]는 결과만 보이지만 h-card-thumbnail은 왜 그 높이를 쓰는지 이름으로 드러냅니다. 값이 한 번만 필요할 때는 숫자가 직접 보여도 괜찮고, 반복되는 순간부터는 이름이 필요해집니다.
선택자가 길어지면 구조를 다시 본다
Arbitrary Variant는 부모에서 자식 스타일을 한 번에 정리할 수 있어 편합니다. 하지만 [&>div>ul>li]:text-sm처럼 깊어지기 시작하면 Tailwind 문법 문제가 아니라 마크업 구조 문제일 수 있습니다. 이런 코드는 나중에 HTML 구조가 한 단계만 바뀌어도 깨질 가능성이 큽니다.
컴포넌트를 직접 수정할 수 있는 상황이라면 자식 요소에 class를 직접 붙이는 방식이 더 명확할 때가 많습니다. 반대로 외부 HTML처럼 내부 구조를 제어할 수 없다면 Arbitrary Variant를 제한적으로 쓰는 쪽이 현실적입니다.
동적 class 조합은 별도로 조심한다
Arbitrary Value를 쓸 때 w-[${width}px]처럼 런타임에서 문자열을 조합하고 싶어질 수 있습니다. 하지만 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가 감지하기 쉽고, 팀원이 보더라도 어떤 크기가 허용되는지 파악할 수 있습니다.
대괄호가 값인지 선택자인지 읽습니다
본문에서 h-[184px]와 [&_p]:leading-7을 골라 일반 CSS로 각각 설명하세요. 이어서 184px을 바꾸는 경우와 p 대신 a를 고르는 경우를 구분합니다.
풀이 기준과 확인 결과
h-[184px]는 height의 값을 지정합니다. [&_p]:leading-7은 현재 요소 안의 p를 선택한 뒤 줄 높이 유틸리티를 적용합니다.
속성값 변경인지 대상 선택 변경인지 말할 수 있어야 합니다. 같은 값이 반복된다면 공통 토큰으로 정할 필요도 검토하세요.
정리
Tailwind의 대괄호 문법은 하나로 묶어 외우면 오히려 헷갈립니다. Arbitrary Value는 값의 예외이고 Arbitrary Variant는 선택자 조건의 예외입니다. 값이 문제인지, 대상이 문제인지 먼저 나누면 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 클래스 적용 오류 해결: 스타일이 보이지 않을 때 확인할 것
공식 문서: 사용자 정의 스타일과 임의 값 · 임의 variant
이 글이 도움이 되었나요?
Tailwind CSS 학습 순서
필수 15개 · 전체 22개
읽음 기록 관리
전체 과정 목차 (22개)
- 필수 길잡이 · Tailwind CSS 실전 로드맵: 기본·반응형·상태·동적 클래스 학습 순서
- 필수 학습 · Tailwind CSS 기본 클래스 사용법: text font bg spacing 익히기
- 필수 학습 · Tailwind border flex 사용법: 레이아웃 기본 클래스 기준
- 필수 학습 · Tailwind CSS Preflight 기준: 기본 스타일 초기화가 생기는 이유
- 필수 학습 · Tailwind CSS Grid 실무 레이아웃 정리: 카드 목록부터 대시보드까지
- 필수 학습 · Tailwind CSS 반응형 클래스 사용법: sm md lg 기준 잡기
- 필수 학습 · Tailwind CSS 상태 variant 사용법: hover focus aria data 기준 잡기
- 필수 학습 · Tailwind CSS group peer has 차이: 상태 기준으로 고르는 방법
- 필수 학습 · Tailwind CSS dark 사용법: 다크 모드 구현 기준 잡기
- 필수 학습 · Tailwind CSS arbitrary value 차이: 대괄호 문법 사용 기준 잡기 현재 글
- 선택 참고 · Tailwind CSS 임의 값 공백 처리: 대괄호 문법이 깨질 때 해결하기
- 필수 학습 · Tailwind CSS @theme 사용법: 변수와 디자인 토큰 구분하기
- 필수 학습 · Tailwind Container Query 사용법: 부모 너비 기준 반응형 처리
- 필수 학습 · React Tailwind className 정리: 조건부·variant·가독성 기준
- 필수 학습 · Tailwind CSS 동적 클래스 해결: 감지 원리와 @source 사용법
- 필수 학습 · Tailwind CSS @apply 사용 기준: @utility와 custom variant 구분하기
- 선택 참고 · Tailwind CSS transition, animation, motion-reduce 사용 기준 정리
- 선택 참고 · Tailwind CSS v4.1 유틸리티 정리: text-shadow와 mask 사용하기
- 선택 참고 · Radix UI와 shadcn/ui 차이: 컴포넌트를 고를 때 헷갈리는 기준 정리
- 선택 참고 · shadcn/ui No import alias found 오류 해결: tsconfig 설정
- 선택 참고 · Tailwind CSS flex 레이아웃 깨질 때 확인할 것
- 선택 참고 · Tailwind CSS 클래스 적용 안 됨 해결 순서
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.