この記事には広告を含む場合があります。
記事内で紹介する商品を購入することで、当サイトに売り上げの一部が還元されることがあります。
レスポンシブはメディアクエリで足りている。私はずっとそう思っていました。
コンテナクエリという新しい仕組みがあることは知っていました。でも解説を読んでも、自分のサイトで何が変わるのかピンと来ませんでした。困っていないものは、便利だと言われても腹落ちしないんですよね。
そこで実際に手を動かして、コンテナクエリを触ってみました。この記事は、その記録です。
まず何がうれしいのか、どんなときに使うのかを先に書きます。そのうえで、書き方とコピペ見本、つまずきポイントへ進みます。
コンテナクエリで何がうれしいのか
結論から書きます。コンテナクエリを使うと、部品が「置かれた場所の広さ」を見て、自分で形を変えてくれます。
同じカードを本文に置けば横並びになり、サイドバーに置けば縦積みになる。置く側は何も指定しません。カードが勝手に判断します。
下のプレビューで確かめてみてください。点線の枠の右下をドラッグすると、幅を変えられます(パソコンの場合)。ブラウザの画面幅はまったく変えていないのに、カードが形を変えます。
置いた場所に合わせて変わる
狭ければ縦積み、広ければ横並び。CSSの書き分けはしていません。
これまでは「サイドバーの中のカードはこう」「フッターの中のカードはこう」と、置き場所ごとにCSSを書き足していました。コンテナクエリなら、それが1組で済みます。
どんなときに使うといい?
先に言ってしまうと、全員が今すぐ使うべき技術ではありません。向き不向きがはっきりしています。
| 場面 | おすすめ |
|---|---|
| 受託・デザインカンプ通りに組む案件 | メディアクエリでOK |
| 1枚もののLP | メディアクエリでOK |
| WordPressテーマやブロックの配布 | コンテナクエリが効く |
| デザインシステム・共通パーツ | コンテナクエリが効く |
| 管理画面・ダッシュボード | コンテナクエリが効く |
分かれ目はひとつです。その部品が最終的にどこに置かれるか、書いた本人に予測できるかどうか。
カンプがあって置き場所が決まっているなら、メディアクエリで十分です。置き場所が読めない部品を作るときにこそ、コンテナクエリが効いてきます。
メリットとデメリット
実際に触ってみて感じた、良いところと引っかかるところをまとめます。
いま分からない言葉が出てきても、飛ばして大丈夫です。すべてこの記事の後半で説明します。
メリット
- 置き場所ごとの例外CSSが要らなくなる
部品ひとつにつき、CSSはひと組で済みます - 部品をそのまま移動できる
本文からサイドバーに移しても、CSSを書き直す必要がありません - 文字サイズをなめらかに連動させられる
cqi という単位を使うと、分岐を書かずに変化させられます。詳しくは「もうひとつの顔:コンテナ単位 cqi」で
デメリット
- 部品を包むdivが1枚、必ず必要になる
これを省くと動きません。理由は「効かないときのチェックリスト」で - ピクセルパーフェクトとは相性が悪い
デザインカンプにない中間状態が生まれます。「で、結局あなたは使うべき?」で詳しく - すでにあるdivに後から足すと、レイアウトが壊れることがある
新しくdivを1枚足せば防げます。「既存の要素に後付けしてはいけない理由」で - 書き間違えても、エラーが出ない
ただ何も起きないので、原因にたどり着きにくいです。「効かないときのチェックリスト」で
デメリットは4つとも、知っていれば避けられるものです。あとで対処法をまとめて説明します。
ここからは仕組みの話に入ります。まずはメディアクエリと比べるところから始めます。
コンテナクエリは、メディアクエリの弱点を埋めるために生まれた仕組みです。その弱点が分かると、コンテナクエリの書き方が自然と腑に落ちます。
メディアクエリの盲点:部品は自分の居場所を知らない
おさらいから始めます。メディアクエリは、レスポンシブでおなじみの記法ですね。「画面がこの幅以上ならこのスタイル」という条件分岐を書くための仕組みです。
ここで、メディアクエリが何を基準に判定しているかを思い出してみましょう。
@media (min-width: 768px) {
.card {
display: flex;
}
}この768pxはブラウザの画面幅です。ここに大きな盲点があります。
画面幅が1200pxだったとしても、部品が置かれている場所の幅は1200pxではありません。本文カラムなら700px、サイドバーなら300pxかもしれない。それなのにメディアクエリは、どちらの部品にも同じ「広い画面用」のスタイルを適用してしまいます。
メディアクエリが見ているのは、一番外側の幅だけ
言い換えると、こうなります。
メディアクエリが教えてくれるのは「画面の幅」だけ。だから部品は、自分が今どれくらいの広さの場所に置かれているのかを知ることができない。
これまで私たちは、どうやって回避してきたでしょうか。おそらく親を名指しして例外を書いてきたはずです。
/* 基本は横並び */
.card {
display: flex;
}
/* サイドバーの中だけ、縦積みに戻す */
.sidebar .card {
display: block;
}
/* フッターの中も、縦積みに戻す */
.footer .card {
display: block;
}でもこの方法は、置き場所が増えるたびにCSSが増えます。しかも部品を移動させるたびに、CSSも書き直しになります。
コンテナクエリは、この関係をひっくり返します。部品自身が「自分の親の幅」を見て、自分で形を決める。置く側は何も指定しなくていい。
メディアクエリがページ視点だとすれば、コンテナクエリは部品視点です。この視点の切り替えが、いちばん大事なところです。
そもそも「コンテナ」とは?
用語の整理をしておきます。コンテナクエリの「コンテナ」とは、部品を包んでいる親要素のことです。日本語にすると「入れ物」ですね。
ただし、すべての親要素が自動的にコンテナになるわけではありません。CSSで「ここを基準の箱にします」と指定した要素だけが、コンテナとして扱われます。
そしてコンテナクエリとは、そのコンテナの幅を条件にして、中身のスタイルを切り替える仕組みのことです。メディアクエリと並べると違いがはっきりします。
| 何を基準にするか | |
|---|---|
| メディアクエリ | ブラウザの画面幅(ビューポート) |
| コンテナクエリ | 自分で指定した親要素(コンテナ)の幅 |
基準を「画面」から「自分の入れ物」に付け替える。やっていることは、それだけです。
コンテナクエリの基本は「2人1組」
コンテナクエリには、必ず2人の登場人物がいます。ここを混同すると、まず動きません。
| 役割 | 誰が | 何を書くか |
|---|---|---|
| ① 基準の箱になる | 親(ラッパー) | 「ここを測る基準にします」と宣言する |
| ② 箱の幅を見て、形を変える | 中身(部品) | 「今の箱は何px?」を条件にスタイルを書く |
① 親が「基準の箱だよ」と宣言する
ラッパーになる親要素に、container-type を書きます。
.cw-demo-area {
container-type: inline-size;
}inline-size は「横方向だけ測る」という意味です。高さも測る size という値もありますが、高さを測ろうとすると「中身が伸びる、箱が伸びる、また判定が変わる」という無限ループの危険があります。
実務ではほぼ inline-size 一択と覚えて大丈夫です。
② 中身が「今の箱、何px?」と聞く
中身のCSSでは、@container を使って問い合わせます。
.cw-demo-card {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}
@container (min-width: 400px) {
.cw-demo-card {
grid-template-columns: 130px 1fr;
align-items: center;
}
}ここでコンテナの名前をひとつも書いていないことに注目してください。これはサボりではなく、一番近い祖先のコンテナを自動で探しに行くという仕様です。
部品がどこに置かれても、その場の親を勝手に見つけてくれる。これが「部品が自律する」の正体です。
ひとつ大事な決まりがあります。container-type を書いた要素そのものには、@container の効果が届きません。効くのは、その中に入っている要素だけです。
だから親(ラッパー)と中身(部品)は、必ず別々の要素にする必要があります。詳しくは後半のチェックリストで説明します。
書く順番が逆になる
メディアクエリをPCファーストで書いている場合、「まずPC用のスタイルを書いて、狭い画面で打ち消す」という順番になっていたと思います。
コンテナクエリでは、この順番を逆にしてください。
狭いときを基本形にして、広くなったら足す。部品はどこに置かれるか分からないので、一番不利な状況をデフォルトにしておくのが安全です。上のコードも、縦積みを初期値にしています。
実際に動かしてみましょう。下のプレビューは、点線の枠の右下をドラッグすると幅を変えられます(パソコンの場合)。ブラウザの画面幅はまったく変えていないのに、カードが自分で形を変えます。
↓ 点線の枠の右下をドラッグしてみてください
コンテナクエリのカード
画面幅ではなく、外側の箱の幅を見て形を決めています。
点線の枠は、ドラッグして試すためだけに用意したものです。実際のコードには必要ありません。
コピペで使えるコンテナクエリ見本
ここからは、そのまま使える見本です。どれも置く場所を変えるだけで、勝手に形が変わります。
プレビューはすべて、点線の枠をドラッグして幅を変えられます。切り替わる瞬間を確かめてみてください。
記事カード
狭いときは縦積み、広いときはサムネイルが左に回り込みます。関連記事や一覧で使いやすい形です。
記事カード
説明文がここに入ります。狭い場所では縦に積まれます。
<div class="cw-card-area">
<div class="cw-card">
<div class="cw-card-thumb">アイキャッチ</div>
<div>
<p class="cw-card-title">記事カード</p>
<p class="cw-card-text">説明文がここに入ります。</p>
</div>
</div>
</div>.cw-card-area {
container-type: inline-size;
}
.cw-card {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
padding: 16px;
box-sizing: border-box;
background: #fff;
border: 1px solid #e3e6eb;
border-radius: 12px;
}
.cw-card-thumb {
display: flex;
align-items: center;
justify-content: center;
height: 90px;
font-size: 13px;
color: #4a7ab5;
background: #eef4fb;
border-radius: 8px;
}
.cw-card-title {
margin: 0 0 6px;
font-size: 16px;
font-weight: bold;
line-height: 1.5;
}
.cw-card-text {
margin: 0;
font-size: 13px;
color: #6b7280;
line-height: 1.7;
}
@container (min-width: 400px) {
.cw-card {
grid-template-columns: 130px 1fr;
align-items: center;
}
}プロフィールボックス
狭いときはアイコンが上で中央寄せ、広いときはアイコンが左に来て左寄せになります。文字の揃え方まで変わるのがポイントです。
ゴロー
CSSとWordPressのことを書いています。
<div class="cw-prof-area">
<div class="cw-prof">
<div class="cw-prof-avatar">画像</div>
<div>
<p class="cw-prof-name">ゴロー</p>
<p class="cw-prof-text">CSSとWordPressのことを書いています。</p>
</div>
</div>
</div>.cw-prof-area {
container-type: inline-size;
}
.cw-prof {
display: grid;
grid-template-columns: 1fr;
justify-items: center;
text-align: center;
gap: 12px;
padding: 20px;
box-sizing: border-box;
background: #fff;
border: 1px solid #e3e6eb;
border-radius: 12px;
}
.cw-prof-avatar {
display: flex;
align-items: center;
justify-content: center;
width: 72px;
height: 72px;
font-size: 12px;
color: #3d8b52;
background: #dff0e4;
border-radius: 50%;
}
.cw-prof-name {
margin: 0 0 4px;
font-size: 16px;
font-weight: bold;
}
.cw-prof-text {
margin: 0;
font-size: 13px;
color: #6b7280;
line-height: 1.7;
}
@container (min-width: 380px) {
.cw-prof {
grid-template-columns: auto 1fr;
justify-items: start;
text-align: left;
align-items: center;
gap: 18px;
}
}CTAボックス
狭いときはボタンが下に落ちて横幅いっぱいに、広いときはテキストの右に並びます。記事下にもサイドバーにも、同じコードで置けるのが便利なところです。
コピペで使えるCSS見本集
ボタン・カード・フォームをまとめています。
<div class="cw-cta-area">
<div class="cw-cta">
<div>
<p class="cw-cta-title">コピペで使えるCSS見本集</p>
<p class="cw-cta-text">ボタン・カード・フォームをまとめています。</p>
</div>
<a class="cw-cta-btn" href="#">見本を見る</a>
</div>
</div>.cw-cta-area {
container-type: inline-size;
}
.cw-cta {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
align-items: center;
padding: 20px;
box-sizing: border-box;
background: #f4faf6;
border: 1px solid #d6eadd;
border-radius: 12px;
}
.cw-cta-title {
margin: 0 0 4px;
font-size: 15px;
font-weight: bold;
}
.cw-cta-text {
margin: 0;
font-size: 13px;
color: #6b7280;
line-height: 1.7;
}
.cw-cta-btn {
display: block;
text-align: center;
padding: 12px 20px;
font-size: 14px;
font-weight: bold;
text-decoration: none;
white-space: nowrap;
color: #fff;
background: #3d8b52;
border-radius: 8px;
}
@container (min-width: 420px) {
.cw-cta {
grid-template-columns: 1fr auto;
}
}お使いのテーマによっては、背景色・枠線・角丸・影がテーマ側のCSSに打ち消されることがあります。その場合は、効かない行の末尾に !important を足してみてください。
CSSの追加場所に迷ったときは、WordPressにCSSを追加する3つの方法|初心者は追加CSSだけでOKもあわせてどうぞ。
もうひとつの顔:コンテナ単位 cqi
ここまで @container の話をしてきましたが、コンテナクエリにはもうひとつの顔があります。コンテナを基準にした単位です。
vw は「画面幅の1%」でした。その考え方のコンテナ版だと思ってください。
| 単位 | 意味 |
|---|---|
| cqw | コンテナ幅の1% |
| cqh | コンテナ高さの1% |
| cqi | コンテナのインライン方向(横書きなら幅)の1% |
| cqmin / cqmax | 2方向のうち、小さいほう/大きいほう |
横書きの日本語サイトなら、cqw と cqi はほぼ同じ結果になります。ただし縦書きになったとき、意図どおりに動くのは cqi のほうです。迷ったら cqi を選んでおけば問題ありません。
これまで見てきた @container は、「400px以上なら横並び」というように、境目でパチンと切り替わる書き方でした。399pxと400pxの間で、見た目が急に変わります。
cqi は違います。境目がありません。箱が1px広がれば、文字も少しだけ大きくなる。ずっとなめらかに変化し続けます。
しかも @container のブロックを1行も書かずに、これができます。
箱の幅で文字が変わる
分岐を書かなくても、文字サイズがなめらかに連動します。
.cw-cqi-area {
container-type: inline-size;
}
.cw-cqi-title {
font-size: clamp(16px, 5cqi, 30px);
font-weight: bold;
line-height: 1.4;
}
.cw-cqi-text {
font-size: clamp(12px, 2.4cqi, 15px);
color: #6b7280;
line-height: 1.7;
}@container のブロックが1つもないことに注目してください。それでも文字サイズは箱の幅に連動します。
clamp で最小値と最大値をはさんでいるので、小さくなりすぎることも、壊れることもありません。ここは必ずセットで使ってください。
cqiの数値はどう決める?
ここでつまずく方が多いと思います。pxのような直感がきかないからです。
コツは、cqiをサイズではなく「割合」として見ることです。5cqi は「コンテナ幅の5%」という意味しか持ちません。
| コンテナ幅 | 5cqi の実サイズ |
|---|---|
| 300px | 15px |
| 400px | 20px |
| 500px | 25px |
| 600px | 30px |
数値を当てずっぽうでいじる必要はありません。基準点をひとつ決めて、割り算するだけです。
手順1:基準点を決める
「コンテナが600pxのとき、見出しは28pxにしたい」というように、幅と文字サイズをセットで1組だけ決めます。ここは普段のpx感覚で構いません。
手順2:割り算する
目標の文字サイズを基準のコンテナ幅で割り、100をかけます。
/* 28 ÷ 600 × 100 = 4.67 */
.cw-title {
font-size: 4.67cqi;
}手順3:clampで上下限をはさむ
これをしないと、狭い箱では読めないほど小さくなり、広い箱では大きくなりすぎます。
.cw-title {
font-size: clamp(16px, 4.67cqi, 30px);
}読み方はこうです。基本は4.67cqi。ただし16pxより小さくならず、30pxより大きくもならない。
慣れるまでは、次の範囲から始めてみてください。
| 用途 | 目安 |
|---|---|
| 見出し | 4〜6cqi |
| 本文 | 2〜2.5cqi |
ただし、本文には無理に使わないほうがいいと考えています。文字サイズが場所によって変わると、読み手が疲れてしまうからです。
見出しや数字など、大きさで印象が決まるものに絞って使う。これくらいが実用的だと思います。
@container は、境目でパチンと切り替える。cqi は、境目なしでなめらかに変える。この2つはセットで覚えると強い。
効かないときのチェックリスト
コンテナクエリは、失敗しても静かに黙っているのが厄介なところです。エラーも警告も出ず、ただ何も起きない。上から順に確認してください。
① カードと同じ要素に container-type を書いていないか
これが最頻出です。「カードの幅で分岐したいんだから、カードに container-type を付ければいい」と考えるのが自然ですが、これは動きません。
/* 動きません */
.cw-card {
container-type: inline-size;
}
@container (min-width: 400px) {
.cw-card {
display: flex;
}
}なぜ動かないのか。上のコードでは、.cw-card が container-type を書いた本人だからです。
@container が形を変えられるのは、container-type を書いた要素の「中身」だけです。container-type を書いた要素そのものは、対象になりません。
<div class="cw-card-area">
<!-- container-type を書いた本人。@container では変えられない -->
<div class="cw-card">
<!-- こちらは中身。@container で変えられる -->
</div>
</div>.cw-card に container-type を書いてしまうと、.cw-card 自身は @container の対象から外れます。書いても何も起きません。エラーも出ないので、余計に気づきにくいのです。
だから、1つの要素に container-type と @container の両方を効かせることはできません。必ず外側にもう1枚、ラッパーが必要になります。
<div class="cw-card-area"><div class="cw-card">カードの中身</div></div>/* 動きます */
.cw-card-area {
container-type: inline-size;
}
@container (min-width: 400px) {
.cw-card {
display: flex;
}
}container-type を書く要素と、@container で形を変える要素。この2つを別々の要素にする。これが動くコードと動かないコードの分かれ目です。
② コンテナ名のつづりが合っているか
コンテナには名前を付けることもできます。入れ子になったコンテナがあるとき、狙った祖先を名指しできます。
.cw-content-area {
container-type: inline-size;
container-name: content;
}
@container content (min-width: 700px) {
.cw-card {
grid-template-columns: 160px 1fr;
}
}ただし落とし穴があります。存在しない名前を指定しても、エラーになりません。警告も出ません。ただ永久にマッチしないだけです。
つづりを1文字間違えただけで、原因不明のバグになります。まずは名前なしで動かして、必要になってから名前を付けるのが安全です。
③ ラッパーに padding が付いていないか
判定に使われるのは、padding を除いた内側の幅です。ラッパーに padding を20px入れていたら、400pxの箱でも判定に使われるのは360pxになります。
ラッパーには余白を付けず、内側の要素に付ける。これだけで防げます。
④ ラッパーの幅を、実際に測ってみる
ここまで確認しても動かないなら、そもそもラッパーが思ったより狭い(または広い)可能性があります。
たとえば @container (min-width: 400px) と書いたのに、ラッパーが実際には380pxしかなければ、当然ながら切り替わりません。コードは正しいのに、条件を満たしていないという状態です。
ブラウザで実際の幅を確認しましょう。手順はこうです。
- ページ上でラッパーの部分を右クリックして、「検証」を選ぶ
- 開いたパネルで、ラッパーの要素(container-type を書いたdiv)をクリックする
- その要素にマウスを乗せると、ページ上に幅がpxで表示される
ここで出た数字が、@container が実際に見ている幅です。
あとは数字を突き合わせるだけです。実際の幅が380pxなら、@container の400pxという条件を下げればいい。コードを疑う前に、まず数字を見る。これが一番の近道です。
CSSが効かない原因は、コンテナクエリ以外にもいろいろあります。詰まったときはCSSが効かない・反映されない原因と直し方チェックリスト
もあわせて確認してみてください。
既存の要素に後付けしてはいけない理由
ここが、この記事でいちばん伝えたいところです。
container-type を書くと、その要素には裏でコンテインメント(封じ込め)という制約がかかります。ブラウザが「この要素の中身は外に影響しない」と決めつけて、計算を省略する仕組みです。
この制約から、3つの副作用が生まれます。どれも「なぜか壊れた」という形で出てくるので、知らないと原因にたどり着けません。
副作用1:中身に合わせて伸び縮みしなくなる
inline-size のコンテインメントは、「中身を見ずに横幅を決める」という宣言でもあります。
だから、中身の分だけ縮んでいた要素(インラインブロック、フロート、テーブル、絶対配置など)に container-type を付けると、突然フル幅に広がったり、逆に潰れたりします。
副作用2:position: fixed が fixed でなくなる
3つのうち、いちばん厄介なのがこれです。コンテインメントがかかった要素は、中にある固定配置・絶対配置の基準点になってしまいます。
つまり、コンテナの中に position: fixed のモーダルや追従バナーがあると、画面ではなくコンテナを基準に位置が決まります。「モーダルが画面中央に出ない」という不可解なバグの正体です。
副作用3:z-index が外に出られなくなる
コンテナになった要素は、新しい重なりの文脈を作ります。中の要素にどれだけ大きな z-index を振っても、コンテナの外側にある要素より上には行けません。
ドロップダウンやツールチップが、他の要素の下に潜り込む。そんな症状で出てきます。
ここから導かれる結論は、ひとつです。
container-type は「便利な追加」ではなく、「その要素の性質を変える」宣言。ラッパーは専用に1枚新設して、そこだけに付ける。
既存のdivに軽い気持ちで付け足すと、思わぬ場所が壊れます。div をひとつ足すだけの無害な操作ではないと覚えておいてください。
で、結局あなたは使うべき?
冒頭でも触れましたが、ひととおり触ったうえでの結論をあらためて書きます。
| 場面 | 主役はどっち |
|---|---|
| 受託・デザインカンプ通りに組む案件 | メディアクエリ |
| 1枚もののLP・ランディングページ | メディアクエリ |
| デザインシステム・自社プロダクト | コンテナクエリ |
| WordPressテーマやブロックの配布 | コンテナクエリ |
デザインカンプが375px、768px、1440pxで切られている案件では、ブレイクポイントは最初から画面幅に紐付いています。そこにコンテナクエリを持ち込むと、カンプにない中間状態が無数に生まれます。
デザイナーが確認していない見た目が出てくるわけで、これは歓迎されません。次に触る人も読みにくくなります。
1枚のLPも同じです。置き場所が最初から決まっているなら、コンテナクエリは過剰です。
では、どこで有効なのか。答えは「置き場所を自分で決められない部品」です。
- 配布するテーマやブロック。書き手がカラムの中に置くか、全幅で置くか、作者には予測できません
- デザインシステムの共通部品。同じカードをグリッド、モーダル、サイドバーで使い回します
- 管理画面やダッシュボード。パネルの幅をユーザーが変えられます
共通しているのは、「最終的に何pxの場所に置かれるか、書いた本人には分からない」という点です。これがコンテナクエリの本当の出番だと思います。
まとめ
コンテナクエリを一通り触ってみて分かったことは、大きく3つです。
- 視点が変わる。ページ視点のメディアクエリに対して、コンテナクエリは部品視点
- 2人1組で書く。親が container-type で宣言し、中身が @container で形を変える。同じ要素には両方を効かせられない
- 顔がふたつある。@container は境目でパチンと切り替え、cqi は境目なしでなめらかに変化
そして何より、「使わなくていい場面がある」と分かったことが収穫でした。コンテナクエリは向き不向きがはっきりしています。まずは自分の作るものがどちらなのかを見極めるところからだと思います。
まずは上の見本をコピペして、点線の枠をドラッグしてみてください。手を動かすと、腹落ちの速さが違います。
カードデザインのバリエーションを探している方は、【CSS】カードデザイン12選|コピペで使えるおしゃれ見本集もあわせてどうぞ。

