DevToolBox무료
블로그

CSS Container Queries 완전 가이드 2026

13분by DevToolBox

CSS 컨테이너 쿼리는 미디어 쿼리 이후 반응형 디자인에서 가장 중요한 발전입니다. 뷰포트 크기가 아닌 부모 컨테이너의 크기에 응답하는 컴포넌트를 구축할 수 있습니다.

컨테이너 쿼리란?

컨테이너 쿼리는 뷰포트가 아닌 포함 요소의 크기에 기반하여 스타일을 적용합니다.

/* The Problem: A card component in different contexts */

/* In main content (800px wide) → horizontal layout */
/* In sidebar (300px wide) → vertical layout */
/* In footer (200px wide) → minimal layout */

/* With media queries, you cannot distinguish these contexts
   because they all exist at the same viewport width.

   With container queries, each container context triggers
   different styles automatically. */

컨테이너 쿼리 vs 미디어 쿼리

미디어 쿼리는 뷰포트 크기에만 응답합니다. 컨테이너 쿼리는 이 근본적 제한을 해결합니다.

FeatureMedia QueriesContainer Queries
Responds toViewport sizeContainer size
ScopeGlobal (page-level)Local (component-level)
ReusabilityBreaks in different contextsWorks in any context
Use casePage layoutComponent layout
Unitsvw, vh, vmin, vmaxcqw, cqh, cqi, cqb
Browser supportUniversalAll modern browsers (2023+)

컨테인먼트 이해

컨테이너 쿼리에는 컨테인먼트가 필요합니다.

컨테인먼트 유형

/* container-type values */

/* inline-size: Containment on the inline axis only (width) */
/* Most common — use this in most cases */
.card-wrapper {
  container-type: inline-size;
}

/* size: Containment on both inline and block axes (width + height) */
/* Use when you need to query both dimensions */
.widget-wrapper {
  container-type: size;
}

/* normal (default): No containment — cannot be queried */
.regular-div {
  container-type: normal; /* This is the default */
}

/* IMPORTANT: container-type: size prevents auto-height behavior.
   The element will not grow to fit its content on the block axis.
   Use inline-size unless you specifically need height queries. */

컨테이너 쿼리 구문

기본 구문은 container-type으로 컨테이너를 정의하고 @container로 쿼리합니다.

/* Step 1: Define a container */
.card-container {
  container-type: inline-size;
}

/* Step 2: Write container queries */
.card {
  /* Default styles (smallest) */
  display: grid;
  gap: 12px;
  padding: 16px;
}

.card__image {
  width: 100%;
  aspect-ratio: 16/9;
  border-radius: 8px;
  object-fit: cover;
}

.card__title {
  font-size: 1rem;
  font-weight: 700;
}

/* When container is at least 400px wide */
@container (min-width: 400px) {
  .card {
    grid-template-columns: 200px 1fr;
    align-items: center;
  }
  .card__title {
    font-size: 1.25rem;
  }
}

/* When container is at least 600px wide */
@container (min-width: 600px) {
  .card {
    grid-template-columns: 280px 1fr;
    gap: 24px;
    padding: 24px;
  }
  .card__title {
    font-size: 1.5rem;
  }
  .card__description {
    display: block; /* Show description only in wide containers */
  }
}

명명된 컨테이너

특정 조상을 대상으로 컨테이너에 이름을 지정할 수 있습니다.

/* Name containers to target specific ancestors */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

.main-content {
  container-type: inline-size;
  container-name: main;
}

/* Query a specific named container */
@container sidebar (max-width: 300px) {
  .widget {
    font-size: 0.875rem;
  }
}

@container main (min-width: 800px) {
  .article-card {
    grid-template-columns: 1fr 1fr;
  }
}

/* Without a name, @container queries the NEAREST ancestor
   with containment. Named queries skip to the named container. */

container 축약형

container-type과 container-name을 하나로 결합합니다.

/* Shorthand: container: <name> / <type> */
.sidebar {
  container: sidebar / inline-size;
}

/* Equivalent to: */
.sidebar {
  container-name: sidebar;
  container-type: inline-size;
}

/* Multiple names (for querying by either name) */
.widget-area {
  container: widget-area panel / inline-size;
}

컨테이너 쿼리 단위

컨테이너 크기에 상대적인 새로운 CSS 단위가 도입되었습니다.

UnitDescriptionEquivalent
cqw1% of container widthSimilar to vw for viewport
cqh1% of container heightSimilar to vh for viewport
cqi1% of container inline sizeWidth in horizontal writing
cqb1% of container block sizeHeight in horizontal writing
cqminSmaller of cqi and cqbSimilar to vmin
cqmaxLarger of cqi and cqbSimilar to vmax
/* Fluid typography with container query units */
.card-title {
  /* Font size scales with container width */
  /* Minimum 1rem, maximum 2rem, scales at 5cqi */
  font-size: clamp(1rem, 5cqi, 2rem);
}

.card-padding {
  /* Responsive padding based on container */
  padding: clamp(12px, 4cqi, 32px);
}

.hero-text {
  /* Large text that scales with its container */
  font-size: clamp(2rem, 8cqi, 5rem);
  line-height: 1.1;
}

실제 예제

반응형 카드 컴포넌트

컨테이너 공간에 적응하는 카드 컴포넌트.

/* Responsive card component */
.card-container {
  container: card / inline-size;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  border-radius: 12px;
  background: var(--card-bg);
  border: 1px solid var(--border-color);
}

.card__image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 8px;
}

.card__meta {
  display: none; /* Hidden in small containers */
}

.card__actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* Medium card (400px+) — side-by-side layout */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: flex-start;
  }
  .card__image {
    width: 180px;
    flex-shrink: 0;
    aspect-ratio: 1;
  }
  .card__meta {
    display: flex;
    gap: 12px;
    font-size: 0.875rem;
    color: var(--text-muted);
  }
}

/* Large card (700px+) — featured layout */
@container card (min-width: 700px) {
  .card {
    flex-direction: column;
    padding: 0;
    overflow: hidden;
  }
  .card__image {
    width: 100%;
    height: 300px;
    border-radius: 0;
  }
  .card__content {
    padding: 24px;
  }
  .card__title {
    font-size: 1.75rem;
  }
}

반응형 내비게이션

컨테이너 너비에 따라 레이아웃을 전환하는 내비게이션.

/* Navigation that adapts to its container */
.nav-container {
  container: nav / inline-size;
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.nav__item {
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 0.875rem;
}

.nav__item-icon {
  display: none;
}

/* Horizontal layout when container is wide enough */
@container nav (min-width: 500px) {
  .nav {
    flex-direction: row;
    gap: 8px;
  }
  .nav__item {
    white-space: nowrap;
  }
}

/* Show icons when there is more space */
@container nav (min-width: 700px) {
  .nav__item-icon {
    display: inline-flex;
    margin-right: 6px;
  }
  .nav__item {
    padding: 10px 16px;
  }
}

적응형 그리드 아이템

그리드 열 너비에 따라 레이아웃을 변경.

/* Grid items that adapt to their column width */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 24px;
}

/* Each grid item is a container */
.grid__item {
  container: grid-item / inline-size;
}

.product {
  text-align: center;
  padding: 16px;
}

.product__price {
  font-size: 1.5rem;
  font-weight: 700;
}

.product__details {
  display: none;
}

@container grid-item (min-width: 350px) {
  .product {
    display: grid;
    grid-template-columns: 120px 1fr;
    text-align: left;
    gap: 16px;
  }
  .product__details {
    display: block;
  }
}

스타일 컨테이너 쿼리

컨테이너의 계산된 스타일 값을 쿼리할 수 있습니다.

/* Style container queries — query computed style values */

/* Define a container with a custom property */
.theme-container {
  container-type: normal; /* No size containment needed for style queries */
  --theme: light;
}

.theme-container.dark {
  --theme: dark;
}

/* Query the custom property value */
@container style(--theme: dark) {
  .card {
    background: #1a1a2e;
    color: #e0e0e0;
    border-color: #333;
  }
  .button {
    background: #4a90d9;
    color: white;
  }
}

@container style(--theme: light) {
  .card {
    background: #ffffff;
    color: #333;
    border-color: #e0e0e0;
  }
}

/* Practical use: component variants via CSS properties */
.alert-container {
  --variant: info;
}

@container style(--variant: error) {
  .alert {
    background: #fef2f2;
    border-left: 4px solid #ef4444;
    color: #991b1b;
  }
}

@container style(--variant: success) {
  .alert {
    background: #f0fdf4;
    border-left: 4px solid #22c55e;
    color: #166534;
  }
}

중첩된 컨테이너 쿼리

여러 조상 컨테이너에 기반한 복잡한 반응형 동작.

/* Nested container queries */
.page {
  container: page / inline-size;
}

.sidebar {
  container: sidebar / inline-size;
}

/* Different behavior based on multiple container sizes */
@container page (min-width: 1200px) {
  .sidebar {
    width: 350px;
  }
}

@container sidebar (max-width: 250px) {
  .sidebar-widget {
    font-size: 0.875rem;
    padding: 8px;
  }
  .sidebar-widget__title {
    font-size: 1rem;
  }
}

@container sidebar (min-width: 300px) {
  .sidebar-widget {
    padding: 16px;
  }
  .sidebar-widget__chart {
    display: block;
  }
}

디자인 패턴

고유 반응형 디자인

CSS Grid auto-fit과 결합합니다.

/* Intrinsic design: Grid + Container Queries */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

.auto-grid > * {
  container-type: inline-size;
}

/* Items automatically adapt as the grid reflows */
/* No breakpoints needed — the grid column width drives the layout */
@container (min-width: 400px) {
  .feature-card {
    grid-template-columns: auto 1fr;
  }
}

@container (min-width: 500px) {
  .feature-card__description {
    font-size: 1.125rem;
  }
}

대시보드 위젯 패턴

그리드 셀에 적응하는 위젯 패턴.

/* Dashboard widget pattern */
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
}

.dashboard__cell {
  container: widget / inline-size;
}

.widget {
  padding: 16px;
  border-radius: 12px;
  background: var(--widget-bg);
}

.widget__chart {
  height: 150px;
}

.widget__table {
  display: none; /* Hidden in small widgets */
}

@container widget (min-width: 400px) {
  .widget__chart {
    height: 200px;
  }
  .widget__stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }
}

@container widget (min-width: 600px) {
  .widget {
    padding: 24px;
  }
  .widget__table {
    display: table;
  }
  .widget__chart {
    height: 280px;
  }
}

브라우저 지원

2026년 기준 우수한 브라우저 지원.

폴백 전략

@supports로 우아한 폴백을 제공.

/* Graceful fallback with @supports */
.card-container {
  /* Fallback: use media queries */
}

@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
  }
}

/* Fallback styles using media queries */
@media (min-width: 768px) {
  .card {
    grid-template-columns: 200px 1fr;
  }
}

/* Container query override (when supported) */
@container (min-width: 400px) {
  .card {
    grid-template-columns: 200px 1fr;
  }
}

성능 고려사항

컨테인먼트 이해가 중요합니다.

  • 새로운 스태킹 컨텍스트를 생성.
  • 불필요한 중첩을 피함.
  • 너비만 필요하면 inline-size 사용.
  • 추가 레이아웃 패스 없음.
  • 명명된 컨테이너로 제어.

모범 사례

  1. 컴포넌트에 컨테이너 쿼리, 페이지에 미디어 쿼리.
  2. inline-size부터 시작.
  3. 컨테이너에 이름 지정.
  4. 컨테이너 쿼리 단위 사용.
  5. Grid와 Flexbox와 결합.
  6. 모바일 퍼스트로 설계.
  7. @supports로 폴백.

미디어 쿼리에서 마이그레이션

모든 미디어 쿼리를 대체할 필요는 없습니다.

/* Decision Guide: Container Query vs Media Query */

/* USE CONTAINER QUERIES when:
   ✅ Component is reused in different layout contexts
   ✅ Component goes in sidebars, modals, grids, etc.
   ✅ You need component-level responsive behavior
   ✅ The component's layout depends on available space

   USE MEDIA QUERIES when:
   ✅ Changing the overall page layout
   ✅ Showing/hiding entire sections
   ✅ Adjusting global typography or spacing
   ✅ Changing navigation from desktop to mobile
   ✅ Print stylesheets

   USE BOTH when:
   ✅ Page layout switches at viewport breakpoints (media query)
   ✅ Components within the layout adapt to their containers (container query)
*/

결론

CSS 컨테이너 쿼리는 반응형 디자인에 대한 사고 방식을 근본적으로 변경합니다. 현대 CSS 레이아웃 기법과 결합하여 더 모듈화되고 유지보수 가능한 디자인 시스템을 구축하세요.

FAQ

컨테이너 쿼리와 미디어 쿼리의 차이는?

미디어 쿼리는 뷰포트, 컨테이너 쿼리는 부모 컨테이너에 응답합니다.

모든 부모 요소에 container-type이 필요한가요?

아닙니다. 쿼리 컨테이너로 사용할 요소에만 설정합니다.

Grid나 Flexbox와 함께 사용할 수 있나요?

네, 완벽하게 작동합니다.

inline-size와 size의 차이는?

inline-size는 인라인 축만, size는 두 축 모두에 컨테인먼트를 생성합니다.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

{ }CSS Minifier / Beautifier🌈CSS Gradient Generator

Related Articles

CSS Flexbox 완전 가이드: 모든 속성과 레이아웃 패턴 설명

CSS Flexbox 마스터: 모든 컨테이너/아이템 속성, 시각적 예제, 내비바/카드 그리드/홀리 그레일 등 실전 레이아웃 패턴.

CSS Grid 레이아웃: 실전 예제 포함 완전 튜토리얼

CSS Grid를 처음부터 배우는 완전 튜토리얼. grid-template, repeat(), minmax(), auto-fit, 이름 영역, 정렬, subgrid, 반응형 패턴.

2025년 CSS 미디어 쿼리와 브레이크포인트

2025년 최신 CSS 미디어 쿼리 패턴과 브레이크포인트. 컨테이너 쿼리, 선호 쿼리, 범위 구문, 반응형 디자인 모범 사례.