はじめに

従来のレスポンシブデザインでは、メディアクエリを使ってビューポート(画面サイズ)に応じたスタイルを適用するのが一般的でした。しかし、この方法にはコンポーネント設計において大きな課題がありました。コンポーネントが配置される場所によって、同じビューポートでも親要素の幅が異なるケースに対応できないのです。

CSSコンテナクエリはこの課題を解決する技術です。親要素(コンテナ)のサイズに応じてスタイルを適用できるため、コンポーネントが「自分がどこに配置されているか」を認識して、適切なレイアウトに切り替わります。

本記事では、コンテナクエリについて以下の内容を解説します。

  • コンテナクエリとは何か
  • container-typecontainer-nameプロパティの使い方
  • @containerルールの構文と書き方
  • メディアクエリとの違いと使い分け
  • コンポーネントベース設計との相性

前提条件

本記事を読み進めるにあたり、以下の知識があることを前提としています。

  • CSSの基本構文(セレクタ、プロパティ、値)
  • メディアクエリの基本的な書き方
  • FlexboxまたはCSS Gridの基礎知識

動作確認環境

  • Google Chrome 105以降
  • Firefox 110以降
  • Safari 16以降
  • Microsoft Edge 105以降

コンテナクエリとは

コンテナクエリ(Container Queries)は、特定の親要素のサイズに基づいてスタイルを適用するCSS機能です。2023年に主要ブラウザで正式サポートされ、モダンCSSの重要な機能として定着しています。

メディアクエリとの本質的な違い

メディアクエリとコンテナクエリの違いを視覚的に理解しましょう。

flowchart TB
    subgraph media["メディアクエリ"]
        direction TB
        V["ビューポート(画面全体)"]
        V --> C1["コンポーネントA"]
        V --> C2["コンポーネントB"]
        V --> C3["コンポーネントC"]
    end
    
    subgraph container["コンテナクエリ"]
        direction TB
        P1["親要素1"] --> CC1["コンポーネントA"]
        P2["親要素2"] --> CC2["コンポーネントB"]
        P3["親要素3"] --> CC3["コンポーネントC"]
    end
比較項目 メディアクエリ コンテナクエリ
参照する対象 ビューポート(画面サイズ) 親要素(コンテナ)
スコープ グローバル ローカル(コンポーネント単位)
再利用性 配置場所に依存 配置場所に依存しない
使用例 ページ全体のレイアウト変更 コンポーネントの内部レイアウト変更

なぜコンテナクエリが必要なのか

具体的なシナリオで考えてみましょう。カードコンポーネントを作成し、以下の3箇所に配置するケースを想定します。

  1. メインコンテンツエリア(幅広い)
  2. サイドバー(幅狭い)
  3. フッター(中程度の幅)
flowchart LR
    subgraph page["ページレイアウト"]
        direction TB
        subgraph main["メインエリア(800px)"]
            Card1["カード:横並びレイアウト"]
        end
        subgraph sidebar["サイドバー(300px)"]
            Card2["カード:縦積みレイアウト"]
        end
        subgraph footer["フッター(500px)"]
            Card3["カード:コンパクトレイアウト"]
        end
    end

メディアクエリでは、画面幅が1200pxの場合、すべてのカードに同じスタイルが適用されます。しかしコンテナクエリを使えば、各カードが「自分の親要素の幅」に応じて最適なレイアウトを選択できます。

container-typeプロパティ

コンテナクエリを使用するには、まず親要素をコンテナとして定義する必要があります。container-typeプロパティを使用して、要素をクエリコンテナに指定します。

基本構文

1
2
3
.container {
  container-type: inline-size;
}

指定可能な値

説明
normal コンテナサイズクエリの対象外(デフォルト値)
inline-size インライン方向(横書きでは幅)のサイズクエリが可能
size インライン方向とブロック方向の両方のサイズクエリが可能

実務ではinline-sizeを使用するケースがほとんどです。sizeを指定すると、要素は高さ方向にもコンテインメント(封じ込め)が適用されるため、子要素による高さの自動調整ができなくなります。

使用例

1
2
3
4
5
6
<div class="post">
  <div class="card">
    <h2>カードタイトル</h2>
    <p>カードの内容がここに入ります。</p>
  </div>
</div>
1
2
3
.post {
  container-type: inline-size;
}

この設定により、.post要素がクエリコンテナとなり、その子孫要素に対してコンテナクエリを適用できるようになります。

container-nameプロパティ

複数のコンテナが存在する場合、特定のコンテナを識別するために名前を付けることができます。

基本構文

1
2
3
4
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

名前付きコンテナのメリット

名前を付けることで、以下のメリットが得られます。

  1. 明確な対象指定:どのコンテナを参照しているか明確になる
  2. 祖先コンテナの指定:直近の親だけでなく、特定の祖先コンテナを対象にできる
  3. コードの可読性向上:意図が明確になり、メンテナンスしやすくなる

複数の名前を付ける

1つのコンテナに複数の名前を付けることも可能です。

1
2
3
4
.wrapper {
  container-type: inline-size;
  container-name: card-container layout;
}

containerショートハンドプロパティ

container-typecontainer-nameは、containerプロパティでまとめて指定できます。

構文

1
2
3
.element {
  container: <container-name> / <container-type>;
}

使用例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 個別指定 */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

/* ショートハンド */
.sidebar {
  container: sidebar / inline-size;
}

ショートハンドを使用することで、コードがより簡潔になります。

@containerルールの書き方

コンテナを定義したら、@containerルールを使って条件付きスタイルを記述します。

基本構文

1
2
3
@container (条件) {
  /* 条件に合致した場合に適用されるスタイル */
}

幅による条件指定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* コンテナの幅が400px以上の場合 */
@container (width >= 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
}

/* コンテナの幅が400px未満の場合 */
@container (width < 400px) {
  .card {
    display: block;
  }
}

名前付きコンテナへのクエリ

特定のコンテナを対象にする場合は、名前を指定します。

1
2
3
4
5
@container sidebar (width > 300px) {
  .card {
    font-size: 1.2em;
  }
}

複数条件の組み合わせ

論理演算子を使って複数の条件を組み合わせられます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* AND条件 */
@container (width > 400px) and (width < 800px) {
  .card {
    /* 400px〜800pxの範囲で適用 */
  }
}

/* OR条件 */
@container (width < 300px) or (width > 900px) {
  .card {
    /* 300px未満、または900px超で適用 */
  }
}

/* NOT条件 */
@container not (width < 400px) {
  .card {
    /* 400px以上で適用 */
  }
}

範囲構文

メディアクエリと同様に、範囲構文も使用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 従来の書き方 */
@container (min-width: 400px) {
  /* ... */
}

/* 範囲構文(推奨) */
@container (width >= 400px) {
  /* ... */
}

/* 範囲指定 */
@container (400px <= width <= 800px) {
  /* ... */
}

実践的な使用例

実際のコンポーネント設計でコンテナクエリを活用する例を見ていきましょう。

レスポンシブカードコンポーネント

配置場所に応じてレイアウトが変わるカードコンポーネントを実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<article class="card-container">
  <div class="card">
    <img src="thumbnail.jpg" alt="サムネイル" class="card-image">
    <div class="card-content">
      <h3 class="card-title">記事タイトル</h3>
      <p class="card-description">記事の説明文がここに入ります。</p>
      <time class="card-date">2026年1月5日</time>
    </div>
  </div>
</article>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* コンテナの定義 */
.card-container {
  container: card / inline-size;
}

/* 基本スタイル(狭い幅向け:縦積み) */
.card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card-content {
  padding: 1rem;
}

.card-title {
  font-size: 1.1rem;
  margin: 0 0 0.5rem;
}

.card-description {
  font-size: 0.9rem;
  color: #666;
  margin: 0 0 0.5rem;
}

/* 中程度の幅:横並びレイアウト */
@container card (width >= 400px) {
  .card {
    flex-direction: row;
  }
  
  .card-image {
    width: 200px;
    height: auto;
    min-height: 150px;
  }
  
  .card-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* 広い幅:強調レイアウト */
@container card (width >= 600px) {
  .card-title {
    font-size: 1.5rem;
  }
  
  .card-description {
    font-size: 1rem;
  }
  
  .card-image {
    width: 280px;
  }
}

ナビゲーションメニュー

コンテナの幅に応じてメニューの表示形式を切り替えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<header class="header">
  <nav class="nav-container">
    <ul class="nav-list">
      <li><a href="/">ホーム</a></li>
      <li><a href="/about">概要</a></li>
      <li><a href="/services">サービス</a></li>
      <li><a href="/contact">お問い合わせ</a></li>
    </ul>
  </nav>
</header>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
.nav-container {
  container: nav / inline-size;
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.nav-list a {
  display: block;
  padding: 0.75rem 1rem;
  text-decoration: none;
  color: #333;
  background: #f5f5f5;
  border-radius: 4px;
}

/* 十分な幅がある場合は横並び */
@container nav (width >= 500px) {
  .nav-list {
    flex-direction: row;
    justify-content: center;
  }
  
  .nav-list a {
    background: transparent;
  }
  
  .nav-list a:hover {
    background: #e0e0e0;
  }
}

コンテナクエリの長さ単位

コンテナクエリでは、コンテナのサイズに相対的な長さ単位を使用できます。

単位 説明
cqw コンテナの幅の1%
cqh コンテナの高さの1%
cqi コンテナのインラインサイズの1%
cqb コンテナのブロックサイズの1%
cqmin cqicqbのうち小さい方
cqmax cqicqbのうち大きい方

使用例

1
2
3
4
5
6
7
8
9
.card-title {
  /* コンテナ幅に応じてフォントサイズを調整 */
  font-size: clamp(1rem, 5cqi, 2rem);
}

.card-image {
  /* コンテナ幅の40%を画像幅に */
  width: 40cqi;
}

これらの単位を使うことで、よりきめ細かいレスポンシブ調整が可能になります。

メディアクエリとの使い分け

コンテナクエリとメディアクエリは競合する技術ではなく、それぞれ適した用途があります。

メディアクエリを使うべきケース

  • ページ全体のレイアウト構造を変更する場合
  • グローバルなスタイル(フォントサイズ、余白など)を調整する場合
  • デバイスの特性(解像度、向き、ホバー対応など)を判定する場合
1
2
3
4
5
6
7
8
/* ページレイアウトの切り替え */
@media (width >= 1024px) {
  .page-layout {
    display: grid;
    grid-template-columns: 1fr 300px;
    gap: 2rem;
  }
}

コンテナクエリを使うべきケース

  • 再利用可能なコンポーネントの内部レイアウトを調整する場合
  • コンポーネントが配置される場所に依存しないスタイルを実装する場合
  • デザインシステムやUIライブラリを構築する場合
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* コンポーネント内部のレイアウト切り替え */
.card-container {
  container: card / inline-size;
}

@container card (width >= 400px) {
  .card {
    flex-direction: row;
  }
}

組み合わせて使う

実際のプロジェクトでは、両者を組み合わせて使用するのが効果的です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* ページ全体のレイアウトはメディアクエリで */
@media (width >= 768px) {
  .main-content {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
}

/* コンポーネントはコンテナクエリで */
.article-card-wrapper {
  container: article-card / inline-size;
}

@container article-card (width >= 350px) {
  .article-card {
    flex-direction: row;
  }
}

コンポーネントベース設計との相性

コンテナクエリは、ReactやVue.jsなどのコンポーネントベースフレームワークと非常に相性が良い技術です。

コンポーネントの独立性向上

コンテナクエリを使用することで、コンポーネントは自己完結的になります。

flowchart TB
    subgraph before["従来のアプローチ"]
        direction TB
        Page1["ページA"]
        Page2["ページB"]
        Card1["カードコンポーネント"]
        Page1 -.->|".page-a .card { ... }"| Card1
        Page2 -.->|".page-b .card { ... }"| Card1
    end
    
    subgraph after["コンテナクエリ"]
        direction TB
        Page3["ページA"]
        Page4["ページB"]
        Card2["カードコンポーネント<br/>(自己完結)"]
        Page3 --> Card2
        Page4 --> Card2
    end

従来は親要素のクラス名に依存したスタイル(.sidebar .cardなど)を書く必要がありましたが、コンテナクエリでは不要です。

デザインシステムへの適用

デザインシステムでコンテナクエリを活用する際の設計パターンを紹介します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* デザイントークンとしてコンテナ定義 */
[data-container="card"] {
  container: card / inline-size;
}

[data-container="nav"] {
  container: nav / inline-size;
}

[data-container="hero"] {
  container: hero / inline-size;
}

/* コンポーネントスタイル */
.ds-card {
  /* 基本スタイル */
}

@container card (width >= 400px) {
  .ds-card {
    /* 拡張スタイル */
  }
}
1
2
3
4
5
<div data-container="card">
  <div class="ds-card">
    <!-- カードの内容 -->
  </div>
</div>

この設計により、コンポーネントを配置する際にdata属性を付与するだけで、適切なレスポンシブ動作が得られます。

ブラウザサポートと注意点

ブラウザサポート状況

コンテナクエリは2023年2月にすべての主要ブラウザでサポートされ、現在はBaseline Widely Availableの状態です。

ブラウザ サポートバージョン
Chrome 105以降
Edge 105以降
Firefox 110以降
Safari 16以降

フォールバック戦略

古いブラウザのサポートが必要な場合は、@supportsを使ったフォールバックを実装できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* 基本スタイル(すべてのブラウザ向け) */
.card {
  display: block;
}

/* メディアクエリによるフォールバック */
@media (width >= 600px) {
  .card {
    display: flex;
  }
}

/* コンテナクエリ対応ブラウザ向け */
@supports (container-type: inline-size) {
  .card-wrapper {
    container: card / inline-size;
  }
  
  /* メディアクエリを上書き */
  @media (width >= 600px) {
    .card {
      display: block; /* リセット */
    }
  }
  
  @container card (width >= 400px) {
    .card {
      display: flex;
    }
  }
}

パフォーマンスに関する考慮

コンテナクエリはcontainer-typeを指定した要素にCSS Containmentを適用します。これにより、ブラウザは再レンダリングの範囲を限定でき、パフォーマンス上のメリットがあります。

ただし、以下の点に注意が必要です。

  • container-type: sizeを使用すると、高さの自動計算ができなくなる
  • 過度に深いネストは避ける
  • 必要な箇所にのみコンテナを定義する

おわりに

コンテナクエリは、コンポーネントベースの現代的なWeb開発において不可欠な技術です。メディアクエリがページ全体のレイアウトを制御するのに対し、コンテナクエリはコンポーネント単位でのレスポンシブ対応を可能にします。

本記事で解説した内容をまとめると、以下のようになります。

  • container-typeプロパティで要素をクエリコンテナに指定する
  • container-nameで名前を付けて特定のコンテナを識別する
  • @containerルールで条件に応じたスタイルを適用する
  • メディアクエリとコンテナクエリは用途に応じて使い分ける
  • コンポーネントベース設計と組み合わせることで、再利用性の高いUIを構築できる

コンテナクエリを活用することで、配置場所に依存しない、真にポータブルなコンポーネントを実装できます。デザインシステムの構築やUIライブラリの開発において、ぜひ活用してください。

参考リンク