TECH · コンテナクエリ
画面幅ではなく親の幅で切り替える
コンテナクエリのサンプル集。container-typeの指定、@containerによる分岐、cqi単位でのタイポグラフィ、名前付きコンテナ、同じ部品を本文とサイドバーの両方に置く例を、動くプレビュー付きで収録します。
サンプルとコード
HTMLもクラスも同じカードを、幅の違う3か所に置いています。ブラウザの幅を変えなくても形が変わることが、メディアクエリとの違いそのものです。
HTML
<article class="card">
<div class="card-thumb"></div>
<div>
<span class="card-tag">CSS</span>
<h3 class="card-title">置き場所の幅で形が決まる</h3>
<p class="card-text">
このカードは画面幅を見ていません。親要素の幅だけを見て、縦積みと横並びを切り替えています。
</p>
</div>
</article> CSS
/* 置き場所をコンテナとして宣言する。inline-size は「横幅だけを
問い合わせ対象にする」指定で、縦は今までどおり中身で決まる。 */
.card-slot {
container-type: inline-size;
}
/* 既定は縦積み。狭いほうを既定にしておくと、
対応していない環境でも壊れずに読める形が残る。 */
.card {
display: grid;
gap: .9rem;
padding: 1rem;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
}
.card-thumb {
aspect-ratio: 16 / 10;
border-radius: 8px;
background: linear-gradient(135deg, #dbeafe, #bfdbfe 60%, #93c5fd);
}
.card-title { margin: 0; font-size: 1rem; line-height: 1.6; }
.card-text { margin: .35rem 0 0; font-size: .86rem; line-height: 1.85; color: var(--muted); }
.card-tag {
display: inline-block; margin-bottom: .4rem;
font-size: .72rem; line-height: 1.8; color: var(--accent);
}
/* ここが本題。見ているのは画面幅ではなく、直近の祖先コンテナの幅。
同じカードでも、置かれた場所が 380px 以上なら横並びになる。 */
@container (min-width: 380px) {
.card {
grid-template-columns: 160px 1fr;
align-items: center;
}
.card-title { font-size: 1.08rem; }
}
/* 段階を足すのもメディアクエリと同じ書き方でよい。 */
@container (min-width: 560px) {
.card { grid-template-columns: 220px 1fr; padding: 1.2rem; }
.card-text { font-size: .92rem; }
} AIへの指示文
コンテナクエリで、置き場所の幅によって形が変わるカードを作ってください。
要件:
- カードを受け取る要素に container-type: inline-size を指定する。
なぜ size ではなく inline-size なのかをコメントで書く
(size を指定すると高さも中身から決まらなくなる)。
- 既定は縦積みにする。対応していない環境でも読める形を既定に
置く、という理由をコメントで書く。
- @container (min-width: 380px) で横並びに切り替え、
560px でさらに余白と文字を大きくする。
- 同じHTMLのカードを、幅の違う3つの入れ物に置いて並べる。
- メディアクエリは1つも使わない。
- 日本語の本文なので line-height は 1.85、letter-spacing は 0 にする。
cqi は親コンテナの横幅の1%です。vw と違い画面ではなく親を見るので、同じ部品を狭い欄に置いても比率が保たれます。clamp で挟むのが実用上の必須条件です。
HTML
<section class="panel">
<span class="panel-badge">CSS Container Query Units</span>
<h2 class="panel-title">親の幅に、文字が追いつく</h2>
<p class="panel-text">
見出しも本文も余白も、画面幅ではなく親要素の幅を基準にしています。同じ部品を狭い欄に置いても、比率を保ったまま縮みます。
</p>
</section> CSS
.panel {
/* container-type を付けた時点で、この要素は
コンテナクエリ単位(cqw / cqi / cqh / cqb)の基準にもなる。 */
container-type: inline-size;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
padding: clamp(1rem, 4cqi, 2rem);
}
/* cqi は「コンテナの inline 方向のサイズの1%」。
横書きの日本語では横幅の1%にあたる。
vw と違って、画面ではなく親の幅に追随する。 */
.panel-title {
margin: 0;
/* 下限・上限を clamp で必ず挟む。cqi 単体だと、
狭いときに読めない大きさまで縮み続ける。 */
font-size: clamp(1.05rem, 5cqi, 2rem);
line-height: 1.45;
}
.panel-text {
margin: .6rem 0 0;
font-size: clamp(.84rem, 2.1cqi, 1rem);
line-height: 1.85;
color: var(--muted);
/* 1行の文字数も親幅に合わせる。読みやすさは
フォントサイズだけでなく行の長さで決まる。 */
max-width: 40em;
}
.panel-badge {
display: inline-block;
margin-bottom: .5rem;
/* 余白も cqi にすると狭いときに消えてしまう。
ここも clamp で下限を確保する。 */
padding: clamp(.15rem, .6cqi, .3rem) clamp(.55rem, 2cqi, 1rem);
border-radius: 999px;
background: #eff6ff;
color: var(--accent);
font-size: clamp(.68rem, 1.6cqi, .82rem);
line-height: 1.8;
} AIへの指示文
コンテナクエリ単位を使って、親の幅に追随するパネルを作ってください。
要件:
- パネルの外側に container-type: inline-size を付け、
cqi の基準にする。
- 見出し・本文・バッジの文字サイズを clamp(下限, Ncqi, 上限) で指定する。
clamp で挟まないと狭いときに読めない大きさまで縮む、という理由を
コメントで書く。
- 余白にも cqi を使い、幅に比例して詰まるようにする。
- 本文には max-width を em で与え、1行の文字数も制御する。
- 親要素の幅をスライダーで変えられるようにして、
画面幅を変えずに変化することを確かめられるようにする。
コンテナが入れ子になると、名前なしの@containerは最も近い祖先を見ます。段組みは内側、余白は外側、というように参照先を分けたいときに名前が要ります。
HTML
<div class="page">
<div class="column">
<article class="card">
<div class="card-thumb"></div>
<div>
<h3 class="card-title">近い祖先と、遠い祖先</h3>
<p class="card-text">
段組みは内側のコンテナ、余白は名前で指定した外側のコンテナを見て決めています。
</p>
</div>
</article>
</div>
</div> CSS
/* コンテナに名前を付ける。省略記法の container は
`container: <name> / <type>` の順で書く。 */
.page {
container: page / inline-size;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
padding: 1rem;
}
/* 内側にもう1つコンテナがある。名前を付けていない匿名コンテナでも、
「最も近い祖先」として選ばれてしまう点に注意。 */
.column {
container: column / inline-size;
border: 1px dashed var(--line);
border-radius: 10px;
padding: .8rem;
}
.card {
display: grid;
gap: .8rem;
padding: .9rem;
border: 1px solid var(--line);
border-radius: 10px;
background: #fbfcfe;
}
.card-thumb {
aspect-ratio: 16 / 9;
border-radius: 8px;
background: linear-gradient(135deg, #dbeafe, #93c5fd);
}
.card-title { margin: 0; font-size: .98rem; line-height: 1.6; }
.card-text { margin: .3rem 0 0; font-size: .84rem; line-height: 1.85; color: var(--muted); }
/* 名前なしの @container は、最も近い祖先コンテナ(= .column)を見る。
中身の詰まり具合を決めるのはこちらが自然。 */
@container (min-width: 320px) {
.card { grid-template-columns: 120px 1fr; align-items: center; }
}
/* 名前を書くと、近い祖先を飛び越して指定した祖先を見にいく。
「ページ全体が広いときだけ余白を増やす」のように、
役割の違う判断を混ぜずに書ける。 */
@container page (min-width: 620px) {
.card { padding: 1.2rem; gap: 1.1rem; }
.card-title { font-size: 1.08rem; }
} AIへの指示文
入れ子になったコンテナを名前で使い分ける例を作ってください。
要件:
- 外側に container: page / inline-size、内側に
container: column / inline-size を指定する。
省略記法が「名前 / 型」の順であることをコメントで書く。
- 名前なしの @container は最も近い祖先(内側)を見る。
これで段組みを切り替える。
- @container page (min-width: 620px) で外側を参照し、
こちらでは余白と見出しサイズだけを変える。
- 名前を付けないと途中の要素が意図せずコンテナになっていて
参照先がずれることがある、という注意をコメントで書く。
- 外側の幅をスライダーで変えられるようにする。
コンテナクエリが本当に効くのはこの場面です。修飾クラスも別コンポーネントも作らず、部品自身が置き場所を見て情報量まで変えています。
HTML
<div class="layout">
<main class="region">
<p class="region-label">本文欄</p>
<article class="notice">
<span class="notice-date">2026.09.01</span>
<h3 class="notice-title">サーバー移設のお知らせ</h3>
<p class="notice-text">
9月14日の午前2時から4時にかけて、一時的にアクセスできない時間があります。
</p>
<a class="notice-action" href="#">詳細</a>
</article>
</main>
<aside class="region">
<p class="region-label">サイドバー</p>
<article class="notice">
<span class="notice-date">2026.09.01</span>
<h3 class="notice-title">サーバー移設のお知らせ</h3>
<p class="notice-text">
9月14日の午前2時から4時にかけて、一時的にアクセスできない時間があります。
</p>
<a class="notice-action" href="#">詳細</a>
</article>
</aside>
</div> CSS
/* ページ側のレイアウト。ここは画面幅の話なので
メディアクエリのままでよい。役割が違う。 */
.layout {
display: grid;
gap: 1.2rem;
grid-template-columns: 1fr;
}
@media (min-width: 720px) {
.layout { grid-template-columns: minmax(0, 1fr) 260px; }
}
/* 部品を受け取る場所は、どこもコンテナにしておく。
こうしておけば、あとで置き場所を入れ替えても
部品側のCSSを触らずに済む。 */
.region { container-type: inline-size; }
/* --- 以下は「お知らせ」部品。置き場所を一切知らない --- */
.notice {
display: grid;
gap: .7rem;
padding: .9rem;
border: 1px solid var(--line);
border-left: 3px solid var(--accent);
border-radius: 10px;
background: #fff;
}
.notice-date {
font-size: .74rem; line-height: 1.8; color: var(--muted);
font-variant-numeric: tabular-nums;
}
.notice-title { margin: 0; font-size: .96rem; line-height: 1.6; }
.notice-text { margin: 0; font-size: .84rem; line-height: 1.85; color: var(--muted); }
.notice-action {
justify-self: start;
padding: .45rem .9rem;
border-radius: 8px;
background: var(--accent);
color: #fff;
font-size: .82rem;
line-height: 1.8;
text-decoration: none;
}
/* 狭い場所では説明文を落として、日付と見出しだけにする。
情報量を減らす判断も、部品の中に書ける。 */
.notice-text { display: none; }
@container (min-width: 300px) {
.notice-text { display: block; }
}
@container (min-width: 460px) {
.notice {
grid-template-columns: 6rem 1fr auto;
align-items: center;
padding: 1.1rem;
}
.notice-title { grid-column: 2; }
.notice-text { grid-column: 2; margin-top: -.4rem; }
.notice-date { grid-row: 1 / span 2; }
.notice-action { grid-column: 3; grid-row: 1 / span 2; }
} AIへの指示文
同じお知らせ部品を、本文欄とサイドバーの両方に置いてください。
要件:
- ページの2段組み自体はメディアクエリで作る。画面の話は
メディアクエリ、部品の話はコンテナクエリ、と役割で分ける
理由をコメントで書く。
- 部品を受け取る領域にはすべて container-type: inline-size を付ける。
あとで置き場所を入れ替えても部品側を触らずに済む、と書く。
- 部品は既定で説明文を display: none にし、
@container (min-width: 300px) で表示する。
狭いときに情報量を減らす判断も部品の中に書ける、と示す。
- @container (min-width: 460px) では日付・見出し・ボタンを
grid で1行に並べる。
- 2か所に置くHTMLは完全に同一にし、修飾クラスを一切足さない。
この技術について
同じカード部品を、本文の中に置くときとサイドバーに置くときで別々に作った経験はありませんか。.card と .card--narrow を用意して、置き場所ごとに正しいほうを選ぶ。その選択を間違えると、サイドバーの中で横並びのカードが潰れる。この面倒さの原因は、CSSが画面の幅しか見られなかったことにあります。
コンテナクエリは、見る対象を画面から親要素に変える仕組みです。親に container-type を付け、@container で分岐する。それだけで部品は自分の置かれた場所を見て形を決めるようになります。
2行で始まる
必要なのは実質2つだけです。
置き場所に container-type: inline-size を指定して「ここが基準です」と宣言する。そして部品側で @container (min-width: 380px) と書く。あとはメディアクエリと同じ書き方で、min-width も max-width も範囲構文も使えます。
inline-size は「横幅だけを問い合わせ対象にする」指定です。size を指定すると高さも中身から決まらなくなるので、実務ではほぼ常に inline-size を選びます。
既定は狭いほうに置く
@container に対応していないブラウザでは、そのブロックが丸ごと無視されます。つまりその外側に書いたスタイルだけが残る。
だから既定は縦積みにしておきます。広くなったときに横並びへ切り替える、という順で書けば、対応していない環境でも崩れずに読める形が残ります。逆にすると、狭い欄で横並びのまま潰れます。
単位も一緒に手に入る
container-type を付けた時点で、その要素はコンテナクエリ単位の基準にもなります。cqi は「コンテナの inline 方向のサイズの1%」で、横書きの日本語では横幅の1%です。
vw との違いは基準だけですが、部品にとっては決定的です。vw は置き場所を変えても値が同じですが、cqi は狭い欄に置けば小さくなります。
ただし cqi を単体で使ってはいけません。下限も上限も無いので、狭いところでは読めない大きさまで縮み、広いところでは不格好に膨らみます。clamp(下限, Ncqi, 上限) の形で必ず挟んでください。
cqi が要ります。名前を付けるのは、飛び越したいときだけ
コンテナが入れ子になると、名前なしの @container は最も近い祖先のコンテナを見ます。多くの場合これで正しいのですが、途中の要素が意図せずコンテナになっていて参照先がずれることがあります。
container: page / inline-size のように名前を付けておけば、@container page (min-width: 620px) で近い祖先を飛び越して指定できます。段組みは内側のコンテナ、余白はページ全体、というように役割の違う判断を混ぜずに書けるのが利点です。
自分自身は問い合わせられない
一つだけ、はっきりした制限があります。@container が見るのは祖先だけで、自分自身は対象にできません。
理由は循環です。自分の幅で分岐してレイアウトが変わり、その結果また幅が変わり、また分岐する。これを許すと計算が収束しないため、仕様として禁じられています。部品の外側にラッパーを1枚置き、そこをコンテナにしてください。
画面の話と、部品の話
コンテナクエリが入っても、メディアクエリは消えません。役割が違うからです。
ページを2段組みにするか1段にするかは画面の話です。ヘッダーのメニューを畳むかどうかもそうです。一方、その中に置かれた部品が横並びになるか縦積みになるかは、画面ではなく置き場所の話です。
| 決めること | 見ている対象 | 使うもの |
|---|---|---|
| ページを2段組みにするか1段にするか | 画面 | メディアクエリ |
| ヘッダーのメニューを畳むか | 画面 | メディアクエリ |
| カードを横並びにするか縦積みにするか | 置き場所 | コンテナクエリ |
| 狭いときに説明文を省くか | 置き場所 | コンテナクエリ |
この線引きで書き分けると、どちらのクエリを使うかで迷わなくなります。そして部品側のCSSは、自分がどこに置かれるかを知らないまま完結します。デザインシステムとして配る部品ほど、この性質が効いてきます。
使いどころとつまずきどころ
向いている場面
- 同じカードを本文とサイドバーの両方に置く画面
- 段組みが変わるダッシュボードの部品
- CMSで置き場所が決まらない埋め込み部品
- デザインシステムとして配る再利用部品
つまずきやすい点
- container-type を付けた要素は、その方向のサイズが子から決まらなくなる
- @container が見るのは祖先だけ。自分自身は問い合わせられない
- 名前を付けないと最も近い祖先コンテナが選ばれる
- cqi は親コンテナ基準。ネストすると基準が変わる
よくある質問
メディアクエリはもう使わなくていいのですか。
役割が違うので両方使います。ページ全体の骨格(2段組みにするか1段にするか、ヘッダーを畳むか)は画面の話なのでメディアクエリのままが自然です。コンテナクエリは、置き場所が決まっていない部品の中身をどう畳むかを担当します。画面の話はメディアクエリ、部品の話はコンテナクエリ、と分けると迷いません。
container-type は inline-size と size のどちらを指定すべきですか。
基本は inline-size です。size を指定すると横だけでなく高さも中身から決まらなくなり、明示的に高さを与えていない要素は潰れます。高さを問い合わせたい場面は実務ではほとんど無いので、迷ったら inline-size にしてください。
自分自身の幅で分岐できますか。
できません。@container が見るのは祖先のコンテナだけです。自分をコンテナにして自分に問い合わせると、分岐でサイズが変わり、そのサイズでまた分岐して、という循環になるため禁じられています。部品の外側に1枚ラッパーを置いて、そこをコンテナにしてください。
cqi と vw は何が違いますか。
基準が違います。vw は画面の幅の1%で、部品をどこへ置いても同じ値になります。cqi は直近のコンテナの inline 方向のサイズの1%なので、狭い欄に置けば小さくなります。ただし cqi は単体では下限も上限も無いので、必ず clamp で挟んでください。
対応していないブラウザではどうなりますか。
@container のブロックが丸ごと無視され、その外に書いたスタイルだけが適用されます。だから既定を狭いレイアウト(縦積み)にしておけば、崩れずに読める形が残ります。主要ブラウザは2023年に出揃っているので、いまは既定側を整えておく程度の配慮で足ります。