미디어 쿼리로 반응형을 구현하다 보면 한계가 온다. 뷰포트 기준이라는 게 문제다. 같은 카드 컴포넌트가 메인 그리드에 들어갈 때와 사이드바에 들어갈 때 차지하는 공간이 다른데, 뷰포트는 둘 다 동일하다. 결국 특수케이스용 클래스를 따로 만들거나, 컴포넌트를 복제하게 된다.
Container Queries는 뷰포트가 아니라 부모 컨테이너의 크기를 기준으로 스타일을 조건부로 적용한다. 처음 제안이 나온 지 꽤 됐는데, 2023년쯤부터 주요 브라우저가 전부 지원하기 시작했고 지금은 부담 없이 쓸 수 있다.
기본 사용법
두 단계다. 부모에 컨테이너를 선언하고, 자식에서 @container로 조건을 건다.
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 480px) {
.card {
display: grid;
grid-template-columns: 160px 1fr;
gap: 16px;
}
}
container-type: inline-size를 부모에 넣으면 그게 컨테이너가 된다. inline-size는 가로 방향만 쿼리 가능하고, size는 가로·세로 모두 가능하다. 대부분의 경우엔 inline-size로 충분하다.
이름을 붙이면 중첩된 컨테이너를 구분할 수 있다.
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (min-width: 300px) {
.sidebar-card { ... }
}
실제로 써보니
같은 ProductCard 컴포넌트를 홈 화면 그리드, 검색 결과, 관련 상품 슬라이더에 다 쓰는 상황이었다. 미디어 쿼리로는 세 곳이 다 같은 뷰포트 크기인데 레이아웃이 달라야 하는 걸 처리하기가 까다로웠다.
컨테이너 쿼리로 바꾸고 나서는 컴포넌트 코드가 깔끔해졌다. 어디에 놓이든 공간에 맞게 알아서 조정되니까.
.product-card-wrapper {
container-type: inline-size;
}
.product-card {
display: flex;
flex-direction: column;
}
.product-card__image {
aspect-ratio: 4/3;
object-fit: cover;
}
@container (min-width: 400px) {
.product-card {
flex-direction: row;
align-items: center;
}
.product-card__image {
width: 140px;
aspect-ratio: 1;
flex-shrink: 0;
}
}
Tailwind에서 쓰기
Tailwind v3.2부터 @tailwindcss/container-queries 플러그인을 지원한다. v4에서는 기본 내장됐다.
npm install @tailwindcss/container-queries
// tailwind.config.js (v3)
module.exports = {
plugins: [require('@tailwindcss/container-queries')],
}
클래스는 @ 접두사를 붙인다.
<div class="@container">
<div class="flex flex-col @[400px]:flex-row gap-4">
<img class="w-full @[400px]:w-36 aspect-square object-cover" />
<div class="flex-1">...</div>
</div>
</div>
미디어 쿼리와 역할 분리
컨테이너 쿼리가 미디어 쿼리를 완전히 대체하는 건 아니다. 두 가지를 혼용하는 게 자연스럽다.
페이지 레이아웃 전환(사이드바 있는 레이아웃 ↔ 단일 컬럼), 전역 폰트 크기, 다크/라이트 모드 같은 건 여전히 미디어 쿼리가 맞다. 컴포넌트 내부에서 사용 가능한 공간에 따라 달라지는 레이아웃은 컨테이너 쿼리가 훨씬 적합하다.
처음엔 뭔가 복잡할 것 같았는데, 실제로는 미디어 쿼리 쓰는 것과 크게 다르지 않다. 부모에 container-type 하나 추가하고, @media 대신 @container 쓰면 된다. 컴포넌트를 재사용 가능하게 만들어야 한다면 한번쯤 시도해볼 만하다.