flexアイテムが縮まない・ellipsisが効かない原因はmin-width:auto

flexアイテムが縮まない・ellipsisが効かない原因はmin-width:auto

結論:flexアイテムが縮まないのはflex-shrinkのせいではない。初期値のmin-width:autoが下限を作っている

  • flex/gridアイテムは初期値が min-width:auto =「中身より小さくならない」。flex-shrink:1 でも中身の幅で止まる
  • text-overflow:ellipsis が効かないのはほぼこれ。3点セット(nowrap/overflow:hidden/ellipsis)に min-width:0 を足す
  • 入れ子のときは最深部だけでは効かない。テキストまでの flex の親を全段 min-width:0 にする
  • Gridの 1fr は minmax(auto,1fr) の意味。はみ出すなら minmax(0,1fr) に書き換える
  • flex-shrink:0 はアイコンやボタンなど「縮ませたくない側」だけ。テキスト側に付けると横スクロールの原因になる

「text-overflow: ellipsis を3点セットで正しく書いたのに、テキストが省略されず親からはみ出す」「flex-shrink は初期値の1なのに、アイテムがまったく縮まない」——レイアウトが完成間際で崩れるとき、原因の大半はこの1つの仕様に集約されます。結論から言うと、flexアイテムとgridアイテムは、初期値で「中身の最小幅より小さくならない」よう保護されているからです。この記事では、その下限がどこで生まれるのか、入れ子構造のどの段に min-width: 0 を入れるべきか、Gridの 1fr で同じ事故が起きる理由、そして犯人の段をコンソールで数秒で特定する方法までを、実案件で確認している順番のまま整理します。

flexアイテムの下限は「中身の最小サイズ」

flex-shrink の初期値は 1 なので、アイテムは縮む設定になっています。それでも縮まないのは、別の場所に下限があるからです。flexアイテムの min-width は初期値が auto で、これは 「中身が要求する最小の幅(min-content)より小さくしない」 という意味になります。

通常のブロック要素なら min-width: auto は 0 と同じ扱いですが、flexアイテムとgridアイテムのときだけ挙動が変わる、というのがつまずきの元です。つまり flex: 1(=flex: 1 1 0%)を指定して「基準幅0から伸び縮みする」つもりでも、min-widthの下限だけは残っているため、中の長い文字列や画像が床になってそこで止まります。

横方向のレイアウトなら min-width、flex-direction: column の縦積みなら min-height が同じ働きをします。縦スクロール領域が縮まないときは min-height: 0 を疑ってください。

症状は3つの顔で出る

  • 省略されない:white-space: nowrap と overflow: hidden と text-overflow: ellipsis を書いたのに、「…」にならず1行がそのまま伸びる
  • 親からはみ出す:カードの中のテキストや <pre> が枠の外へ飛び出し、背景色や枠線だけが元の幅で残る
  • ページ全体に横スクロールが出る:狭い画面でだけ右に余白ができ、body に overflow-x: hidden を当てて隠したくなる

3つ目は対症療法で塞ぐと、後からスクロール連動やスティッキーの挙動まで巻き込んで壊れます。ページ全体の横スクロールについては原因の切り分け手順をスマホで横スクロールが出る原因の記事にまとめているので、body側を触る前にそちらで犯人を特定してください。

直し方:min-width:0 を「テキストまでの全段」に入れる

基本形はこれだけです。

.card__body {
  flex: 1;
  min-width: 0; /* これが無いと下の3点セットが効かない */
}
.card__title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

実務でハマるのはここからです。入れ子になっている場合、いちばん内側にだけ min-width: 0 を入れても効きません。 下限は各段のflexアイテムがそれぞれ持っているため、外側の段が中身の幅で止まっていれば、内側をいくら0にしても全体の幅は縮まないからです。

私の案件でも、ヘッダー → ユーザー情報のラッパ → 名前とメールの縦積み、という3段のflexで、最深部の行にだけ min-width: 0 を足して「効かない」と30分溶かしたことがあります。テキストからルート方向へたどって、途中にあるflexアイテムを全段 min-width: 0 にした瞬間に省略が効きました。 崩れているテキストから親を順に見上げ、display: flex の子にあたる要素を漏れなく拾うのが確実です。

共通クラスを1つ用意して、縮めたい列に付けて回る運用にしておくと事故が減ります。

/* 縮むことを許可する列に付ける */
.is-shrinkable { min-width: 0; }
/* 縦積み(column)のスクロール領域用 */
.is-shrinkable-y { min-height: 0; }

Gridの 1fr でも同じ事故が起きる

grid-template-columns: 200px 1fr と書いた列に、折り返せない長いURLやコードブロック、white-space: nowrap のテーブルが入ると、1fr の列が中身の幅まで広がってグリッド全体がはみ出します。 1fr は minmax(auto, 1fr) の短縮であり、この auto が先ほどと同じ下限だからです。

/* はみ出す */
.layout { grid-template-columns: 200px 1fr; }

/* 中身に引きずられない */
.layout { grid-template-columns: 200px minmax(0, 1fr); }

サイドバー+本文の2カラムで、本文に長いURLやコードを載せた瞬間だけレイアウトが崩れる場合は、ほぼこれです。1fr は minmax(0, 1fr) で書くのを既定の書き方にしてしまって構いません。auto-fit や auto-fill の repeat() でも同様に効きます。

犯人の段をコンソールで一発で見つける

目視で祖先をたどるのは骨が折れるので、はみ出している要素を選択した状態($0)でDevToolsのコンソールに貼り、下限を持っている段を一覧で出します。

// はみ出す要素をElementsで選択してから実行
(function (el) {
  const rows = [];
  for (let n = el; n && n !== document.documentElement; n = n.parentElement) {
    const ps = n.parentElement ? getComputedStyle(n.parentElement) : null;
    const isItem = ps && /flex|grid/.test(ps.display);
    if (!isItem) continue;
    const s = getComputedStyle(n);
    rows.push({
      要素: n.tagName.toLowerCase() + (n.className ? '.' + String(n.className).split(' ')[0] : ''),
      親: ps.display,
      minWidth: s.minWidth,
      overflow: s.overflowX,
      要対応: s.minWidth === 'auto' && s.overflowX === 'visible'
    });
  }
  console.table(rows);
})($0);

要対応: true の行がすべて下限を持っている段です。ここに min-width: 0 を入れます。なお overflow が visible 以外(hidden・auto・scroll)の要素は、その時点で下限が 0 相当になるため対応不要です。「スクロール領域を作ったら急に省略が効き出した」のはこの仕様で、逆に言えば overflow: hidden を足すのも正解の一つになります。

実務で踏みやすい周辺の落とし穴

  • flex-shrink: 0 の付け場所を間違える:アイコン・アバター・ボタンなど「潰れてはいけない側」に付けるのが正解です。テキスト側に付けると、狭い画面で必ずはみ出します
  • 画像の max-width: 100% が効かない:画像自身ではなく、画像を包んでいるflexアイテムに下限が残っているケースです。包む側を min-width: 0 にします
  • 日本語は折り返るのに崩れる:原因はほぼ英数の長い連続文字列(URL・ID・トークン)です。全体に word-break: break-all を当てると日本語の途中で切れて読みにくくなるので、overflow-wrap: anywhere を該当箇所に限定して当てます
  • テーブルとコードブロック:折り返せない中身は、縮めるのではなく overflow-x: auto のラッパで包んでその枠内だけ横スクロールさせるのが、レイアウトを壊さない定石です
  • 親の高さが縮まない:flex-direction: column の中でスクロールさせたい領域には min-height: 0。これは画面高を基準にした全画面レイアウトで顕在化するため、100vhとdvhの記事の内容と併せて確認すると早いです

公開前チェックリスト

  • 幅375pxで、長いメールアドレスと長いURLをダミーで流し込んで崩れないか確認した
  • 省略されるべきテキストが「…」で止まり、親の枠を越えていない
  • ページ全体に横スクロールが出ていない(body の overflow-x: hidden で隠していない)
  • flex-shrink: 0 がアイコン・ボタン側にだけ付いている
  • Gridの 1fr が minmax(0, 1fr) になっている
  • ユーザー名やタイトルに実データ級の長さを入れて確認した(短いダミーテキストでは再現しない)

この手の崩れは「CSSの書き方のミス」に見えて、実際はどこまで縮んでよいかを宣言していないだけです。縮ませたい列に min-width: 0、縮ませたくない要素に flex-shrink: 0、Gridは minmax(0, 1fr)——この3つを最初から既定の書き方にしておくと、後から原因を探す時間がまるごと消えます。重なり順の不具合と同じく、症状が出てから数字を足すのではなく、構造の側で決めておくのが結局いちばん速い解法です(重なり順についてはz-indexが効かない理由の記事で同じ切り分け方を扱っています)。

関連記事(コーディング実務Tips)

多言語・ベトナム語サイトの制作でお困りではありませんか?

フォント・文字化け・WordPress多言語化・サーバー選定まで、多言語サイト制作の実績を持つ制作会社が一括対応します。お見積り・ご相談は無料です。

多言語サイト制作の詳細を見る →

すぐに相談したい方は お問い合わせフォームへ

Web制作Tips(コーディング実務)カテゴリの最新記事