- CSS
CSS overflow使い方|hidden・scroll・auto効かない原因
CSSのoverflowとは、中身がボックスからはみ出したときに「そのまま見せる・隠す・スクロールさせる」のどれにするかを決めるプロパティです。 値は visible・hidden・clip・scroll・auto の5つで、書き方そのものは1行ですが、「横スクロールが出ない」「逆にページ全体が横に動いてしまう」「position: sticky が止まらなくなった」といったつまずきが起こりやすいプロパティでもあります。
この記事では、5つの値を同じボックスに当てて実測した早見表、overflow-x と overflow-y を片方だけ visible にできない仕様の再現、横スクロールの原因を5種類に分けた切り分け手順、原因の要素を見つける確認用コードをまとめました。数値はすべて Chromium 141(Playwright 1.56)で実際に測ったものです。コードはそのまま貼って使える形にしています。
overflowの早見表
幅200px・高さ100px(枠線1px、box-sizing: border-box)の箱に、400×300pxの中身を入れて比べた結果です。
| 値 | はみ出した部分 | スクロールバー | ユーザー操作でスクロール | JSでスクロール(scrollLeft) | 主な用途 |
|---|---|---|---|---|---|
visible(初期値) |
そのまま見える | 出ない | できない | できない(0のまま) | 通常の表示 |
hidden |
隠れる | 出ない | できない | できる(50に変わる) | はみ出しを隠す、角丸の切り抜き |
clip |
隠れる | 出ない | できない | できない(0のまま) | 隠すだけでよいとき、stickyと併用 |
scroll |
隠れる | 常に出る | できる | できる | バーの場所を常に確保したいとき |
auto |
隠れる | はみ出したときだけ出る | できる | できる | スクロール領域の基本形 |
やりたいことから選ぶ場合は次の表が便利です。
| やりたいこと | 書き方 |
|---|---|
| はみ出した部分を隠したい | overflow: hidden; |
| 隠したいが、stickyや片方向のはみ出しは残したい | overflow: clip; または overflow-x: clip; |
| 必要なときだけスクロールバーを出したい | overflow: auto; |
| 横方向だけスクロールさせたい | overflow-x: auto; + 中身を折り返さない指定 |
| 1行で末尾を「…」にしたい | white-space: nowrap; overflow: hidden; text-overflow: ellipsis; |
| スクロールバーの出入りで幅が動くのを防ぎたい | scrollbar-gutter: stable; |
| モーダル内のスクロールを背面に伝えたくない | overscroll-behavior: contain; |
| ページ全体の意図しない横スクロールを直したい | 「原因の要素を見つける確認用コード」で特定してから直す |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS overflow使い方|hidden・scroll・auto効かない原因」
検証した環境
| 項目 | 内容 |
|---|---|
| ブラウザ | Chromium 141.0.7390.37(Playwright 1.56.0 から起動) |
| 画面サイズ | 800×600px(スマホ用コードは 375×667px、タッチ有効) |
| スクロールバー | 幅を取る従来型(15px)と、幅を取らないオーバーレイ型の両方で測定 |
| 未実行のもの | Safari(iOS・macOS)と Firefox での表示。この2つは仕様と一般に知られた挙動として記載 |
スクロールバーには、表示領域の幅を使う「従来型」(Windowsの標準など)と、中身の上に重なって幅を使わない「オーバーレイ型」(スマホやmacOSの標準設定)があります。同じCSSでも結果の数値が変わるため、この記事ではどちらで測ったかを書き添えています。
overflowの基本の書き方
overflow は値を1つ書くと縦横の両方に、2つ書くと「横 縦」の順に当たります。
/* 縦横どちらも同じ値 */
.box { overflow: auto; }
/* 1つ目が横(overflow-x)、2つ目が縦(overflow-y) */
.box { overflow: hidden auto; }
/* 方向ごとに個別に書く */
.box {
overflow-x: auto; /* 横 */
overflow-y: hidden; /* 縦 */
}
/* 書字方向に合わせる論理プロパティ(横書きでは inline が横、block が縦) */
.box {
overflow-inline: auto;
overflow-block: hidden;
}
overflow が働くには「はみ出す先の大きさ」が決まっている必要があります。 高さを指定していないブロックは中身に合わせて伸びるので、縦方向にははみ出しが起こらず、overflow-y: auto を書いてもスクロールバーは出ません。縦スクロールにしたい箱には height か max-height を指定します。
.log {
max-height: 200px; /* これを超えたぶんがスクロール対象になる */
overflow-y: auto;
}
5つの値を同じボックスで比べる
次のHTMLをそのまま保存して開くと、5つの値の違いを並べて確認できます。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<style>
* { box-sizing: border-box; }
.box { width: 200px; height: 100px; border: 1px solid #333; margin: 8px 8px 220px; }
.inner { width: 400px; height: 300px; background: linear-gradient(#fc0, #f60); }
</style>
</head>
<body>
<div class="box" style="overflow: visible"><div class="inner"></div></div>
<div class="box" style="overflow: hidden"><div class="inner"></div></div>
<div class="box" style="overflow: clip"><div class="inner"></div></div>
<div class="box" style="overflow: scroll"><div class="inner"></div></div>
<div class="box" style="overflow: auto"><div class="inner"></div></div>
</body>
</html>
各ボックスに JavaScript で scrollLeft = 50; scrollTop = 40; を代入したあとの値を読み取りました。
実行結果(Chromium 141、従来型スクロールバー):
| 値 | clientWidth×clientHeight | scrollWidth×scrollHeight | スクロールバーの幅 | 代入後の scrollLeft / scrollTop |
|---|---|---|---|---|
visible |
198×98 | 400×300 | 0 | 0 / 0 |
hidden |
198×98 | 400×300 | 0 | 50 / 40 |
clip |
198×98 | 400×300 | 0 | 0 / 0 |
scroll |
183×83 | 400×300 | 15 | 50 / 40 |
auto |
183×83 | 400×300 | 15 | 50 / 40 |
clientWidth は「枠線とスクロールバーを除いた、中身を表示できる幅」、scrollWidth は「はみ出した分も含めた中身全体の幅」です。scrollWidth が clientWidth より大きければ、その箱の中身は横にはみ出しています。 この2つの比較は、あとで出てくる原因の切り分けでも使います。
hiddenとclipの違い
見た目はどちらも「隠す」ですが、hidden はスクロールできる入れ物(スクロールコンテナ)になり、clip はならないという違いがあります。上の結果でも、hidden は JavaScript から scrollLeft を変えられ、clip は0のままでした。
| 比べる点 | hidden |
clip |
|---|---|---|
| スクロールコンテナになる | なる | ならない |
| JSやページ内リンクで中身が動く | 動く | 動かない |
| Tabキーで隠れた入力欄に移動したとき | 中身がスクロールして表示位置がずれることがある | ずれない |
| 新しいブロック整形コンテキストを作る | 作る(floatの回り込み解除に使える) | 作らない |
| 片方向だけの指定 | もう片方が visible なら auto に変わる |
visible と組み合わせられる |
子孫の position: sticky |
この要素が基準になる | 基準にならない(ページのスクロールに付く) |
| はみ出しの余白 | なし | overflow-clip-margin で指定できる |
hidden を指定した箱の中でフォーカスが隠れた要素に移ると、中身がスクロールして表示がずれることがあります。「隠すだけで、スクロールは一切させたくない」場合は clip が合っています。 一方、float の回り込み解除のように整形コンテキストが必要な場面では hidden か display: flow-root を使います。
scrollとautoの違い
中身が収まっている箱(200×100pxの箱に100×50pxの中身)でも測りました。
実行結果(従来型スクロールバー):
| 値 | 中身が収まっているとき | 中身がはみ出したとき |
|---|---|---|
scroll |
clientWidth 183(バーの場所を常に15px確保) | clientWidth 183 |
auto |
clientWidth 198(バーなし) | clientWidth 183(バーが現れて15px狭くなる) |
auto は必要なときだけバーが出るので見た目がすっきりします。その代わり、中身が増えてバーが現れた瞬間に表示幅が15px狭くなり、レイアウトが少し動きます。 これを避けたいときは、後述の scrollbar-gutter: stable を足します。オーバーレイ型スクロールバーの環境では、scroll でも auto でもバーの幅は0でした。
overflow-xとoverflow-yは片方だけvisibleにできない
「横ははみ出して見せたいが、縦は隠したい」と考えて次のように書いても、思ったとおりにはなりません。
<div class="box" style="overflow-x: visible; overflow-y: hidden">
<div class="inner"></div>
</div>
getComputedStyle() で実際に適用された値を読み取った結果です。
| 書いた指定 | 適用された overflow-x | 適用された overflow-y | 横にスクロール |
|---|---|---|---|
overflow-x: visible; overflow-y: hidden |
auto | hidden | できる |
overflow-x: hidden; overflow-y: visible |
hidden | auto | できる |
overflow-x: visible; overflow-y: scroll |
auto | scroll | できる |
overflow-x: visible; overflow-y: clip |
visible | clip | できない(横はそのまま見える) |
overflow-x: clip; overflow-y: hidden |
hidden | hidden | JSからは動く |
片方が visible でも clip でもない値のとき、visible は auto に、clip は hidden に読み替えられます。 これは仕様で決まっている動作で、MDN の overflow のページにも同じ説明があります(今回ページを開いて確認しました)。スクロールコンテナは縦横をまとめて1つの入れ物として扱うため、片方だけ「入れ物の外に見せる」ことができない、と考えると覚えやすいです。
片方向だけ隠したいときの書き方は次のとおりです。
/* 縦だけ隠して、横ははみ出したまま見せる */
.box {
overflow-x: visible;
overflow-y: clip;
}
/* 横だけ隠して、縦(ドロップダウンやツールチップ)は見せる */
.nav {
overflow-x: clip;
overflow-y: visible;
}
visible と組み合わせられるのは clip だけ、と覚えておくと迷いません。
横スクロールが効かない(出ない)原因と直し方
overflow-x: auto を書いたのに横スクロールにならないときは、そもそも中身が横にはみ出していないことがほとんどです。同じ幅200pxの箱で、4通りの書き方を比べました。
<style>
.row { width: 200px; border: 1px solid #333; overflow-x: auto; }
</style>
<!-- 1. 何もしない:折り返して収まる -->
<div class="row">
<span>カード1</span> <span>カード2</span> <span>カード3</span>
<span>カード4</span> <span>カード5</span> <span>カード6</span>
</div>
<!-- 2. 折り返しを止める -->
<div class="row" style="white-space: nowrap">(中身は同じ)</div>
<!-- 3. flex にして子に width だけ指定 -->
<div class="row" style="display: flex">
<div style="width: 120px">A</div><div style="width: 120px">B</div><div style="width: 120px">C</div>
</div>
<!-- 4. flex の子を縮ませない -->
<div class="row" style="display: flex">
<div style="flex: 0 0 120px">A</div><div style="flex: 0 0 120px">B</div><div style="flex: 0 0 120px">C</div>
</div>
実行結果:
| 番号 | 書き方 | clientWidth | scrollWidth | 結果 |
|---|---|---|---|---|
| 1 | 指定なし | 198 | 198 | 2行に折り返して収まる。横スクロールなし |
| 2 | white-space: nowrap |
198 | 364 | 横スクロールになる |
| 3 | flex + 子に width: 120px |
198 | 198 | 子が66pxずつに縮んで収まる。横スクロールなし |
| 4 | flex + 子に flex: 0 0 120px |
198 | 360 | 横スクロールになる |
原因1:中身が折り返して収まっている
文字やインライン要素は、幅が足りなければ次の行へ折り返します。折り返せる中身は横にはみ出さないので、スクロールも起こりません。 1行に並べたいなら white-space: nowrap を足します。
原因2:flexの子要素が縮んでいる
flex の子要素は、初期値が flex-shrink: 1(足りなければ縮む)です。width: 120px と書いても、合計が親を超えると均等に縮められます。flex: 0 0 120px(または flex-shrink: 0)で縮まないようにすると、はみ出してスクロールになります。
原因3:スクロールさせたい箱の幅が決まっていない
overflow-x: auto を書いた箱自体が中身に合わせて広がっていると、箱の中でははみ出しが起こりません。display: inline-block や width: max-content、float を指定した箱、あとで説明する「flex の子要素」がこれに当たります。箱に max-width: 100% や min-width: 0 を足して、幅が親に収まるようにします。
原因4:指定する要素が違う
overflow-x: auto は「はみ出す中身」ではなく「それを包む親」に書きます。 表を横スクロールにしたい場合、table 自体ではなく、包む div に指定します。
<div class="table-wrap">
<table>…</table>
</div>
.table-wrap { overflow-x: auto; max-width: 100%; }
.table-wrap table { min-width: 600px; width: 100%; border-collapse: collapse; }
.table-wrap th, .table-wrap td { white-space: nowrap; padding: 8px; border: 1px solid #ccc; }
幅375pxの画面で測ると、.table-wrap は clientWidth 375・scrollWidth 600 となり、表だけが横に動き、ページ全体の横幅は375pxのまま保たれました。
原因5:親や祖先が先に隠している
スクロールさせたい箱より外側に overflow: hidden があり、そちらで先に切り取られている場合もあります。開発者ツールで親をたどり、overflow が付いている要素を確認します。
横スクロールが出てしまう原因と直し方
今度は逆に、出したくないのにページ全体が横に動く場合です。原因は「画面より幅の広い要素がどこかにある」ことなので、まずその要素を見つけます。
原因の要素を見つける確認用コード
ブラウザの開発者ツールのコンソールに貼って実行します。画面の右端や左端からはみ出している要素を一覧にし、赤い線で囲みます。
(() => {
const w = document.documentElement.clientWidth;
const list = [...document.querySelectorAll('body *')].filter(el => {
const r = el.getBoundingClientRect();
return r.right > w + 0.5 || r.left < -0.5;
});
list.forEach(el => (el.style.outline = '2px solid red'));
console.table(list.map(el => ({
要素: el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') +
(el.className ? '.' + String(el.className).trim().replace(/\s+/g, '.') : ''),
右端: Math.round(el.getBoundingClientRect().right),
幅: Math.round(el.getBoundingClientRect().width),
})));
console.log('画面の幅:', w, '/ ページ全体の幅:', document.documentElement.scrollWidth);
})();
あとで出てくる width: 100vw の要素を置いたページで実行したところ、次のように原因の要素が1つだけ取り出せました(要点のみ)。
要素: div#hero.hero 右端: 800 幅: 800
画面の幅: 785 / ページ全体の幅: 800
一覧に親子が続けて出てきた場合は、いちばん内側(子孫側)の要素が元の原因であることが多いです。CSSだけで手早く見たいときは、次の1行を一時的に足す方法もあります。
* { outline: 1px solid rgba(255, 0, 0, .4); }
outline は大きさに影響しないので、レイアウトを変えずに全要素の輪郭を見られます。
原因A:flexの子要素が縮めない(min-width: auto)
サイドバーと本文を flex で横に並べ、本文の中に長い1行を持つ pre を入れた例です。
<div style="display: flex; width: 300px">
<div style="flex: none; width: 80px">side</div>
<div style="flex: 1">
<pre style="overflow-x: auto; margin: 0">const veryLongLine = "0123456789…(長い1行)";</pre>
</div>
</div>
実行結果(直す前と後):
| 状態 | 本文側の幅 | pre の clientWidth / scrollWidth | 親の scrollWidth(親の幅は約300) |
|---|---|---|---|
直す前(flex: 1 のみ) |
905 | 905 / 905 | 985(親から大きくはみ出す) |
直した後(min-width: 0 を追加) |
218 | 218 / 905 | 298(親に収まり、pre だけが横スクロール) |
flex の子要素は min-width の初期値が auto で、「中身の最小幅より小さくは縮まない」という動きをします。長い1行、折り返さない文字列、大きな画像が中にあると、子要素がその幅まで広がり、親を突き抜けます。pre に overflow-x: auto を書いていても、pre 自身が905pxに広がっているので、はみ出しが起こらずスクロールにもなりません。
/* 直し方:flex の子要素に min-width: 0 */
.main { flex: 1; min-width: 0; }
grid でも同じことが起こります。 grid-template-columns: 80px 1fr では本文側が905pxに広がり、80px minmax(0, 1fr) に変えると220pxに収まりました。1fr は minmax(auto, 1fr) と同じ意味で、最小幅が中身に合わせて決まるためです。
/* 直し方:grid は minmax(0, 1fr) */
.layout { display: grid; grid-template-columns: 80px minmax(0, 1fr); }
原因B:width: 100vw
100vw は「スクロールバーの幅も含めた画面の幅」です。縦スクロールバーが幅を取る環境では、100vw の要素はスクロールバーの分だけ画面より広くなります。
実行結果(800px幅の画面、縦に長いページ、従来型スクロールバー):
| 指定 | 要素の幅 | 表示できる幅(clientWidth) | ページ全体の幅(scrollWidth) | 横スクロール |
|---|---|---|---|---|
width: 100vw |
800 | 785 | 800 | 15px分だけ発生 |
width: 100% |
785 | 785 | 785 | なし |
オーバーレイ型スクロールバーの環境(スマホやmacOSの標準設定)では差が0になるため、作っている端末では気づかず、Windows で初めて横スクロールが出る、という形で見つかることがよくあります。全幅にしたいブロック要素は、width: 100% か、幅を指定しないのが確実です。
原因C:そのほかのよくある原因
| 原因 | 見分け方 | 直し方 |
|---|---|---|
| 画像や動画が親より大きい | 確認用コードで img が出る |
img { max-width: 100%; height: auto; } |
| 長いURLや英数字が折り返さない | 文字を含む要素が出る | overflow-wrap: anywhere; |
マイナスのmarginや left: -◯px |
左端がマイナスの要素が出る | 値を見直す、親に overflow-x: clip |
width 固定+padding |
幅が親+paddingぶん大きい | box-sizing: border-box; |
画面外に待機させたメニュー(translateX(100%)) |
右端が画面幅より大きい | メニューの親か body に overflow-x: clip |
white-space: nowrap の付けっぱなし |
文字要素が出る | 必要な箇所だけに限定する |
原因を直すのが基本ですが、画面外に待機させるメニューのように「はみ出すこと自体が意図どおり」の場合は、overflow-x: clip で切り取るのが扱いやすい方法です。次の節で理由を説明します。
overflow: hiddenでposition: stickyが効かなくなる例
横スクロールを止めるために body やラッパーへ overflow-x: hidden を書いたら、ヘッダーの position: sticky が止まらなくなった、というのは定番のつまずきです。4通りの親で、ページを300pxスクロールしたあとのヘッダーの位置を測りました。
<div style="overflow: hidden"> <!-- ここを4通りに変える -->
<header style="position: sticky; top: 0; height: 40px">ヘッダー</header>
<div style="height: 1200px"></div>
</div>
実行結果:
| 親の指定 | 300pxスクロール後のヘッダーの上端 | 結果 |
|---|---|---|
overflow: hidden |
-300px | 画面外へ流れる(stickyが働かない) |
overflow-x: hidden |
-300px | 画面外へ流れる(stickyが働かない) |
overflow: clip |
0px | 画面上端に止まる |
overflow-x: clip |
0px | 画面上端に止まる |
position: sticky は「いちばん近いスクロールコンテナ」を基準に止まります。hidden を指定した親はスクロールコンテナになるので、sticky の基準がページからその親に切り替わります。ところが親自体はスクロールしないため、止まる動きが起こりません。overflow-x: hidden だけでも、前の節で見たとおり overflow-y が auto に読み替えられ、同じ結果になります。
clip はスクロールコンテナを作らないので、sticky の基準はページのまま保たれます。 横のはみ出しを切り取りつつ sticky も使いたいときは、次の形にします。
/* 直す前 */
.wrapper { overflow-x: hidden; }
/* 直した後 */
.wrapper { overflow-x: clip; }
sticky が働かない原因はほかにもあります(top の指定漏れ、親の高さなど)。ここでは overflow に関係するものだけを取り上げました。
1行で省略する text-overflow: ellipsis
text-overflow: ellipsis は、overflow が visible 以外で、かつ折り返さない1行のときに働きます。3つをセットで書きます。
.ellipsis {
width: 200px; /* 幅が決まっていること */
white-space: nowrap; /* 折り返さない */
overflow: hidden; /* はみ出しを隠す */
text-overflow: ellipsis;/* 末尾を … にする */
}
幅200pxに対して中身の scrollWidth は409pxで、末尾が「…」に置き換わりました。flex の子要素に使う場合は、前述の min-width: 0 も合わせて必要です。複数行の省略は line-clamp を使う別の方法になります。
scrollbar-gutterでスクロールバーの場所を確保する
scrollbar-gutter は、スクロールバーが出ていないときも、その場所を空けておくプロパティです。auto で中身が増えたときの「がたつき」を防げます。
.panel {
overflow: auto;
scrollbar-gutter: stable; /* バーの側に常に場所を確保 */
}
.panel-centered {
overflow: auto;
scrollbar-gutter: stable both-edges; /* 反対側にも同じ幅を確保(中央がずれない) */
}
中身が収まっている幅200pxの箱で測った結果です(従来型スクロールバー)。
| 指定 | clientWidth | 確保された幅 | 中身の開始位置(左端から) |
|---|---|---|---|
overflow: auto のみ |
198 | 0 | 1px |
+ scrollbar-gutter: stable |
183 | 15(右) | 1px |
+ scrollbar-gutter: stable both-edges |
168 | 30(左右15ずつ) | 16px |
overflow: hidden + stable |
183 | 15(右) | 1px |
overflow: hidden でも場所が確保される点が便利です。たとえばモーダルを開く間だけページを overflow: hidden にする場合、html { scrollbar-gutter: stable; } を指定しておくと、スクロールバーが消えても本文の幅が変わりません。オーバーレイ型スクロールバーの環境では、もともと幅を取らないので何も確保されません。
overscroll-behaviorでスクロールの連鎖を止める
モーダルやサイドメニューの中を端までスクロールすると、続けて背面のページが動き出します。これを「スクロールチェーン(連鎖)」と呼びます。overscroll-behavior で止められます。
| 値 | 動き |
|---|---|
auto(初期値) |
端まで行くと、外側の要素やページへスクロールが伝わる |
contain |
外側へ伝えない。端でのバウンドなどその要素内の効果は残る。引っ張って更新やスワイプでの戻る操作も働かなくなる |
none |
外側へ伝えず、端での効果も出さない |
/* モーダルの中身:背面のページへスクロールを伝えない */
.modal-body {
max-height: 80dvh;
overflow-y: auto;
overscroll-behavior: contain;
}
/* 横スクロールの列:端で「戻る」スワイプが誤って働くのを防ぐ */
.scroller {
overflow-x: auto;
overscroll-behavior-x: contain;
}
Chromium 141 では CSS.supports('overscroll-behavior', 'contain') が true を返しました。指で端まで動かしたときの連鎖の有無は、自動テストでは測っていません(未実行)。
スマホ向け横スクロールUIのコピペ用コード
カードを横に並べ、指で滑らせて1枚ずつ止まる列です。JavaScript は使いません。
<div class="scroller" tabindex="0" role="region" aria-label="タスク一覧">
<div class="card">タスク1</div>
<div class="card">タスク2</div>
<div class="card">タスク3</div>
<div class="card">タスク4</div>
<div class="card">タスク5</div>
</div>
* { box-sizing: border-box; }
.scroller {
display: flex;
gap: 12px;
padding: 12px 16px;
overflow-x: auto; /* 横スクロール */
overscroll-behavior-x: contain; /* 端でページの戻る操作に伝えない */
scroll-snap-type: x mandatory; /* カード単位で止める */
scroll-padding-inline: 16px; /* 止まる位置を左余白ぶんずらす */
scrollbar-width: none; /* スクロールバーを隠す(Firefox・新しめの各ブラウザ) */
}
.scroller::-webkit-scrollbar { display: none; } /* スクロールバーを隠す(WebKit系) */
.scroller > .card {
flex: 0 0 70%; /* 縮ませない。次のカードが少し見える幅 */
max-width: 280px;
scroll-snap-align: start;
padding: 16px;
border: 1px solid #ccc;
border-radius: 12px;
background: #fff;
}
実行結果(375×667px、タッチ有効):
| 測った項目 | 値 |
|---|---|
.scroller の clientWidth / scrollWidth |
375 / 1280 |
| カード1枚の幅 | 240px(内側の幅343pxの70%) |
| ページ全体の scrollWidth | 375(ページは横に動かない) |
scrollLeft = 200 を代入したあとの位置 |
252(2枚目の先頭に吸着。240+12) |
ポイントは3つです。(1) 子要素を flex: 0 0 ◯ で縮ませない、(2) 次のカードを少し見せて「横に続きがある」と伝える、(3) スクロールバーを隠すときはキーボードでも操作できるよう tabindex="0" を付ける。 スクロールバーを隠すと続きがあることに気づきにくくなるため、(2) は特に大切です。スナップの細かい調整は scroll-snap の話題になるので、ここでは最小限にとどめます。
ブラウザの対応状況(2026年10月時点)
| 機能 | 状況 | 確認方法 |
|---|---|---|
overflow(visible / hidden / scroll / auto) |
Baseline: Widely available(2015年7月から) | MDN を開いて確認 |
overflow: clip |
Chromium 141 で CSS.supports が true。主要ブラウザはいずれも対応済み(Safari は16から、というのが一般に知られた状況) |
Chromium は実測、ほかは未実行 |
overflow-clip-margin |
Chromium 141 で true。ブラウザにより対応範囲に差がある | Chromium は実測、ほかは未実行 |
scrollbar-gutter |
Baseline 2024(2024年12月から最新の各ブラウザで利用可) | MDN を開いて確認、Chromium は実測 |
overscroll-behavior |
MDN の表示は「Limited availability」(2026年10月3日に閲覧)。Chromium 141 では true | MDN を開いて確認、Chromium は実測 |
scrollbar-width: none |
Chromium 141 で true | Chromium は実測、ほかは未実行 |
scrollbar-gutter や overscroll-behavior は、対応していないブラウザでは単に無視されるだけで、表示が崩れることはありません。足しておいて損のない指定として使えます。
うまくいかないときのチェックリスト
| 症状 | 確認すること | 直し方 |
|---|---|---|
| 縦スクロールが出ない | 箱に高さがあるか | height か max-height を指定 |
| 横スクロールが出ない | scrollWidth > clientWidth か |
white-space: nowrap、子に flex: 0 0 ◯ |
| 横スクロールが出ない | 箱自体が広がっていないか | 箱に max-width: 100%、flex の子なら min-width: 0 |
| ページ全体が横に動く | 確認用コードで要素を特定 | 原因に応じて min-width: 0、width: 100%、max-width: 100% |
overflow-x: visible が効かない |
もう片方が hidden / auto / scroll か | clip と組み合わせる |
| sticky が止まらない | 祖先に hidden / auto / scroll があるか | clip に置き換える |
| ドロップダウンが切れる | 祖先に overflow: hidden があるか |
overflow-x: clip; overflow-y: visible、または親の外に出す |
… にならない |
nowrap・overflow・幅の3点 | 3つをセットで指定、flex の子は min-width: 0 |
| バーの出入りで幅が動く | 従来型スクロールバーか | scrollbar-gutter: stable |
body の overflow: hidden でスクロールできない |
指定が残っていないか | モーダルを閉じるときに外す |
よくある質問
Q. overflow: hidden と overflow: clip はどちらを使えばよいですか?
A. float の回り込み解除や、JavaScript で中身を動かす予定があるなら hidden です。単にはみ出しを隠したいだけなら clip が向いています。clip はスクロールコンテナを作らないので、position: sticky や片方向だけの切り取りと両立できます。
Q. body に overflow-x: hidden を書けば横スクロールは解決しますか?
A. 見た目は止まりますが、はみ出した要素はそのまま残り、sticky が働かなくなるなどの副作用が出ることがあります。まず確認用コードで原因の要素を見つけて直し、それでも切り取りが必要なときに overflow-x: clip を使う、という順番がおすすめです。
Q. overflow: auto と scroll はどちらが基本ですか?
A. 基本は auto です。必要なときだけバーが出ます。バーの出入りで幅が変わるのが気になる場合は、auto に scrollbar-gutter: stable を足すと、scroll のように場所を確保しつつ、不要なバーは描かれません。
Q. スクロールバーを隠してもスクロールはできますか?
A. できます。scrollbar-width: none と ::-webkit-scrollbar { display: none; } はバーの見た目を消すだけで、スクロールの動きは残ります。overflow: hidden は操作によるスクロール自体ができなくなるので、目的に合わせて使い分けます。
まとめ
- overflow の値は5つ。隠すなら
hiddenかclip、スクロールさせるなら基本はauto hiddenはスクロールコンテナになり、clipはならない。sticky や片方向の切り取りにはclipoverflow-xとoverflow-yは片方だけvisibleにできない(autoに読み替えられる)。visibleと組めるのはclipだけ- 横スクロールが出ないときは
scrollWidth > clientWidthを確認し、white-space: nowrapやflex: 0 0 ◯で中身をはみ出させる - ページ全体が横に動くときは確認用コードで要素を特定。flex の子は
min-width: 0、grid はminmax(0, 1fr)、全幅は100vwではなく100% scrollbar-gutter: stableでがたつきを防ぎ、overscroll-behavior: containで連鎖を止める- スマホの横スクロール列は、縮ませない子要素+スナップ+次のカードを少し見せるの3点で作る
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS overflow使い方|hidden・scroll・auto効かない原因」
株式会社RJCでは、CSSのレイアウトやスクロールの仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。