本文へ移動
aapp-hacks
← 記事一覧へ
著者
app-hacks
公開日

CSS / Tailwind CSS / Responsive Design

FlexboxとCSS Gridの実践的な使い分け:カードUIとヘッダーレイアウトの設計

一次元のヘッダーにはFlexbox、二次元のカード一覧にはGridを使う判断基準をCSSとTailwindで示す。

FlexboxとCSS Gridは競合する仕組みではない。Flexboxは一つの軸に沿って要素を並べ、余白や折り返しを調整する。Gridは行と列を同時に定義し、複数行にまたがる要素の位置を揃える。コンポーネントの主な制約が一次元か二次元かで選ぶと、不要な幅計算やmedia queryを減らせる。

ヘッダーはFlexboxで考える

一般的なヘッダーは、左にサイト名、右にナビゲーションを置く。必要なのは横方向の配置と、狭い画面での間隔調整であり、行と列の交点を管理する必要はない。

<header class="site-header">
  <a class="brand" href="/">app-hacks</a>
  <nav aria-label="メインナビゲーション">
    <ul class="nav-list">
      <li><a href="/blog/">記事一覧</a></li>
      <li><a href="/about/">About</a></li>
    </ul>
  </nav>
</header>
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  min-height: 4rem;
}

.nav-list {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

justify-content: space-betweenが両端へ配置し、align-items: centerが交差軸上で高さを揃える。ロゴ幅とナビゲーション幅を列定義として先に決める必要はない。要素の実寸を保ったまま残りの余白だけを分配できるのがFlexboxの利点である。

Tailwind CSSでは同じ構造をユーティリティで直接表現できる。

<header class="flex min-h-16 items-center justify-between gap-5">
  <a class="text-lg font-bold" href="/">app-hacks</a>
  <nav aria-label="メインナビゲーション">
    <ul class="flex items-center gap-3">
      <li><a class="px-3 py-2" href="/blog/">記事一覧</a></li>
      <li><a class="px-3 py-2" href="/about/">About</a></li>
    </ul>
  </nav>
</header>

画面がさらに狭く、ナビゲーションが収まらない場合は、すぐにハンバーガーメニューを追加するのではなく項目数を見直す。二項目程度ならgapと左右paddingを狭めるだけで保てる。操作を隠すUIはJavaScriptとアクセシビリティ対応を増やすため、必要性が明確な場合に限定する。

カード一覧はGridで組む

カード一覧では、複数列の幅と行間を揃え、画面幅に応じて列数を変える。これは二次元配置でありGridが適している。

<section class="card-grid">
  <article class="card">...</article>
  <article class="card">...</article>
  <article class="card">...</article>
</section>
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1.5rem;
}

.card {
  min-width: 0;
  border: 1px solid #e4eaf0;
  border-radius: 1rem;
  background: #fff;
  padding: 1.5rem;
}

auto-fitは入るだけ列を作り、空の列を畳む。minmax(min(100%, 18rem), 1fr)は、列の理想的な最小幅を18remにしつつ、コンテナ自体が18remより狭い場合は100%まで縮める。単純なminmax(18rem, 1fr)では狭い画面で列がはみ出すことがある。

明確なブレークポイントで列数を制御したい場合、TailwindのGridクラスが読みやすい。

<section class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
  <article class="min-w-0 rounded-2xl border border-slate-200 bg-white p-6">
    <h2 class="text-xl font-bold">カードタイトル</h2>
    <p class="mt-3 leading-7 text-slate-600">カードの概要を表示する。</p>
  </article>
</section>

ここでは初期値を一列にし、利用可能な幅が増えたときだけ二列、三列へ広げる。モバイルファーストで書くため、grid-cols-1に接頭辞を付けない。カードへ固定widthを指定せず、Gridのtrackへ幅決定を任せる。

内側と外側で組み合わせる

カード一覧全体をGridにしても、カード内部のメタデータ行はFlexboxが適することが多い。タグ、日付、アイコンは一方向へ並び、長い場合に折り返せればよい。

<article class="grid gap-4 rounded-2xl border border-slate-200 p-6">
  <div class="flex flex-wrap items-center gap-x-3 gap-y-2 text-sm text-slate-500">
    <time datetime="2026-09-07">2026/09/07</time>
    <span class="rounded-md bg-sky-50 px-2 py-1 text-sky-700">CSS</span>
  </div>
  <h2 class="text-xl font-bold">FlexboxとGridの使い分け</h2>
  <p class="leading-7 text-slate-600">レイアウトの主軸から選択する。</p>
</article>

外側がGrid、内側がFlexという構造は自然である。ページ全体をどちらか一方へ統一する必要はない。各コンテナが直接の子要素をどう配置するかだけを判断する。

破綻しやすい指定を避ける

Flexboxの子要素に長いURLやコードがあると、既定の最小幅によってコンテナを押し広げる場合がある。必要な子へmin-width: 0、Tailwindならmin-w-0を置き、テキストにはoverflow-wrap: anywhereなど適切な折り返しを指定する。

Gridでカードの高さを揃えるために固定heightを置くと、文字数やユーザーの文字拡大で内容がはみ出す。Gridの各行は既定で同じtrack内の高さを揃えるため、カード自体は内容に応じて伸ばす。ボタンを下端へ揃えたい場合はカード内部をgridまたはflex flex-colにし、ボタン手前へmargin-top: autoを置く。

絶対配置は通常フローから要素を外すため、主要なレイアウトには使わない。バッジをカード角へ重ねるなど、重なり自体が要件の場面に限定する。画面幅ごとの座標を増やすより、FlexとGridが持つサイズ分配へ任せる方がレスポンシブに保ちやすい。

選択基準は見た目の好みではなく制約である。ナビゲーションのように一列の流れと余白を扱うならFlexbox、カード一覧のように列幅と複数行の整列を扱うならGridを使う。この判断をコンテナごとに繰り返せば、media queryと固定値を増やさずにレイアウトを組み立てられる。