📦

メディアクエリの限界を超える!CSS Container Queries完全ガイド

に公開

はじめに:メディアクエリの限界

レスポンシブデザインといえば、長年メディアクエリが主役でした。

しかし、メディアクエリには致命的な欠点があります。

/* メディアクエリは viewport の幅でしか判断できない */
@media (min-width: 768px) {
  .card {
    display: flex; /* タブレット以上は横並び */
  }
}

この何が問題なのでしょうか?

問題1:コンポーネントの配置場所に依存してしまう

例えば、以下のようなカードコンポーネントがあるとします:

<!-- メインエリア(広い)に配置 -->
<main class="main-area">
  <div class="card">...</div>
</main>

<!-- サイドバー(狭い)に配置 -->
<aside class="sidebar">
  <div class="card">...</div>
</aside>

メディアクエリの場合:

  • viewport が768px以上なら、どちらのカードも横並びレイアウトになる
  • サイドバーのカードは狭いのに、無理やり横並びになってレイアウトが崩れる

本当にやりたいこと:

  • カードの親要素の幅に応じてレイアウトを変えたい
  • メインエリアのカードは横並び
  • サイドバーのカードは縦並び(親が狭いから)

これがメディアクエリの限界です。

問題2:コンポーネントの再利用性が低い

/* このカードはメインエリア専用になってしまう */
.main-area .card {
  display: flex;
}

/* サイドバー用に別のスタイルが必要... */
.sidebar .card {
  display: block;
}

配置場所ごとにスタイルを書く必要があり、コンポーネント指向の設計ができません


CSS Container Queries が解決する

Container Queries を使えば、親要素の幅に応じてスタイルを変えられます。

/* 親要素をコンテナとして定義 */
.card-container {
  container-type: inline-size;
}

/* 親の幅が400px以上なら横並び */
@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

これで:

  • メインエリアのカードは横並び(親が広いから)
  • サイドバーのカードは縦並び(親が狭いから)
  • 同じCSSで、配置場所に関係なく適切なレイアウトになる

基本的な使い方

ステップ1:親要素をコンテナとして定義

.container {
  container-type: inline-size; /* この要素をコンテナにする */
}

ステップ2:@containerルールを書く

@container (min-width: 400px) {
  /* コンテナの幅が400px以上の時のスタイル */
  .card {
    display: flex;
  }
}

完全な例

<div class="card-container">
  <div class="card">
    <img src="image.jpg" alt="">
    <div class="content">
      <h2>タイトル</h2>
      <p>説明文...</p>
    </div>
  </div>
</div>
/* 親をコンテナとして定義 */
.card-container {
  container-type: inline-size;
}

/* デフォルト:縦並び */
.card {
  display: block;
}

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

/* コンテナの幅が400px以上:横並び */
@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
  
  .card img {
    width: 200px;
    height: auto;
  }
}

container-type の種類と使い分け

container-type には3つの値があります:

1. inline-size(⭐ 最もよく使う)

.container {
  container-type: inline-size;
}
  • インライン方向(通常は幅)のみをコンテナサイズとして扱う
  • 高さは無視される
  • パフォーマンスが良い(高さの再計算が不要)
  • 99%のケースでこれを使えばOK

2. size

.container {
  container-type: size;
}
  • 幅と高さの両方をコンテナサイズとして扱う
  • 高さにも依存したレイアウトが必要な場合
  • パフォーマンスに注意(レイアウトシフトのリスク)

3. normal(デフォルト)

.container {
  container-type: normal;
}
  • コンテナクエリを使わない(通常の要素)
  • 明示的に書く必要はない

container-name でターゲット指定

複数のコンテナが入れ子になっている場合、どのコンテナに対してクエリを書くか指定できます。

/* 名前付きコンテナ */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

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

/* sidebarコンテナに対するクエリ */
@container sidebar (min-width: 300px) {
  .widget {
    padding: 1rem;
  }
}

/* mainコンテナに対するクエリ */
@container main (min-width: 800px) {
  .article {
    columns: 2;
  }
}

省略記法:

.sidebar {
  /* container-type と container-name を同時に指定 */
  container: sidebar / inline-size;
}

実際に動くデモを見てみよう

まずは実際に動くデモで、Container Queriesの威力を体感してください!

デモ1:カードコンポーネント(メイン+サイドバー)

👆 ブラウザの幅を変えて、メインエリアとサイドバーのカードの挙動を比較してください

  • メインエリア(広い):親が広いので横並びレイアウト
  • サイドバー(狭い):親が狭いので縦並びレイアウト
  • 同じHTML、同じCSSなのに自動的にレイアウトが変わります

デモ2:Container Query Units(cqw)

👆 コンテナの幅に応じて、フォントサイズが自動的にスケールします

  • cqw(Container Query Width)を使用
  • メディアクエリ不要で、流動的なタイポグラフィを実現

実用パターン1:カードコンポーネント

最も一般的な使い方です。

<!-- メインエリア(広い)-->
<main class="main-area">
  <div class="card-container">
    <div class="card">
      <img src="image.jpg" alt="">
      <div class="content">
        <h2>記事タイトル</h2>
        <p>説明文が入ります...</p>
      </div>
    </div>
  </div>
</main>

<!-- サイドバー(狭い)-->
<aside class="sidebar">
  <div class="card-container">
    <div class="card">
      <img src="image.jpg" alt="">
      <div class="content">
        <h2>記事タイトル</h2>
        <p>説明文が入ります...</p>
      </div>
    </div>
  </div>
</aside>
.card-container {
  container-type: inline-size;
}

/* デフォルト:縦並び(狭い時) */
.card {
  display: block;
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

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

.card .content {
  padding: 1rem;
}

/* コンテナの幅が400px以上:横並び */
@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
  
  .card img {
    width: 200px;
    height: 150px;
    flex-shrink: 0;
  }
}

/* コンテナの幅が600px以上:さらに調整 */
@container (min-width: 600px) {
  .card img {
    width: 250px;
    height: 180px;
  }
  
  .card .content {
    padding: 1.5rem;
  }
  
  .card h2 {
    font-size: 1.5rem;
  }
}

ポイント:

  • 同じHTML、同じCSSで、配置場所によって自動的にレイアウトが変わる
  • メインエリアでは横並び、サイドバーでは縦並びになる

実用パターン2:グリッドの列数に応じた調整

<div class="grid-container">
  <div class="product-card">...</div>
  <div class="product-card">...</div>
  <div class="product-card">...</div>
</div>
.grid-container {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
  /* 自動的に列数が変わる */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.product-card {
  container-type: inline-size;
}

/* カードの幅が150px未満:テキストを省略 */
@container (max-width: 149px) {
  .product-card .title {
    font-size: 0.875rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  
  .product-card .description {
    display: none; /* 説明文を非表示 */
  }
}

/* カードの幅が150px以上:通常表示 */
@container (min-width: 150px) {
  .product-card .title {
    font-size: 1rem;
  }
  
  .product-card .description {
    display: block;
  }
}

Container Query Units(コンテナ単位)

Container Queries では、コンテナのサイズを基準とした単位が使えます。

単位 意味
cqw コンテナの幅の1%
cqh コンテナの高さの1%
cqi コンテナのインライン方向の1%
cqb コンテナのブロック方向の1%
cqmin cqicqb の小さい方
cqmax cqicqb の大きい方

viewport単位との違い:

/* viewport基準 */
.title {
  font-size: 5vw; /* viewport の幅の5% */
}

/* コンテナ基準 */
.card-container {
  container-type: inline-size;
}

.card .title {
  font-size: 5cqw; /* コンテナの幅の5% */
}

実用例:

.card-container {
  container-type: inline-size;
}

/* コンテナの幅に応じてフォントサイズを自動調整 */
.card .title {
  font-size: clamp(1rem, 4cqw, 2rem);
}

.card .description {
  font-size: clamp(0.875rem, 2.5cqw, 1rem);
}

これで、どんな幅のコンテナに配置しても、自動的に適切なフォントサイズになります。


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

両方を組み合わせることで、最も柔軟なレスポンシブデザインが実現できます。

メディアクエリを使うべき場面

  • グローバルなレイアウト変更
    • ヘッダーのナビゲーション表示/非表示
    • サイドバーの表示/非表示
    • 全体のグリッド構造の変更
/* グローバルレイアウト */
@media (min-width: 768px) {
  .layout {
    display: grid;
    grid-template-columns: 250px 1fr;
  }
}

Container Queriesを使うべき場面

  • コンポーネント単位の調整
    • カードの内部レイアウト
    • ボタンのサイズ調整
    • テキストの表示/非表示
/* コンポーネント内部 */
.card-container {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

併用パターン

/* グローバル:タブレット以上でサイドバー表示 */
@media (min-width: 768px) {
  .layout {
    display: grid;
    grid-template-columns: 250px 1fr;
  }
}

/* コンポーネント:親の幅に応じてカードを調整 */
.card-container {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

パフォーマンスへの影響

container-type: inline-size は安全

  • 高さの再計算が不要なので、パフォーマンスへの影響はほぼない
  • レイアウトシフトのリスクも低い

container-type: size は注意が必要

  • 高さも含めてコンテナサイズとして扱うため、レイアウトの再計算が発生しやすい
  • 特に、コンテンツの高さが動的に変わる場合は注意
  • 必要な場合のみ使う

ベストプラクティス

/* ✅ 推奨:inline-size を使う */
.container {
  container-type: inline-size;
}

/* ⚠️ 注意:size は必要な場合のみ */
.special-container {
  container-type: size;
  height: 100vh; /* 高さを固定する */
}

ブラウザサポート状況

2023年2月以降、主要ブラウザでサポート開始

ブラウザ サポート開始
Chrome 105+(2022年8月)
Edge 105+(2022年9月)
Safari 16.0+(2022年9月)
Firefox 110+(2023年2月)

2025年現在:もう安心して使えます!

フォールバック戦略

古いブラウザのサポートが必要な場合:

/* フォールバック:メディアクエリ */
@media (min-width: 400px) {
  .card {
    display: flex;
  }
}

/* Container Queries対応ブラウザ */
@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
  }
  
  @container (min-width: 400px) {
    .card {
      display: flex;
    }
  }
}

まとめ

Container Queriesを使うべき理由

  1. コンポーネントの再利用性が劇的に向上

    • 配置場所に依存しないスタイル
    • 同じコンポーネントをどこにでも使える
  2. メディアクエリより柔軟

    • viewport ではなく親要素の幅で判断
    • より細かい制御が可能
  3. コンポーネント指向の設計に必須

    • React/Vue などのコンポーネントフレームワークと相性抜群
    • デザインシステムの構築が容易に
  4. もう安心して使える

    • 主要ブラウザで2年以上サポート
    • パフォーマンスも問題なし

今日から使えるチェックリスト

  • カードコンポーネントを @container で実装してみる
  • メディアクエリを Container Queries に置き換えられる箇所を探す
  • コンテナ単位(cqwなど)を使ってフォントサイズを調整してみる
  • デザインシステムに Container Queries を組み込む

Container Queries は、レスポンシブデザインの新しいスタンダードです。

ぜひ今日から使ってみてください!


参考リンク

Discussion