スマートフォン・タブレットからインターネットサーバーオペレーション

APPW.jp
 
CSS NOTES ①

縮まないと、あふれる
min-width:0 と overflow の関係

overflow-x:auto を書いたのに横スクロールにならない。 text-overflow:ellipsis が効かない。 原因はどれも同じでした——その要素に「縮む余地」が無い。 自分のサイトで実際に踏んだ4つのパターンを、仕様に照らして整理します。

overflow min-width flexbox max-content

この記事で扱うこと
  1. overflow は、何を前提にしているか スクロールが生まれる条件
  2. ブロック要素は、そもそも広がらない width:auto の性質
  3. フレックスの子は、縮まない min-width:auto という既定値
  4. overflow-y だけ書くと、横も auto になる 計算値の伝播
  5. width:max-content が要る場合と、無駄な場合 中身がブロックかどうか
  6. まとめの早見表 症状から引く
[ 1 ]

overflow は、何を前提にしているか

overflow-x:auto は「はみ出したらスクロールさせる」指定です。 ここで見落としやすいのが、「はみ出す」には要素の幅が先に決まっている必要があることです。

原則。 スクロールは「箱の幅」と「中身の幅」の差から生まれます。 箱が中身に合わせて広がってしまえば差はゼロになり、 overflow を書いても何も起きません

つまり overflow を効かせるには、その要素が 中身より小さくなれることが前提になります。これを本記事では「縮む余地」と呼びます。

縮む余地が無くなる原因は、大きく2つでした。 ① 要素自身が中身に合わせて広がる② 中身が箱の幅に合わせてしまい、差が生まれない。 順に見ていきます。

[ 2 ]

ブロック要素は、そもそも広がらない

まず安心できる話から。通常のブロック要素は width:auto が既定で、 これは親の内容幅に一致します。中身がどれだけ長くても、箱は広がりません。

これは正しく動く
.box {
  overflow-x: auto;   /* 親幅に収まり、中身がはみ出せばスクロール */
}

ですから「ブロック要素に overflow-x:auto を書いたのに効かない」なら、 原因は別のところにあります。§3以降のどれかです。

ただし予防として min-width:0 は書いておく価値があります。 その要素が将来フレックスやグリッドの子になった瞬間、§3の問題が発生するからです。 ブロックのままなら書いても何も変わりません
[ 3 ]

フレックスの子は、縮まない

ここが本題です。フレックスアイテムの min-width は既定が auto で、 これは「中身の最小幅より小さくならない」という意味になります。

効かない例
.bar { display: flex; }

.bar .path {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* ↑ 3点セットを書いても、省略されない */
}

white-space:nowrap があるので中身の最小幅は一行ぶんの実寸です。 min-width:auto がそれを下限にするため、 箱は縮めず、中身と同じ幅になります。差がゼロなので省略記号も出ません。

効く例
.bar .path {
  min-width: 0;        /* 縮む余地をつくる */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
実際に踏んだ場所
ターミナル風のバーで、パスが省略されずに画面幅を押し広げていました。 overflow:hiddentext-overflow も書いてあるのに効かず、 原因が分かるまで何度も別の場所を疑いました。
グリッドの子も同じです。 display:grid の子要素も min-width:auto になります。 さらに縦方向では min-height:auto が同じ働きをするので、 縦スクロールが効かないときは min-height:0 を疑ってください。
[ 4 ]

overflow-y だけ書くと、横も auto になる

縦だけスクロールさせたい箱を作ったつもりが、横にもスクロールできる状態になっていました。

意図しない横スクロール
.list {
  max-height: 230px;
  overflow-y: auto;
  /* overflow-x は書いていない → visible のつもり */
}
仕様。 overflow-xoverflow-y片方が visible 以外のとき、もう片方の visible計算値として auto に変わります

理由は単純で、片方だけを見えるままにする描画ができないからです。 縦にクリップしながら横だけ枠外へ描く、ということが成立しません。 そこで visibleauto へ倒されます。

両方を明示する
.list {
  max-height: 230px;
  overflow-y: auto;
  overflow-x: clip;   /* 横は塞ぐ、と書く */
}
実際に踏んだ場所
デモの一覧を max-height + overflow-y:auto で作ったところ、 中身に white-space:nowrap の長い行があり、 横方向のスクロール領域が生まれていました。 縦だけのつもりだったので、しばらく気づけませんでした。
[ 5 ]

width:max-content が要る場合と、無駄な場合

箱に縮む余地があっても、まだスクロールしないことがあります。 今度は中身の側に理由があります。

中身がブロック要素だと、差が生まれない

スクロールできない
.wrap { overflow-x: auto; }

.wrap .line {
  /* div などのブロック要素 */
  white-space: pre;   /* 折り返さない */
}

.line はブロックなので width:auto = 親幅です。 中身のテキストは枠外へ描かれますが、要素自体の幅は親と同じ箱と中身の差がゼロなので、スクロール領域が生まれません

中身に実寸を持たせる
.wrap .line {
  width: max-content;   /* 中身の実寸まで広がる */
  min-width: 100%;       /* 短い行でも枠いっぱい */
  white-space: pre;
}

ところが、書いても無駄な場合がある

打ち消される
.wrap pre {
  overflow-x: auto;
  width: max-content;   /* max-width に負けて無効 */
  max-width: 100%;
}
判断の基準。 width:max-content が要るのは、スクロール容器の中に 別のブロック要素があり、それが幅を持てないときだけです。 容器自身が pre のように直接テキストを持つなら不要—— むしろ max-width:100% と競合して混乱のもとになります。
実際に踏んだ場所
div + white-space:pre でログ行を組んだ箇所で、 親に overflow-x:auto を書いても効きませんでした。 逆に pre を使っている箇所へ同じ指定を足したところ、 そちらは最初から不要だったと後で分かりました。
[ デモ ]

5つのパターンを、並べて見る

画面幅360px、中身の実寸900pxという条件で、箱と中身がどう振る舞うかを比べます。

Interactive · shrink & overflow
縮む余地は、あるか
図は縮尺を変えた模式図です。数値は 360px 端末での計算値。
箱(オレンジ)と 中身(赤)
.box
中身

2番目だけ、箱そのものが画面をはみ出しています。 フレックスアイテムが縮めないため、ページ全体の幅を押し広げる—— これが最も見つけにくいパターンでした。

[ 6 ]

まとめの早見表

症状疑うところ対処
overflow-x:auto が効かない中身がブロック要素中身に width:max-content
text-overflow:ellipsis が効かないフレックスの子min-width:0
箱が画面をはみ出すフレックス/グリッドの子min-width:0
意図しない横スクロールoverflow-y だけ指定overflow-x:clip を明示
縦スクロールが効かないフレックスの子(縦方向)min-height:0
max-content を書いても変わらないmax-width:100% と競合そもそも不要な場面

この記事のまとめ

  • スクロールは「箱の幅」と「中身の幅」の差から生まれる。差が無ければ overflow は働かない。
  • ブロック要素は width:auto = 親幅。そもそも広がらないので安全。
  • フレックス/グリッドの子は min-width:auto。中身より小さくなれない。
  • overflow-y だけ指定すると、横の visibleauto に変わる
  • width:max-content中身がブロックのときだけmax-width:100% があると打ち消される。
  • 予防として min-width:0 を書いておくと、後でフレックスの子にしても壊れない。
CSS NOTES シリーズ
  1. 縮まないと、あふれる(この記事)
    min-width:0 と overflow の関係
  2. クリップとスクロールの境界
    hidden / clip / overscroll-behavior
  3. 背景と合成レイヤー
    ::before / fixed / will-change

『縮まないと、あふれる — min-width:0 と overflow の関係』を公開しました。