position: sticky は「スクロールにくっつく、途中まで普通の要素」
position: sticky は
「最初は普通に流れの中にいるけど、ある位置までスクロールされたら、そこからは固定される」
という、relative と fixed のハイブリッドみたいな挙動をします。
代表的な使いどころはこうです。
- ページ上部に“途中から”くっつくヘッダー
- 長い記事の右側に“追従する”目次
- セクションごとに“見出しが上に貼り付く”リスト
まずは「いつ、どこに、どうくっつくのか」をイメージで掴みましょう。
基本ルールと「くっつく条件」
sticky が発動するために必要なもの
position: sticky は、次の条件が揃って初めて“くっつき”ます。
position: stickyが指定されているtop(またはbottom/left/right)が指定されている- 親要素のスクロール領域の中にいる(親の高さが十分ある)
特に大事なのが 「top を必ず指定する」 ことです。position: sticky; だけでは何も起きません。
一番シンプルな sticky の例
.sticky-header {
position: sticky;
top: 0;
background: #0f172a;
color: #e5e7eb;
padding: 8px 16px;
}
CSS<div class="sticky-header">
セクションの見出し
</div>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
HTMLスクロールして、この見出しが画面上部に到達した瞬間から、top: 0 の位置に“くっついて”動かなくなります。
さらにスクロールして、この sticky 要素の親コンテナの終わりを超えると、
また普通に流れていきます。
Tailwind での最小例
<div class="sticky top-0 bg-slate-900 text-slate-100 px-4 py-2">
セクションの見出し
</div>
HTMLsticky が position: sticky;top-0 が top: 0; です。
例1:ページ上部に「セクション内だけ」くっつく見出し
CSS 版
.section {
border: 1px solid #e5e7eb;
margin-bottom: 32px;
padding: 16px;
}
.section-title {
position: sticky;
top: 0;
background: #f9fafb;
padding: 8px 12px;
font-weight: 600;
z-index: 10;
}
CSS<section class="section">
<h2 class="section-title">セクションA</h2>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
</section>
<section class="section">
<h2 class="section-title">セクションB</h2>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
</section>
HTMLスクロールすると、
セクションAの見出しが上にくっつき、
さらに下に進むと、セクションBの見出しがAを押し出すように入れ替わります。
深掘りポイント
sticky は「ページ全体」ではなく「親要素の中」で効きます。
親のボックスを出たら、くっつきは解除されるイメージです。
Tailwind 版
<section class="border border-gray-200 mb-8 p-4">
<h2 class="sticky top-0 bg-gray-50 px-3 py-2 font-semibold z-10">
セクションA
</h2>
<p class="mt-4">長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
<p>長いテキスト…</p>
</section>
HTMLsticky top-0 に加えて、
背景色を付けておくと、下のテキストと重なったときも読みやすくなります。
例2:右側に「追従する目次」を作る
レイアウトのイメージ
左:記事本文(長い)
右:目次(スクロールしてもある程度ついてくる)
CSS 版
.layout {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
gap: 24px;
align-items: flex-start;
}
.toc {
position: sticky;
top: 80px;
background: #f9fafb;
padding: 12px 16px;
border-radius: 8px;
border: 1px solid #e5e7eb;
}
CSS<div class="layout">
<article>
<h1>記事タイトル</h1>
<p>長い本文…</p>
<p>長い本文…</p>
<p>長い本文…</p>
<p>長い本文…</p>
<p>長い本文…</p>
</article>
<aside class="toc">
<strong>目次</strong>
<p>・見出し1</p>
<p>・見出し2</p>
<p>・見出し3</p>
</aside>
</div>
HTMLtop: 80px; にしているのは、
上に固定ヘッダーなどがある場合に「少し下に余裕を持たせる」ためです。
Tailwind 版
<div class="grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)] gap-6 items-start">
<article>
<h1 class="text-2xl font-bold mb-4">記事タイトル</h1>
<p class="mb-4">長い本文…</p>
<p class="mb-4">長い本文…</p>
<p class="mb-4">長い本文…</p>
<p class="mb-4">長い本文…</p>
<p class="mb-4">長い本文…</p>
</article>
<aside class="sticky top-20 bg-gray-50 border border-gray-200 rounded-lg p-4">
<p class="font-semibold mb-2">目次</p>
<p class="text-sm text-gray-600">・見出し1</p>
<p class="text-sm text-gray-600">・見出し2</p>
<p class="text-sm text-gray-600">・見出し3</p>
</aside>
</div>
HTMLsticky top-20 で「上から少し余裕を空けて追従する目次」を作っています。
例3:テーブルヘッダーをスクロール中も見えるようにする
CSS 版
.table-wrapper {
max-height: 300px;
overflow: auto;
border: 1px solid #e5e7eb;
}
.table-wrapper thead th {
position: sticky;
top: 0;
background: #f9fafb;
z-index: 5;
}
CSS<div class="table-wrapper">
<table>
<thead>
<tr>
<th>名前</th>
<th>メール</th>
<th>ステータス</th>
</tr>
</thead>
<tbody>
<tr><td>…</td><td>…</td><td>…</td></tr>
<!-- 行がたくさん続く -->
</tbody>
</table>
</div>
HTMLここでは「画面」ではなく「スクロール可能な親(.table-wrapper)」を基準に sticky が効きます。
ヘッダー行がスクロールしても常に上に残るので、長いテーブルでも見やすくなります。
Tailwind 版
<div class="max-h-72 overflow-auto border border-gray-200">
<table class="min-w-full text-sm">
<thead>
<tr class="bg-gray-50">
<th class="sticky top-0 bg-gray-50 px-3 py-2 text-left">名前</th>
<th class="sticky top-0 bg-gray-50 px-3 py-2 text-left">メール</th>
<th class="sticky top-0 bg-gray-50 px-3 py-2 text-left">ステータス</th>
</tr>
</thead>
<tbody>
<tr class="border-t">
<td class="px-3 py-2">山田太郎</td>
<td class="px-3 py-2">taro@example.com</td>
<td class="px-3 py-2">有効</td>
</tr>
<!-- 行が続く -->
</tbody>
</table>
</div>
HTMLsticky top-0 と bg-gray-50 をセットにすることで、
ヘッダーがスクロール中も背景付きで読みやすくなります。
position: sticky が「効かない」ときに疑うポイント
親要素に overflow: hidden / auto / scroll があるか
sticky は「スクロールコンテナ」を基準に動きます。
親に overflow: hidden などがあると、その親がスクロールコンテナになり、
意図しないところで sticky が止まったり、そもそも発動しなかったりします。
特に
- 親に
overflow: hiddenが付いている - 高さが足りず、スクロールがほとんど発生していない
このあたりは sticky が効かない原因としてよくあります。
top を指定していない
position: sticky; だけでは何も起きません。
必ず top(または bottom / left / right)をセットで指定します。
Tailwind なら sticky top-0 のように、
「sticky と top のクラスをセットで書く」のを癖にするとミスが減ります。
sticky を設計するときの考え方(ここが一番大事)
fixed ではなく sticky を選ぶ理由を意識する
fixed
「ページ全体に対して、常に画面に貼り付けておきたいもの」
例:グローバルヘッダー、フッターバー、右下ボタン
sticky
「あるコンテナの中だけで、スクロールに追従してほしいもの」
例:記事の目次、セクション見出し、テーブルヘッダー
「どこまで追従してほしいか」が fixed と sticky の分かれ目です。
Tailwind では「sticky top-◯」をワンセットで覚える
よく使うのはこのあたりです。
class="sticky top-0"
class="sticky top-16"
class="sticky top-20"
HTML上に固定ヘッダーがある場合は、その高さ分だけ top を空けると、
ヘッダーと重ならずにきれいに並びます。
まとめ
position: sticky は
- 最初は普通に流れの中にいる
- スクロールで指定位置に達したら、そこからは固定される
- 親コンテナの中だけで効く(親を出たら解除される)
という「relative と fixed のいいとこ取り」のような挙動をします。
CSS なら
position: sticky;
top: 0;
CSSTailwind なら
class="sticky top-0"
HTMLが基本形です。


