Tailwind CSS v4のコンテナクエリ入門|@containerで「置き場所」に応じて変わるUIを作る

同じカード部品を狭いサイドバーに置くと画像とテキストが縦積みになり、広いメイン領域に置くと横並びになる。親コンテナの幅で見た目が切り替わるコンテナクエリの概念図
  • このエントリーをはてなブックマークに追加

スポンサーリンク

同じカードコンポーネントなのに、サイドバーに置くと窮屈で崩れて見える——そんな経験はありませんか? 広いメイン領域では横並びできれいに収まるのに、狭いサイドバーに置いた途端、画像とテキストが横に並んだままはみ出してしまう。よくある「置き場所によって最適なレイアウトが変わる」問題で、これを解決するのが Tailwind CSSのコンテナクエリです。

これまでのメディアクエリ(sm: md:)は画面幅でしか切り替えられないので、この問題を根本的には解けませんでした。ここで効くのが Tailwind CSS v4のコンテナクエリです。この記事では、@container の基本から実例、名前付きコンテナ、ハマりどころまでを解説します。Tailwindの基礎に不安があれば Tailwind CSSの使い方【初心者向け】 を先にどうぞ。

まず結論:v4なら@containerを付けるだけ

コンテナクエリは「画面幅ではなく、親要素(コンテナ)の幅でスタイルを切り替える」仕組みです。Tailwind v4(2025年1月の安定版)からは追加プラグインなしで標準搭載されました。v3までは @tailwindcss/container-queries プラグインが必要でしたが、もう不要です。

使い方はシンプルで、次の2ステップだけです。

  • 基準にしたい親要素に @container を付ける
  • 中の子要素で @sm: @md: のように、アットマーク付きのブレークポイントを使う
<!-- 親に @container、子は @md: で「親が448px以上なら横並び」 -->
<div class="@container">
  <div class="flex flex-col @md:flex-row">
    <img src="/thumb.jpg" class="w-full @md:w-1/3">
    <div class="p-4">本文…</div>
  </div>
</div>

この @md:flex-row は「この親コンテナの幅が448px以上になったら横並びにする」という意味です。画面がどれだけ広くても、親が狭ければ縦積みのまま。逆に画面が狭くても親が広ければ横並びになります。@container を付けると container-type: inline-size が自動で当たるので、自分でCSSを書く必要はありません。

同じカード部品を狭いサイドバーに置くと画像とテキストが縦積みになり、広いメイン領域に置くと横並びになる。親コンテナの幅で見た目が切り替わるコンテナクエリの概念図
同じカードでも、置いた場所(親コンテナの幅)に応じて縦積み・横並びが自動で切り替わる

メディアクエリとの違いを1枚で整理する

混同しやすいので、先に違いをはっきりさせておきます。ポイントは「何の幅を見ているか」です。

sm: md:(メディアクエリ)@sm: @md:(コンテナクエリ)
基準にする幅画面(ビューポート)の幅親コンテナの幅
切り替わる単位ページ全体で一斉にコンポーネントごとに個別に
向いている場面ページ全体のレイアウトどこにでも置ける再利用部品
書き方md:flex-row親に @container + @md:flex-row
メディアクエリ(sm: md:)は画面全体の幅を見てページ全体で一斉に切り替わるのに対し、コンテナクエリ(@sm: @md:)は親要素の幅を見て部品ごとに個別に切り替わることを示す比較図
メディアクエリは画面(ビューポート)の幅、コンテナクエリは親要素の幅を基準にする

メディアクエリは「ページ全体を画面幅で組む」のに向いています。一方コンテナクエリは、同じ部品を置いた場所に合わせて最適化できるのが本質的なメリットです。

たとえば1枚のカードを、幅の狭いサイドバーに置けば自動で縦積みに、広いメイン領域に置けば横並びに——というのが、カード側のクラスを一切変えずに実現できます。「部品は1つ、置き場所は複数」という現代的なコンポーネント設計と、とても相性がよいわけです。

基本の書き方:レスポンシブなカードを作る

実際に「狭いと縦積み・広いと横並び」のカードを書いてみます。ポイントは、切り替えの基準になる親に @container を置くことです。

<!-- このカードは「親の幅」で見た目が変わる -->
<div class="@container">
  <article class="flex flex-col @sm:flex-row gap-4 rounded-lg border p-4">
    <img src="/thumb.jpg"
         class="w-full @sm:w-40 rounded object-cover">
    <div>
      <h3 class="text-lg font-bold">記事のタイトル</h3>
      <p class="mt-1 text-sm text-gray-600">概要テキスト…</p>
    </div>
  </article>
</div>

親が384px(@sm)未満のときは flex-col で縦積み、384px以上になると @sm:flex-row が効いて横並びになります。画像の幅も w-full から @sm:w-40 へ切り替わります。

このカードをグリッドの中に並べたり、サイドバーに1枚だけ置いたり、置き場所を変えても、それぞれの列幅に応じて勝手に最適なレイアウトになります。ダッシュボードのウィジェットや商品カードなど、同じ部品を何度も使い回すUIで威力を発揮します。

コンテナのブレークポイント一覧

アットマーク付きのブレークポイントは、メディアクエリの sm md とは値が別物です。コンテナは画面より小さいことが多いので、しきい値も小さめに設定されています。よく使うものを挙げます。

クラス幅(rem)幅(px)
@xs20rem320px
@sm24rem384px
@md28rem448px
@lg32rem512px
@xl36rem576px
@2xl42rem672px

「画面の md(768px)と同じ感覚で @md を使ったら、思ったより早く切り替わった」というのは、この値の違いが原因です。コンテナの @md は448pxだと覚えておきましょう。ちょうどよい値がなければ、後述の任意の値で指定できます。

名前付きコンテナで「どの親か」を指定する

コンテナを入れ子にすると、子要素はどの親を基準にすればよいか曖昧になります。デフォルトではいちばん近い @container を見ますが、「もっと外側の特定の親を基準にしたい」ときは名前付きコンテナを使います。

親に @container/名前 と付け、子で @md/名前: のように名前を添えるだけです。

<!-- 外側を main、内側を card と名付ける -->
<div class="@container/main">
  <div class="@container/card">
    <!-- 「main が672px以上」のときだけ効く -->
    <p class="hidden @2xl/main:block">メイン領域が広いときだけ表示</p>
  </div>
</div>

名前を付けておくと「近い方の親に意図せずマッチしてしまう」事故を防げます。入れ子のコンテナを扱うなら、最初から名前付きにしておくのが安全です。

用意された値以外を使いたいときは任意の値で

ブレークポイント表にちょうどよい値がないときは、角カッコで任意の幅を直接指定できます。@min-[475px]: は「475px以上」、@max-[800px]: は「800px以下」という意味です。

<div class="@container">
  <!-- 親が475px以上で2カラム、800px以下で文字を小さく -->
  <div class="grid grid-cols-1 @min-[475px]:grid-cols-2">…</div>
  <p class="@max-[800px]:text-sm">…</p>
</div>

プロジェクト全体で使う独自の基準なら、@theme で --container-* 変数を定義してオリジナルの名前を増やすこともできます。まずは任意の値で試し、繰り返し使うものだけ変数化する、という進め方がおすすめです。

ハマりポイント逆引き|@containerが効かないときの確認順

コンテナクエリでつまずきやすいのは、だいたい次の3つです。「@sm: や @md: が効かない」と思ったら、上から順に疑ってください。

  • 親に @container を付け忘れている:基準になる親がないと @md: は永遠に発火しません。まずは親の @container を確認
  • @sm: と sm: を取り違えている:アットマークの有無で「親の幅」か「画面の幅」かが変わります。挙動が想定と逆なら、まずここ
  • 入れ子で意図しない親にマッチしている:近い方の @container が基準になります。狙った親を指定したいなら名前付きコンテナで解決

特に多いのが1つ目です。子に @md:flex-row を書いても、親のどこかに @container がないと何も起きません。「レスポンシブに切り替わらない」ときは、まず親をたどって @container があるか見るのが最短ルートです。

まとめ

Tailwind v4のコンテナクエリを振り返ります。

  • v4ならプラグイン不要。親に @container、子に @sm: @md: を付けるだけ
  • sm: は画面幅、@sm: は親コンテナの幅で切り替わる(別物)
  • コンテナの @md は448pxなど、メディアクエリより小さめの値
  • 入れ子は名前付きコンテナ(@container/card + @md/card:)で曖昧さを消す
  • 効かないときは、まず親の @container を確認する

「どこに置いても最適に表示される部品」を作れるのが、コンテナクエリのいちばんの価値です。カードやウィジェットを再利用するUIを組む機会があれば、ぜひ @container から試してみてください。関連して、状態でスタイルを変える data属性の使い方 や peerとgroupの違い、全体像は Tailwind CSSの使い方【初心者向け】 もどうぞ。RailsにTailwind v4を入れる手順は RailsにTailwind CSS v4を導入する方法 で解説しています。

スポンサーリンク

  • このエントリーをはてなブックマークに追加