- 著者
- app-hacks
- 公開日
- 更新日
- 確認日
CSS / レスポンシブデザイン / コンポーネント設計
CSSコンテナクエリで部品単位のレスポンシブ設計をする
CSSコンテナクエリで親レイアウトではなく部品自身の幅を基準に表示を切り替え、再利用しやすいカードを設計する方法を解説します。
CSSコンテナクエリで部品単位のレスポンシブ設計をする
メディアクエリはビューポート全体の幅を基準にするため、同じカードをサイドバーと本文の両方で使うと、カード自身の余白に合わないタイミングでレイアウトが変わることがあります。コンテナクエリは、親要素をクエリコンテナとして登録し、子コンポーネントが自分の置かれた幅を条件にスタイルを切り替える仕組みです。
親要素をクエリコンテナにする
まず、部品の幅を決める親要素にcontainer-typeを指定します。inline-sizeを指定すると、インライン方向のサイズを条件にできます。名前を付けておくと、同じページに複数のコンテナがある場合も条件の対象を明確にできます。
.card-list {
container: card-list / inline-size;
display: grid;
gap: 1rem;
}
この時点ではカードの表示は変わりません。親が「幅を問い合わせられる範囲」を宣言し、子側の@containerがその幅を読むという役割分担です。コンテナ名は、色や見た目ではなく、何の幅を提供するかを表す名前にします。
子コンポーネントの幅で列数を切り替える
カード自身の構造は通常の1列表示を基本にし、十分な幅がある場合だけ2列の内部レイアウトへ切り替えます。ビューポートの幅ではなく、card-listの幅が条件になるため、同じカードを別の親に置いても自分の使える幅に合わせて動作します。
.card {
display: grid;
gap: 0.75rem;
}
@container card-list (min-width: 36rem) {
.card {
grid-template-columns: 8rem 1fr;
align-items: start;
}
}
ブレークポイントの数値は、サイト全体の端末幅から決めるのではなく、カードのタイトルや説明文が読める最小幅から決めます。カードの内容が変われば適切な境界も変わるため、値を共通の正解として別の部品へコピーしないことが大切です。
メディアクエリとの責務を分ける
ページ全体のナビゲーションや、画面の大きな構造を切り替える処理はメディアクエリの方が読みやすい場合があります。一方、カード、検索結果、料金プランのように配置先が変わる部品はコンテナクエリと相性がよい設計です。
/* ページ全体の構造はビューポートを基準にする */
@media (min-width: 60rem) {
.site-header {
display: grid;
grid-template-columns: auto 1fr auto;
}
}
/* 再利用部品の内部構造は親コンテナを基準にする */
@container card-list (min-width: 36rem) {
.card__actions {
justify-self: end;
}
}
基準を混ぜると、どの幅で変更が起きるのか追いにくくなります。「ページ構造はビューポート」「部品内部はコンテナ」というように、判断する対象を先に決めておくと保守しやすくなります。
サイズと内容を実際に確認する
コンテナクエリを追加したら、広い画面だけでなく、狭いサイドバー、本文中の埋め込み領域、長いタイトルでも確認します。min-widthの境界付近で、見出しの折り返し、ボタンの横幅、画像の縮小を確認し、特定の文章だけでレイアウトが破綻しないかを見ます。
コンテナ名の変更や親要素の入れ替えを行うと、条件が一致しなくなることがあります。部品を移動した後は、クラス名だけでなく、祖先要素にコンテナが登録されているかも確認してください。値の役割をカスタムプロパティで管理する場合は、CSSカスタムプロパティでテーマ値を管理する設計の命名方針と揃えると、部品の責務を整理しやすくなります。