【CSS】Flexboxで高さが揃わない原因と解決方法|カードレイアウト・実務パターン付き

Flexboxで要素を横並びにしたとき、一部のカードだけ高さが違うという経験はありませんか?

特にカード型レイアウトや料金プランの比較UIなど、実務では「高さを揃えたい」場面が頻繁に出てきます。

この記事では、高さが揃わない原因の特定方法から、基本の解決策、実務で使える応用テクニックまで体系的に解説します。

この記事で分かること

  • align-items: stretch のデフォルト動作と高さ揃えの仕組み
  • 高さが揃わない代表的な7つの原因と解決策
  • カード型レイアウトでの高さ揃えテクニック
  • flex-grow でコンテンツ領域を伸ばす方法
  • ネストしたFlexboxでの高さ揃え
  • CSS Grid での代替アプローチ
  • フッターをカード下部に固定する実装
  • 実務パターン(商品カード・記事カード・料金プラン)
スポンサーリンク
  1. なぜ高さが揃わないのか
    1. align-items: stretch がデフォルト
    2. 高さが揃わなくなる主な原因
  2. align-items: stretch で高さを揃える(基本解決)
    1. 基本パターン
    2. align-items を変更してしまっている場合
  3. 子要素の height / min-height が原因のケース
    1. height指定が stretch を無効にする
    2. min-height は stretch と共存できる
  4. カード型レイアウトでの高さ揃え
    1. 基本のカードレイアウト
  5. flex-grow でコンテンツ領域を伸ばす
  6. ネストしたFlexboxでの高さ揃え
    1. 問題:外側は揃うが内側のカードが揃わない
    2. 解決:各階層でFlexboxを設定する
  7. CSS Grid での代替アプローチ
    1. grid-auto-rows で行の高さを統一
    2. grid-auto-rows: 1fr で全行統一
    3. Flexbox vs CSS Grid の使い分け
  8. Flexbox + overflow での対処
    1. テキストが溢れる場合の処理
    2. テキストの行数を制限する(-webkit-line-clamp)
  9. フッターを下部に固定する(カード内レイアウト)
    1. margin-top: auto を使う方法
    2. flex-grow との違い
  10. レスポンシブ対応での注意点
    1. flex-wrap: wrap の高さ問題
    2. 解決策:CSS Gridに切り替える
    3. 縦並びに切り替える場合の注意
  11. よくある失敗パターンと解決策
    1. 失敗①:画像の高さがバラバラ
    2. 失敗②:align-self を個別に指定している
    3. 失敗③:子要素に100%以外のheightを指定
    4. 失敗④:CSSフレームワークとの競合
  12. 実務パターン集
    1. パターン①:商品カード
    2. パターン②:記事カード(ブログ)
    3. パターン③:料金プラン
  13. まとめ
    1. 関連記事

なぜ高さが揃わないのか

まず、Flexboxの高さに関するデフォルト動作を正確に理解しましょう。

align-items: stretch がデフォルト

Flexコンテナのalign-itemsプロパティは、デフォルトでstretchが適用されています。つまり、何も指定しなくても子要素の高さは自動的に揃うのが本来の動作です。

CSS – デフォルトの動作
/* Flexコンテナのデフォルト値 */
.container {
    display: flex;
    align-items: stretch; /* デフォルト値 - 明示不要 */
}

この状態では、最も高さのある子要素に合わせて他の子要素も伸びます。

ポイント:「高さが揃わない」のは、何かがstretch動作を打ち消しているからです。原因を特定することが解決の第一歩になります。

高さが揃わなくなる主な原因

原因 説明
align-itemsの変更 center / flex-start 等に変更している
align-selfの指定 子要素個別にstretch以外を指定
heightの明示指定 子要素にheightやmax-heightを設定
内側のコンテンツ量 テキスト量や画像サイズの違い
flex-wrap: wrap 折り返し時に行ごとの高さが異なる
min-heightの指定 最小高さが固定されている
overflowの影響 overflowがhiddenやscrollで高さが制限される

align-items: stretch で高さを揃える(基本解決)

最もシンプルな解決方法は、align-items: stretchがデフォルトのまま残っていることを確認し、それを阻害する要因を取り除くことです。

基本パターン

HTML
<div class="container">
    <div class="item">短いテキスト</div>
    <div class="item">長いテキストが入る要素。<br>複数行になります。<br>3行目もあります。</div>
    <div class="item">中くらいのテキスト</div>
</div>
CSS – 高さが揃う基本パターン
.container {
    display: flex;
    gap: 16px;
    /* align-items: stretch はデフォルトなので省略可 */
}

.item {
    flex: 1;
    padding: 16px;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    /* height を指定しない! */
}

この状態では、最も高さのある2番目の要素に合わせて、1番目・3番目の要素も同じ高さになります。

align-items を変更してしまっている場合

もしalign-items: centeralign-items: flex-startを指定していると、子要素はコンテンツの高さに縮まります。

CSS – 高さが揃わなくなる例
/* NG:高さが揃わない */
.container {
    display: flex;
    align-items: center; /* stretchではなくなった */
}

/* OK:高さが揃う */
.container {
    display: flex;
    align-items: stretch; /* 明示的に戻す */
}

注意:CSSフレームワーク(Bootstrap, Tailwind等)がデフォルトでalign-itemsを変更している場合があります。DevToolsで実際に適用されている値を確認してください。

子要素の height / min-height が原因のケース

align-items: stretchが効くのは、子要素にheight(高さ)が明示的に指定されていないときだけです。

height指定が stretch を無効にする

CSS – heightがstretchを打ち消す
/* NG:heightが指定されているとstretchが効かない */
.item {
    height: 100px; /* この指定がstretchを無効にする */
}

/* NG:max-heightも高さを制限する */
.item {
    max-height: 200px; /* 200pxより伸びなくなる */
}

/* OK:heightを削除すればstretchが効く */
.item {
    /* heightは指定しない */
    padding: 16px;
}

min-height は stretch と共存できる

min-heightは「最低限の高さ」を保証するだけなので、stretchと組み合わせて使えます。

CSS – min-heightとstretchの共存
.item {
    min-height: 80px;  /* 最低80px、それ以上は伸びる */
    /* height は指定しない */
}
プロパティ stretchとの関係 結果
height stretchを無効にする 高さが揃わない
max-height 上限で制限する 上限を超えると揃わない
min-height 共存できる 最低高さ以上で揃う
指定なし stretchが完全に効く 高さが揃う

カード型レイアウトでの高さ揃え

実務で最も多いのが、カード型レイアウトで高さを揃えたいというケースです。

基本のカードレイアウト

HTML – カードレイアウト
<div class="card-container">
    <div class="card">
        <img src="image1.jpg" alt="">
        <h3>タイトル</h3>
        <p>短い説明文</p>
    </div>
    <div class="card">
        <img src="image2.jpg" alt="">
        <h3>長いタイトルが入る場合</h3>
        <p>長い説明文が入ります。複数行にわたるテキストで高さが変わります。</p>
    </div>
    <div class="card">
        <img src="image3.jpg" alt="">
        <h3>タイトル</h3>
        <p>中くらいの説明文</p>
    </div>
</div>
CSS – カードの高さを揃える
.card-container {
    display: flex;
    gap: 20px;
    /* align-items: stretch がデフォルトで適用される */
}

.card {
    flex: 1;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    /* heightは指定しない */
}

.card img {
    width: 100%;
    height: 180px;
    object-fit: cover;
}

.card h3,
.card p {
    padding: 0 16px;
}

flex: 1で幅を均等にし、heightを指定しないことで、stretchにより全カードが同じ高さになります。

flex-grow でコンテンツ領域を伸ばす

カードの高さが揃っても、カード内のテキスト領域が均等に伸びないケースがあります。この場合、カード自体もFlexboxにして、伸ばしたい領域にflex-grow: 1を指定します。

CSS – flex-growでコンテンツ領域を伸ばす
.card {
    flex: 1;
    display: flex;
    flex-direction: column; /* カード内を縦並びに */
}

.card-body {
    flex-grow: 1; /* この領域が残りスペースを埋める */
    padding: 16px;
}

.card-footer {
    padding: 12px 16px;
    border-top: 1px solid #e2e8f0;
}

flex-grow の仕組み

  • flex-grow: 0(デフォルト): コンテンツの高さのまま
  • flex-grow: 1: 親要素の残りスペースをすべて占める
  • 複数の子要素にflex-growを指定すると、比率に応じて分配される

ネストしたFlexboxでの高さ揃え

Flexboxをネスト(入れ子)にすると、外側の高さ揃えが内側まで伝播しないことがあります。

問題:外側は揃うが内側のカードが揃わない

HTML – ネストしたFlexbox
<div class="outer">
    <div class="column">
        <div class="inner-card">カードA</div>
        <div class="inner-card">カードB</div>
    </div>
    <div class="column">
        <div class="inner-card">カードC(長いコンテンツ)</div>
        <div class="inner-card">カードD</div>
    </div>
</div>

解決:各階層でFlexboxを設定する

CSS – ネストしたFlexboxの高さ揃え
/* 外側:カラムの高さを揃える */
.outer {
    display: flex;
    gap: 20px;
    /* align-items: stretch でカラムの高さが揃う */
}

/* 中間:カラム自体もFlexboxにする */
.column {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* 内側:カードが均等に伸びる */
.inner-card {
    flex: 1; /* 残りスペースを均等に分配 */
    padding: 16px;
    background: #fff;
    border-radius: 8px;
}

ポイント:ネストしたFlexboxで高さを揃えるには、「外側→中間→内側」のすべての階層でFlexboxの設定が必要です。中間の.columndisplay: flexにし忘れると、内側のカードの高さは揃いません。

CSS Grid での代替アプローチ

FlexboxではなくCSS Gridを使うと、より簡潔に高さを揃えられるケースがあります。

grid-auto-rows で行の高さを統一

CSS – CSS Gridで高さを揃える
.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    /* グリッドは同じ行のアイテムを自動的に同じ高さにする */
}

CSS Gridでは、同じ行に配置されたアイテムは自動的に同じ高さになります。Flexboxのようにalign-itemsを気にする必要がありません。

grid-auto-rows: 1fr で全行統一

複数行にまたがる場合、各行で高さが変わることがあります。grid-auto-rows: 1frを使うと、すべての行を同じ高さにできます。

CSS – 全行の高さを統一
.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 1fr; /* すべての行を同じ高さに */
    gap: 20px;
}

Flexbox vs CSS Grid の使い分け

比較項目 Flexbox CSS Grid
高さ揃えの仕組み align-items: stretch 自動(同じ行は同じ高さ)
複数行での高さ揃え 行ごとに高さが異なる grid-auto-rows: 1frで統一可
列数の固定 flex-basisやwidthで調整 grid-template-columnsで簡単
向いている場面 1行の横並び、可変数 複数行のグリッドレイアウト
ブラウザ対応 IE11一部対応 IE11非対応

Flexbox + overflow での対処

カード内のコンテンツが多すぎて溢れる場合、overflowプロパティで制御しながら高さを揃える方法があります。

テキストが溢れる場合の処理

CSS – overflow制御付きの高さ揃え
.card-container {
    display: flex;
    gap: 20px;
}

.card {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.card-text {
    flex-grow: 1;
    overflow-y: auto; /* テキストが多い場合はスクロール */
    padding: 16px;
}

注意:overflow: hiddenをカード自体に付けると、box-shadowが切れたり、ホバーエフェクトが欠けたりすることがあります。overflowは溢れる可能性のある内部要素にだけ指定しましょう。

テキストの行数を制限する(-webkit-line-clamp)

CSS – 行数制限で高さを統一
.card-description {
    display: -webkit-box;
    -webkit-line-clamp: 3; /* 最大3行 */
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.6;
}

行数を制限することで、コンテンツ量に関係なくテキスト領域の高さを揃えることも可能です。ただし、テキストが途中で切れるため「続きを読む」リンクと組み合わせるのが一般的です。

フッターを下部に固定する(カード内レイアウト)

高さが揃ったカードレイアウトで、もうひとつよくある要望が「ボタンやリンクをカードの一番下に固定したい」というケースです。

margin-top: auto を使う方法

HTML – フッター固定のカード
<div class="card-container">
    <div class="card">
        <h3>プランA</h3>
        <p>短い説明</p>
        <a href="#" class="card-btn">詳細を見る</a>
    </div>
    <div class="card">
        <h3>プランB</h3>
        <p>長い説明文が入ります。複数行になって高さが変わります。</p>
        <a href="#" class="card-btn">詳細を見る</a>
    </div>
</div>
CSS – margin-top: auto でフッター固定
.card-container {
    display: flex;
    gap: 20px;
}

.card {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 24px;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-btn {
    margin-top: auto; /* 残りスペースを上マージンに */
    display: inline-block;
    padding: 10px 20px;
    background: #0284c7;
    color: #fff;
    border-radius: 8px;
    text-align: center;
    text-decoration: none;
}

margin-top: auto が効く仕組み

  • Flexboxでは、autoマージンは残りの空きスペースをすべて吸収します
  • margin-top: autoは「上方向のスペースを最大限取る」= 要素を一番下に押し下げます
  • これはflex-growを使う方法よりもHTML構造を変えずに実装できるメリットがあります

flex-grow との違い

方法 仕組み 使い分け
margin-top: auto スペースをマージンに変換 ボタンを下に押し下げるだけ
flex-grow: 1 要素自体が伸びる テキスト領域を広げたいとき

レスポンシブ対応での注意点

レスポンシブデザインでは、画面幅に応じてFlexboxの方向やラップを変更しますが、そのときに高さ揃えが崩れることがあります。

flex-wrap: wrap の高さ問題

flex-wrap: wrapを使うと、折り返された行ごとに高さが計算されるため、行をまたいだ高さ揃えはできません

CSS – flex-wrapでの高さ問題
/* 行をまたいだ高さ揃えは不可能 */
.container {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
}

.item {
    flex: 0 0 calc(33.333% - 14px);
    /* 同じ行の要素は揃うが、行をまたぐと揃わない */
}

解決策:CSS Gridに切り替える

行をまたいだ高さ揃えが必要な場合は、CSS Gridのgrid-auto-rows: 1frを使いましょう。

CSS – レスポンシブ対応のGridレイアウト
.card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 1fr;
    gap: 20px;
}

/* タブレット */
@media (max-width: 768px) {
    .card-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* スマホ */
@media (max-width: 480px) {
    .card-grid {
        grid-template-columns: 1fr; /* 1列表示 */
        grid-auto-rows: auto; /* 1列なら揃え不要 */
    }
}

縦並びに切り替える場合の注意

CSS – レスポンシブでの方向切り替え
.container {
    display: flex;
    gap: 20px;
}

@media (max-width: 768px) {
    .container {
        flex-direction: column;
        /* 縦並びでは align-items: stretch は「幅」を揃える */
        /* 高さはコンテンツに依存する(これが正しい動作) */
    }
}

ポイント:flex-direction: columnに切り替わると、主軸が縦方向になります。このときalign-items: stretch幅方向に作用するため、高さ揃えとは無関係になります。縦並び時は各要素がコンテンツに応じた高さになるのが自然な動作です。

よくある失敗パターンと解決策

実務でハマりやすいパターンをまとめます。

失敗①:画像の高さがバラバラ

CSS – 画像の高さを揃える
/* NG:アスペクト比が異なる画像でカードの高さがバラバラ */
.card img {
    width: 100%;
    /* height未指定 → 画像本来のアスペクト比で表示 */
}

/* OK:高さを固定してobject-fitでトリミング */
.card img {
    width: 100%;
    height: 200px;
    object-fit: cover; /* 比率を保ちつつ領域を埋める */
}

/* OK:aspect-ratioで比率を統一(モダンブラウザ) */
.card img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
}

失敗②:align-self を個別に指定している

CSS – align-selfの落とし穴
/* NG:特定の要素だけ高さが揃わない */
.item:nth-child(2) {
    align-self: flex-start; /* この要素だけstretchが解除される */
}

/* OK:align-selfを削除またはstretchに */
.item:nth-child(2) {
    align-self: stretch; /* 明示的にstretchを指定 */
}

失敗③:子要素に100%以外のheightを指定

CSS – heightの指定ミス
/* NG:heightがstretchを打ち消す */
.card {
    height: fit-content; /* コンテンツの高さに固定 → 揃わない */
}

/* NG:パーセント指定も注意 */
.card {
    height: 50%; /* 親の50%に固定 → 揃わない */
}

/* OK:heightを指定しない */
.card {
    /* height は削除する */
}

失敗④:CSSフレームワークとの競合

CSS – フレームワーク競合の解決
/* Bootstrapの .row は align-items が変更される場合がある */
/* DevToolsで確認し、必要に応じてオーバーライド */
.row.equal-height {
    align-items: stretch;
}

/* Tailwind CSSの場合 */
/* items-stretch クラスを追加する */
/* <div class="flex items-stretch gap-4"> */

実務パターン集

最後に、実務でそのまま使えるパターンを3つ紹介します。

パターン①:商品カード

CSS – 商品カード(高さ揃え + フッター固定)
.product-list {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
}

.product-card {
    flex: 0 0 calc(33.333% - 16px);
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    transition: box-shadow 0.2s;
}

.product-card:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

.product-card img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.product-info {
    flex-grow: 1; /* テキスト領域が伸びる */
    padding: 16px;
}

.product-price {
    padding: 12px 16px;
    border-top: 1px solid #e2e8f0;
    font-weight: 700;
    color: #dc2626;
}

パターン②:記事カード(ブログ)

CSS – 記事カード(高さ揃え + 行数制限)
.article-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 24px;
}

.article-card {
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
}

.article-card img {
    width: 100%;
    height: 180px;
    object-fit: cover;
}

.article-body {
    flex-grow: 1;
    padding: 16px;
}

.article-excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    color: #64748b;
    line-height: 1.6;
}

.article-meta {
    margin-top: auto;
    padding: 12px 16px;
    border-top: 1px solid #f1f5f9;
    font-size: 13px;
    color: #94a3b8;
}

パターン③:料金プラン

CSS – 料金プラン(高さ揃え + 推奨プラン強調)
.pricing-container {
    display: flex;
    gap: 24px;
    align-items: stretch; /* 明示的に指定 */
    max-width: 960px;
    margin: 0 auto;
}

.pricing-card {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 32px 24px;
    background: #fff;
    border: 2px solid #e2e8f0;
    border-radius: 16px;
    text-align: center;
}

/* 推奨プランの強調 */
.pricing-card.recommended {
    border-color: #0284c7;
    box-shadow: 0 8px 24px rgba(2, 132, 199, 0.15);
    position: relative;
}

.pricing-features {
    flex-grow: 1; /* 機能一覧が残りスペースを埋める */
    text-align: left;
    list-style: none;
    padding: 0;
    margin: 24px 0;
}

.pricing-features li {
    padding: 8px 0;
    border-bottom: 1px solid #f1f5f9;
}

.pricing-btn {
    margin-top: auto; /* ボタンを一番下に固定 */
    padding: 14px;
    background: #0284c7;
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
}

まとめ

Flexboxで高さが揃わない問題の原因と解決策を整理します。

状況 解決策 プロパティ
基本の高さ揃え align-items: stretchを維持 デフォルトのまま
heightが邪魔をしている heightを削除する min-heightなら共存可
カード内の領域を伸ばしたい flex-grow: 1を使う flex-direction: column
フッターを下に固定 margin-top: autoを使う またはflex-grow
行をまたいだ高さ揃え CSS Gridに切り替え grid-auto-rows: 1fr
画像の高さがバラバラ object-fitで統一 height固定 + object-fit: cover
フレームワークとの競合 DevToolsで確認して上書き align-items: stretch

最も重要なポイントは、Flexboxはデフォルトで高さが揃うということです。揃わない場合は「何かがstretchを阻害している」ので、DevToolsでalign-itemsと子要素のheightを確認するのが解決の近道です。

カードレイアウトでは、外側の高さ揃え(align-items: stretch)に加えて、内側の構造(flex-direction: column + flex-grow)を設計することで、美しく整ったUIを実現できます。

関連記事