タイトル: スクロールの際のサイドバー固定方法「position: sticky」
SEOタイトル: CSS position:sticky でサイドバー固定(動かない時の罠 / IE 対応 / Tailwind / sticky-top)
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><code>position: sticky</code> は <strong>スクロール中は通常配置、閾値を超えると固定</strong>される CSS プロパティ</li><li><strong>必須セット</strong>: <code>position: sticky</code> + <code>top: 0</code>(または bottom/left/right のいずれか)を必ず両方指定</li><li><strong>動かない罠 No.1</strong>: 親要素のいずれかに <code>overflow: hidden / auto / scroll</code> があると無効化される</li><li><strong>動かない罠 No.2</strong>: 親要素に高さがなく、sticky 要素のスクロール余地がない(親 = sticky 要素のサイズ)</li><li>IE 非対応 → <code>position: fixed</code> でフォールバック、または JS で代替(<code>stickybits</code> 等)。Tailwind は <code>sticky top-0</code></li></ul></td></tr></table>
<h2>position: sticky の基本</h2>
<p>CSS の <code>position: sticky</code> は、<strong>スクロール位置が閾値を超えると要素がその位置で固定される</strong>挙動を実現します。サイドバー、ヘッダー、テーブルヘッダー固定によく使われます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">.sidebar {
position: sticky; /* これだけではダメ */
top: 0; /* 必ず top/bottom/left/right のいずれかが必要 */
}
/* ヘッダー固定 */
header {
position: sticky;
top: 0;
z-index: 100;
background: white;
}
/* テーブルヘッダー固定 */
thead th {
position: sticky;
top: 0;
background: #f0f0f0;
}</code></pre>
<h2>基本のレイアウト例(サイドバー固定)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><div class="container">
<main class="content">
<!-- 長い本文 -->
</main>
<aside class="sidebar">
<!-- ナビゲーション -->
</aside>
</div>
<style>
.container {
display: flex;
gap: 24px;
align-items: flex-start; /* これ重要! */
}
.content {
flex: 1;
}
.sidebar {
width: 240px;
position: sticky;
top: 16px; /* 上端から 16px 空ける */
max-height: calc(100vh - 32px);
overflow-y: auto;
}
</style></code></pre>
<h2>動かないときのチェックリスト</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3cd"><th>チェック項目</th><th>確認方法</th><th>修正</th></tr></thead>
<tbody>
<tr><td><code>top / bottom / left / right</code> 未指定</td><td>DevTools で値確認</td><td><code>top: 0</code> を追加</td></tr>
<tr><td>親に <code>overflow: hidden / auto / scroll</code></td><td>親をたどって overflow を確認</td><td>overflow を <code>visible</code> に</td></tr>
<tr><td>親の高さが sticky 要素と同じ</td><td>親に高さ指定があるか</td><td>親に <code>align-items: flex-start</code> や <code>min-height</code></td></tr>
<tr><td>display: flex の影響</td><td>親が flex で要素が伸びている</td><td><code>align-self: flex-start</code></td></tr>
<tr><td>z-index 不足</td><td>固定後、他要素に隠れる</td><td><code>z-index: 100</code> 追加</td></tr>
<tr><td>テーブル <code>thead</code> で効かない</td><td>古いブラウザ仕様</td><td><code>th</code> 個別に sticky 指定</td></tr>
</tbody>
</table>
<h2>罠1: 親要素の overflow</h2>
<p><strong>祖先要素のどこかに <code>overflow: hidden / auto / scroll</code> があると sticky が無効</strong>になります。スクロールコンテナが別になり、ビューポートに対するスクロールを観測できないためです:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><body>
<div style="overflow: hidden;"> <!-- ← これが原因 -->
<div class="sidebar"
style="position: sticky; top: 0;">
サイドバー(動かない)
</div>
</div>
</body>
<!-- 修正: overflow を消すか、別の方法で hidden 効果を得る --></code></pre>
<h2>罠2: 親要素の高さ</h2>
<p>flex レイアウトの場合、デフォルトで全ての子が同じ高さに伸びます。サイドバー自身の高さ = コンテナの高さ となり、スクロールしてもくっつく余地がない:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">/* NG: サイドバーが本文と同じ高さに伸びる */
.container {
display: flex;
}
.sidebar {
position: sticky;
top: 0;
/* スクロールする余地がない */
}
/* OK: コンテナ側で子の伸びを抑制 */
.container {
display: flex;
align-items: flex-start; /* これ */
}
/* または個別に */
.sidebar {
align-self: flex-start;
position: sticky;
top: 0;
}</code></pre>
<h2>テーブルヘッダーの固定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><style>
.table-wrap {
/* overflow を sticky の祖先に置かない! */
height: 400px;
overflow-y: auto; /* これがスクロールコンテナ */
}
.table-wrap th {
position: sticky;
top: 0;
background: #f5f5f5;
z-index: 1;
}
/* 左列も固定したいとき */
.table-wrap td:first-child,
.table-wrap th:first-child {
position: sticky;
left: 0;
background: #fff;
z-index: 2;
}
/* 左上の交点 */
.table-wrap th:first-child {
z-index: 3;
}
</style>
<div class="table-wrap">
<table>
<thead>
<tr><th>項目</th><th>値1</th><th>値2</th></tr>
</thead>
<tbody><!-- 大量の行 --></tbody>
</table>
</div></code></pre>
<h2>scroll-margin と組み合わせる</h2>
<p>sticky ヘッダーがあるとアンカーリンクで「ヘッダーの下に隠れる」問題が起きます。<code>scroll-margin-top</code> で解決:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">header {
position: sticky;
top: 0;
height: 60px;
}
/* アンカーで飛んだとき、ヘッダー高さぶん余白を取る */
h2[id] {
scroll-margin-top: 80px;
}
html {
scroll-behavior: smooth; /* スクロールも滑らかに */
}</code></pre>
<h2>IE / 古いブラウザ対応</h2>
<p>IE は完全非対応。Edge Legacy も限定的。フォールバックパターン:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">.sidebar {
/* IE 用フォールバック */
position: fixed;
top: 0;
width: 240px;
}
/* sticky 対応ブラウザでは上書き */
@supports (position: sticky) {
.sidebar {
position: sticky;
top: 0;
}
}</code></pre>
<p>JS でフォールバックするなら <strong><a href="https://github.com/dollarshaveclub/stickybits" target="_blank" rel="noopener">stickybits</a></strong> が定番。</p>
<h2>Tailwind CSS</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- sticky top-0 で1行 -->
<aside class="sticky top-0 self-start w-64 max-h-screen overflow-y-auto">
サイドバー
</aside>
<!-- 上から 4 (1rem) 開ける -->
<header class="sticky top-0 z-10 bg-white">
ヘッダー
</header></code></pre>
<h2>レスポンシブ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-css">/* PC: 固定サイドバー */
.sidebar {
position: sticky;
top: 16px;
}
/* モバイル: sticky を解除 */
@media (max-width: 768px) {
.sidebar {
position: static;
}
}</code></pre>
<h2>FAQ</h2>
<p><strong>Q: <code>position: -webkit-sticky</code> は今も必要?</strong><br />
A: 2017 年以降のブラウザでは不要。Safari 13 以降は <code>position: sticky</code> 単独で動きます。</p>
<p><strong>Q: 親に overflow が必要だが sticky も使いたい</strong><br />
A: 構造を変える必要があります。<strong>sticky 要素を overflow コンテナの外に移動</strong>するか、内側でスクロールする設計に変更してください。</p>
<p><strong>Q: 複数の sticky を縦に並べたい</strong><br />
A: それぞれに違う <code>top</code> を指定(1 個目 <code>top: 0</code>、2 個目 <code>top: 60px</code> 等)。</p>