コンテナクエリ
同じカードコンポーネントが置き場所ごとに正しく折り返る。ビューポート依存だったレスポンシブを要素単位にし、真に再利用可能な部品を作る仕組みを原理から解説します。
- 1.コンテナクエリは祖先に container-type を宣言し、その要素の寸法を子孫が @container で参照する仕組み。判定基準がビューポートではなく直近の「クエリコンテナ」になる点がメディアクエリとの本質的な違い。
- 2.container-type: inline-size はサイズ封じ込め相当を伴い、そのコンテナ自身の内在サイズ計算から子孫を除外する。ゆえに高さ・幅を明示しないコンテナは0に潰れうる。
- 3.cqw/cqh/cqi/cqb などのコンテナクエリ単位は、直近のクエリコンテナの寸法に対する比率。vwと違い、同じCSSがどの祖先サイズに置かれても正しくスケールする。
メディアクエリで解けない問題
レスポンシブデザインの主力である @media は、判定基準が常にビューポートです。これは「ページ全体のレイアウト」を切り替えるには十分ですが、「1つのコンポーネントを、置かれた場所の幅に応じて切り替える」ことはできません。同じカードコンポーネントを、広いメインカラムに置くときは横並び、狭いサイドバーに置くときは縦積みにしたい——ビューポート幅は同じでも、置き場所の幅は違います。メディアクエリはこの違いを見分けられず、コンポーネント側で「自分がどこに置かれるか」を仮定した個別CSSを書く羽目になり、再利用性が落ちます。
コンテナクエリは、判定基準をビューポートから祖先要素(クエリコンテナ)の寸法へ移すことでこれを解きます。コンポーネントは「自分の直近の container が何pxか」だけを見て振る舞いを変えられるため、ページのどこに置かれても同じCSSで正しく折り返ります。
| 観点 | メディアクエリ | コンテナクエリ |
|---|---|---|
| 判定基準 | ビューポートの幅・高さ | 直近のクエリコンテナの寸法 |
| 適用範囲 | ページ全体・グローバル | そのコンテナ配下の部分木のみ |
| 再利用性 | 置き場所依存の個別CSSが必要 | コンポーネント単位で自己完結 |
| 単位 | vw/vh(ビューポート基準) | cqw/cqh/cqi/cqb(コンテナ基準) |
container-type と container-name
コンテナクエリを機能させるには、まず祖先要素を「クエリコンテナ」として宣言する必要があります。これを行うのが container-type です。
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* ショートハンド */
.card-wrapper {
container: card / inline-size;
}
container-type の値は3種類あります。inline-size はインライン方向(横書きなら幅)のサイズのみをクエリ対象にし、最も一般的です。size は幅と高さの両方を対象にしますが、後述のとおり高さのサイズ封じ込めを伴うため明示的な高さ指定が要ります。normal はコンテナクエリの基準にならない既定値です。
container-name は任意の識別名で、@container 側で「どの祖先を基準にするか」を名指しするために使います。名前を省略すると、子孫は名前を持たない最も近い祖先コンテナを暗黙に基準にします。複数階層でコンテナが入れ子になっている場合、名前指定が無いと「意図した外側のコンテナではなく、より近い内側のコンテナ」に引っかかることがあるため、部品が複雑になったら名前指定が安全です。
@container card (min-width: 400px) {
.card-title { font-size: 1.5rem; }
}
@container の中身は通常のCSSと同じ書き方で、条件に一致する間だけ内側の宣言が有効になります。min-width に加えて min-height や、論理プロパティの min-inline-size/min-block-size も使えます。
なぜサイズ封じ込めが要るのか
container-type: inline-size を指定した要素には、暗黙に contain: inline-size 相当のサイズ封じ込めが働きます。これは技術的な必然です。もしコンテナが子孫の内容量に応じて自分のサイズを決める(intrinsic sizing)なら、「子の見た目は親のサイズで決まり、親のサイズは子の内容で決まる」という循環参照が生まれ、ブラウザはレイアウトを収束させられません。この循環を断つため、コンテナクエリの元になる要素は「子を見ずに自分の幅を確定させる」契約を負わされます。
container-type: inline-size の場合、封じ込められるのはインライン方向(幅)だけなので、高さは通常どおり子の内容から決まります。しかし container-type: size は幅と高さの両方を封じ込めるため、明示的な高さを与えない限りコンテナは高さ0に潰れます。これは contain: size 単体を使うときと同じ落とし穴です。
container-type: size を使うときは、必ず height や contain-intrinsic-size で高さを明示してください。指定を忘れると、子孫の内容がどれだけあってもコンテナは高さ0として扱われ、中身が見えなくなります。実務では高さクエリが不要なら inline-size を選ぶのが安全です。
コンテナクエリ単位(cqw/cqh/cqi/cqb)
コンテナクエリには専用の長さの単位があり、vw/vh がビューポート基準であるのに対し、これらは直近のクエリコンテナ基準です。
| 単位 | 基準 | 対応するビューポート単位 |
|---|---|---|
| cqw | コンテナの幅の1% | vw |
| cqh | コンテナの高さの1% | vh |
| cqi | コンテナのインラインサイズの1% | vi |
| cqb | コンテナのブロックサイズの1% | vb |
| cqmin | cqiとcqbの小さい方 | vmin |
| cqmax | cqiとcqbの大きい方 | vmax |
たとえば見出しの文字サイズをコンテナの幅に比例させたい場合、vw では常にビューポート全体が基準になってしまい、狭いサイドバーに置いても画面全体の幅で計算された大きな文字になります。cqw(または論理方向を尊重する cqi)を使えば、どのコンテナに置かれても、そのコンテナ自身の幅に対する比率でスケールします。
.card-wrapper { container-type: inline-size; }
.card-title {
/* コンテナ幅の5% + 1remを最低保証。置き場所が変わっても比率は一定 */
font-size: clamp(1rem, 5cqw + 1rem, 2rem);
}
cqi/cqb は書字方向を考慮する論理単位で、writing-mode が縦書きに変わっても「インライン方向」「ブロック方向」の意味が保たれます。横書き前提で組む場合は cqw/cqh でも実用上差はありませんが、国際化を見据えるなら論理単位のほうが一貫します。
レイアウトの再利用性という核心
コンテナクエリが解決する本質的な問題は、コンポーネントの内部スタイルが、外部の配置文脈(ページのどこに置かれるか)から独立することです。CSSカスケードと詳細度がセレクタの優先順位を決める仕組みであるのに対し、コンテナクエリは「どの条件でスタイルを適用するか」の判定軸そのものを、グローバル(ビューポート)からローカル(祖先コンテナ)へ移す仕組みだと捉えると位置づけが明確になります。
.card { display: flex; flex-direction: column; }
@container (min-width: 480px) {
.card { flex-direction: row; }
}
このCSSを持つ .card は、メインカラム(幅が広い)に置けば横並びに、サイドバー(幅が狭い)に置けば縦積みになります。同じHTML・同じCSSファイルのまま、置き場所に応じて自動的に姿を変えるため、ページ側のレイアウトを変更してもコンポーネント側の修正は不要です。これがビューポート基準のメディアクエリでは原理的に到達できなかった再利用性です。
落とし穴:クエリコンテナ自身は条件を見られない
@container の条件は、そのコンテナ自身のスタイルには適用できません。コンテナの寸法を基準にした条件分岐は、あくまで子孫要素に対してのみ有効です。コンテナ自身の見た目を寸法に応じて変えたい場合は、別途外側にもう1段コンテナを用意するか、:has() などの構造セレクタと組み合わせる必要があります。
コンテナクエリには寸法だけでなく、祖先のカスタムプロパティ値を条件にする「コンテナスタイルクエリ」(@container style(...))もあります。テーマ切り替えのように「寸法ではなく状態」を子孫へ伝播させたい場合に使われますが、対応状況は寸法ベースのクエリより新しく、実務投入は対応ブラウザの確認が前提です。
頻出は、(1) メディアクエリがビューポート基準、コンテナクエリが祖先コンテナ基準という判定軸の違い、(2) container-type: inline-size が幅方向のサイズ封じ込めを暗黙に伴い、循環参照を避けるための必然であること、(3) container-type: size は高さも封じ込めるため高さ指定が無いと潰れる点、(4) cqw等のコンテナクエリ単位がvw等と違い直近コンテナ基準である点、の4点です。
まとめ
コンテナクエリは、レスポンシブの判定基準をビューポートから祖先要素の寸法へ移す仕組みです。container-type/container-name でクエリコンテナを宣言し、@container で子孫のスタイルを条件分岐します。inline-size は幅方向のサイズ封じ込めを伴い、これは子の内容とコンテナ自身のサイズが循環参照するのを防ぐための必然的な制約です。cqw/cqh/cqi/cqb はコンテナ基準の単位で、置き場所に依存しないスケーリングを可能にします。結果として、同じコンポーネントのCSSがページのどこに置かれても正しく折り返る、真の意味での再利用可能なレイアウトが実現します。周辺の仕組みはCSS Containmentとcontent-visibility、包含ブロックの決定規則は包含ブロックと位置指定も合わせて押さえてください。
Web/フロントエンド Article
コンテナクエリを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
CSS
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
container-type: inline-size はサイズ封じ込め相当を伴い、そのコンテナ自身の内在サイズ計算から子孫を除外する。ゆえに高さ・幅を明示しないコンテナは0に潰れうる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「CSS / レスポンシブ」に近いか確認する。
- 強みである「コンテナクエリは祖先に container-type を宣言し、その要素の寸法を子孫が @container で参照する仕組み。判定基準がビューポートではなく直近の「クエリコンテナ」になる点がメディアクエリとの本質的な違い。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。