overflow-x:autoでスクロールしないときに確認すること

※本記事のコードは参考用です。使用前にご自身で動作確認をお願いします。

ブログ記事でコードブロックや横長のテーブルを表示する際、overflow-x: auto を指定してもスクロールバーが現れず、要素がはみ出してしまう経験をしたことがある方は多いのではないでしょうか。私もまさにその状況に悩まされ、かなりの時間を無駄にしてしまいました。

そこでこの記事では、私のつまずいたポイントとなぜ overflow-x: auto が効かないのか、そしてどう対処すればよいのかを整理しておきます。

確かめること

  • 全体のレイアウトにflexやgridを利用しているか
  • 要素にwidthまたはmin-widthを明示しているか

よくあるレイアウトパターンと問題

例えば私のブログでは、以下のような2カラムのGridレイアウトを使っています。

このとき、左カラム(.main)の中にある overflow-x: auto を指定した要素(例えばテーブルやコードブロック)が横幅を超えると、突き抜けて右カラムを押し出してしまうという現象が起きました。

.wrapper {
    display: grid;
    grid-template-column: auto 240px; /* または 1fr 240px */
}

2カラムのレイアウトだと、flexもよく使いますが、同様です。例えばflex:1を指定している要素の中にスクロール要素が入っている場合、widthの指定が必要です。スクロールさせたい要素が勝手に空気を読んでくれると思っていましたがうまくいきませんでした。また、1fr は「空いているスペースのうち、1単位分を使う」という意味ですが、1fr でも縮みません。

原因

対応させるには、要素の最小サイズを指定することが大切です。

初期値は min-width: auto となりますが、MDNのmin-widthのページでは、autoの値について下記のように書かれています。

auto

The default value. The source of the automatic value for the specified element depends on its display value. For block boxes, inline boxes, inline blocks, and all table layout boxes auto resolves to 0.

For flex items and grid items, the minimum width value is either the specified suggested size, such as the value of the width property, the transferred size, calculated if the element has an aspect-ratio set and the height is a definite size, otherwise, the min-content size is used. If the flex or grid item is a scroll container, or if a grid item spans more than one flexible column track, the automatic minimum size is 0.

min-width - CSS | MDN

flexアイテムやgridアイテムについては、min-widthauto で、auto の意味が特別に定義されていて、widthの値やアスペクト比で高さが固定されている場合は計算されたサイズで、それ以外の場合はmin-contentサイズとなるといったことが説明されています。
min-contentとは、要素の中身が改行せずに収まるための最小サイズで、つまり「内容を収めるために必要な最小幅」を取ろうとします。

そのため、テーブルやコードブロックなどのスクロールさせたい要素の最低限のwidthを確保しようとして縮まないのです。

解決方法

できれば成り行きで自動的に計算してほしいので、要素がきちんと縮むようにmin-widthを明示していきましょう。

minmax(0, 1fr)を指定する

問題を解決するためには、.mainが「0まで縮んでもOK」とブラウザに伝えることが必要です。
minmax(0, 1fr)を指定することで、1fr の部分に「最小0を許可すること」が明示され、中身がどんな幅でも縮んで240pxの列に影響を与えないようになります。

.wrapper {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 240px;
}

子要素にmin-width: 0を指定する

これは、親がminmaxしてなくても、子が自分から縮むことを許すという対処法で、主にコンテンツに長い単語やFlexboxの影響で縮まないときに使うと便利です。これでも同様に、「.main は中身に合わせて広がらず、縮んでよい」という意味になります。

そこで明示的に min-width: 0 を指定することで、縮んでもOKな子要素として振る舞ってくれます。

.main {
  min-width: 0;
}

まとめ

<div class="wrapper">
  <div class="main">長いコンテンツがある部分</div>
  <div class="sidebar">固定サイドバー</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 240px;
  gap: 40px;
}

/* または */
.main {
  min-width: 0;
}

定期的に沼にハマってしまうので、忘れないようにしたいです。

参考

CSS Gridのカラム幅を1frにしたときのワナ!意図せぬ水平スクロールバーが表示されてしまった時の解決方法 | コリス
min-width - CSS | MDN