position: sticky が効かない時に祖先の overflow を疑え
2026-04-26
#CSS
#Next.js
#デバッグ
position: sticky の罠で半日溶かしたので 共有する。
症状:
.sidebar { position: sticky; top: 130px; }を設定- でもスクロール時に 追従しない(普通の relative と同じ振る舞い)
- DevTools で sticky の指定は効いている(rule が適用されている)
最初に試した 対応(全部ハズレ):
align-self: startを付けるmax-height: calc(100vh - 150px)で内部スクロール化する- inner wrapper を sticky にしてみる
- 親 grid container の
align-items: startを変えてみる
どれも効かない。
真因は祖先要素の overflow: hidden だった。具体的には Next.js の app/layout.tsx で:
<main style={{ overflowX: "hidden" }}>
overflow-x: hidden でも scroll container を作る。CSS spec 上、position: sticky は最も近い scroll container の範囲内でしか追従しない。<main> が scroll container 化していると、その中の sticky は <main> の高さ範囲内でしか効かず、ページスクロール全体には追従しない。
修正は 1 行:
<main style={{ overflowX: "clip" }}>
overflow: clip は overflow: hidden と違って scroll container を作らない(プログラマティックスクロールも禁止)。横はみ出し防止には clip を使う。
検証手順:
# 祖先要素を全部洗って overflow を確認
grep -rE 'overflow[^:]*:\s*(hidden|scroll|auto)' app/ components/
inline style の見落としに注意。CSS だけ grep していると、tsx の style={{}} で書かれた overflow が抜ける。全.tsx に対して overflowX:|overflowY:|overflow: も grep する のが正しい。
今後 sticky で詰まったら、まずこのチェックから始める。