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 メディアクエリ
メディアクエリはビューポートサイズにのみ応答します。コンテナクエリはこの制限を解決します。
| Feature | Media Queries | Container Queries |
|---|---|---|
| Responds to | Viewport size | Container size |
| Scope | Global (page-level) | Local (component-level) |
| Reusability | Breaks in different contexts | Works in any context |
| Use case | Page layout | Component layout |
| Units | vw, vh, vmin, vmax | cqw, cqh, cqi, cqb |
| Browser support | Universal | All 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 を1つの宣言にまとめます。
/* 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 単位が導入されました。
| Unit | Description | Equivalent |
|---|---|---|
| cqw | 1% of container width | Similar to vw for viewport |
| cqh | 1% of container height | Similar to vh for viewport |
| cqi | 1% of container inline size | Width in horizontal writing |
| cqb | 1% of container block size | Height in horizontal writing |
| cqmin | Smaller of cqi and cqb | Similar to vmin |
| cqmax | Larger of cqi and cqb | Similar 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 を使用。
- 追加のレイアウトパスは発生しない。
- 名前付きコンテナで制御。
ベストプラクティス
- コンポーネントにコンテナクエリ、ページにメディアクエリ。
- inline-size から始める。
- コンテナに名前を付ける。
- コンテナクエリ単位を使用。
- Grid と Flexbox と組み合わせる。
- モバイルファーストで設計。
- @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 は両軸にコンテインメントを作成します。