CSS Tips | 超実務コアCSSテクニック:レイアウト基礎 - position: sticky

Web APP CSS
スポンサーリンク

position: sticky は「スクロールにくっつく、途中まで普通の要素」

position: sticky
「最初は普通に流れの中にいるけど、ある位置までスクロールされたら、そこからは固定される」
という、relative と fixed のハイブリッドみたいな挙動をします。

代表的な使いどころはこうです。

  • ページ上部に“途中から”くっつくヘッダー
  • 長い記事の右側に“追従する”目次
  • セクションごとに“見出しが上に貼り付く”リスト

まずは「いつ、どこに、どうくっつくのか」をイメージで掴みましょう。


基本ルールと「くっつく条件」

sticky が発動するために必要なもの

position: sticky は、次の条件が揃って初めて“くっつき”ます。

  1. position: sticky が指定されている
  2. top(または bottom / left / right)が指定されている
  3. 親要素のスクロール領域の中にいる(親の高さが十分ある)

特に大事なのが 「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>
HTML

stickyposition: sticky;
top-0top: 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>
HTML

sticky 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>
HTML

top: 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>
HTML

sticky 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>
HTML

sticky top-0bg-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;
CSS

Tailwind なら

class="sticky top-0"
HTML

が基本形です。

タイトルとURLをコピーしました