CSS Container Queries を使い始めたら、コンポーネントのレスポンシブ対応がかなり楽になった話

CSS の @container ルールを使い始めたところ、ビューポートではなくコンテナのサイズに基づいてスタイルを変えられるようになり、コンポーネント単位のレスポンシブ対応がかなりシンプルになりました。

🙌 結論から

CSS の @container ルール(Container Queries)を使うと、ページのビューポートサイズではなく、親コンテナのサイズに応じてスタイルを変えられます!

「同じコンポーネントをサイドバーとメインカラムに置いたときに別々のレイアウトにしたい」という場面で、Media Queries では対応が難しかったのですが、Container Queries でかなりすっきり書けるようになりました(^_^)

2024年時点で主要ブラウザすべてが対応済みなので、新規プロジェクトなら積極的に使っていける機能です。

💡 Container Queries とは

従来の Media Queries は、ウィンドウ(ビューポート)の幅や高さを条件にスタイルを変えるものでした。

Container Queries は親要素のサイズを条件にスタイルを変えられるものです。

基本的な書き方はこうなります。

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

/* コンテナが 480px 以上のときのスタイル */
@container card (min-width: 480px) {
  .card {
    display: flex;
    flex-direction: row;
    gap: 1rem;
  }
}

container-type: inline-size を指定することで、その要素がコンテナになります(・∀・)

👀 Media Queries との違い

Media Queries でレスポンシブ対応をしていると、「同じカードコンポーネントをサイドバーにもメインカラムにも置きたい」というときに困っていました。

サイドバーに置かれたカードは幅が狭いので縦並びにしたい、メインカラムのカードは横並びにしたい…という場合、ビューポート幅だけで判断すると「サイドバーにあるのかメインカラムにあるのか」を CSS だけでは判別できません(´・ω・`)

なのでこれまでは

  • 配置場所ごとにクラスを付け替える
  • JavaScript で親要素の幅を測って切り替える

といった方法を取ることが多かったのですが、これが毎回かなり面倒で……

Container Queries を使うと、コンポーネントは「自分を包むコンテナがどのくらいの幅か」だけを見ればよくなります!

✨ 実際に書いてみた

カードコンポーネントで試した例を紹介します。

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

/* デフォルト(縦並び) */
.card {
  display: grid;
  gap: 0.75rem;
}

/* コンテナが 480px 以上なら横並び */
@container (min-width: 480px) {
  .card {
    grid-template-columns: 160px 1fr;
    align-items: center;
  }
}

このカードコンポーネントをサイドバー(幅 240px)とメインカラム(幅 640px)の両方に置くと、サイドバーでは縦並び、メインカラムでは横並びに自動で切り替わります!

コンポーネント側では何もしていない。コンテナのサイズだけで切り替わる。これがかなり便利でした(^o^)/

ちなみに container-name を省略した @container (min-width: ...) は最も近い祖先のコンテナが対象になるので、シンプルな構成であれば名前は省略して書けます。

また、container-type には以下の値が使えます。

  • inline-size:横幅のみ対象
  • size:横幅・縦幅両方が対象
  • normal:コンテナにしない(デフォルト)

多くの場面では inline-size で十分です。

📐 コンテナクエリ単位とスタイルクエリも使ってみた

@container に慣れてきたところで、コンテナクエリ単位という便利な仕組みも使うようになりました。

vwvh がビューポート基準の単位であるように、コンテナには cqw(幅)・cqh(高さ)・cqi(インライン方向)・cqb(ブロック方向)という専用の単位があります。

.card-title {
  /* コンテナ幅の5%をフォントサイズにする */
  font-size: clamp(1rem, 5cqi, 2rem);
}

これを使うと、コンテナのサイズに応じてフォントサイズや余白が滑らかに変化する、いわゆる「流体タイポグラフィ」がブレイクポイントなしで実現できます(・∀・)

ちなみに cqw / cqh より cqi / cqb を使う方が無難だと知りました。

cqw は常に「物理的な横幅」を指しますが、cqi(インラインサイズ)は書字方向を意識してくれる単位なので、縦書きレイアウトなどでも意図通りに動いてくれます。

もうひとつ最近使い始めたのが スタイルクエリ@container style())です。

これはコンテナのサイズではなく、CSSカスタムプロパティの値を条件にスタイルを切り替えられる機能です。

.theme-container {
  --variant: compact;
}

@container style(--variant: compact) {
  .card {
    padding: 0.5rem;
  }
}

「このコンポーネントは --variant: compact のときだけ余白を詰める」というように、JavaScriptで状態を切り替える代わりにCSS変数だけで見た目を変える運用ができるようになって、コンポーネントの状態管理がかなりシンプルになりました!

サイズだけでなく「状態」でもスタイルを分岐できるようになったことで、Container Queriesの使い道がさらに広がった感覚があります。

パパ系Webエンジニア〜愛用ガジェット一覧 🛍️

この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌

👍 まとめ

Container Queries のポイントをまとめると:

  • container-type を指定した要素がコンテナになる
  • @container で、そのコンテナのサイズに応じたスタイルを書ける
  • コンポーネント単位でレスポンシブ対応できるので、配置場所を気にしなくてよくなる

Media Queries ではビューポート全体のサイズしか見られなかった部分が、Container Queries ではコンポーネントが置かれた場所のサイズで自動的に変わるようになります。

「同じコンポーネントをいろんな場所に置きたい」というニーズにとても合っているので、新規プロジェクトではぜひ積極的に使ってみてください(。•̀ᴗ-)✧

※ ブラウザ対応状況や仕様は今後も更新される可能性があります。最新情報は MDN をご確認ください👀