スティッキーヘッダー(Sticky Header)— スクロール追従ナビ

ナビゲーション 中級

このコンポーネントについて

スティッキーヘッダーは、ページをスクロールしてもナビゲーションバーが画面上部に固定表示されるUIパターンです。ユーザーがどこまでスクロールしても主要なナビゲーションにすぐアクセスできるため、コンテンツが長いページやブログ・ECサイト・業務アプリで広く採用されています。

このページでは position: sticky によるヘッダー固定を実装します。ヘッダーは最初から表示されており、スクロールしても画面上部に貼り付いたまま追従します。スクロール時にシャドウが現れてコンテンツの上に「浮いている感」を演出することで、ヘッダーが固定されていることを視覚的に伝えます。

  • スクロール追従position: sticky; top: 0; でスクロールしても画面上部に固定される
  • スクロール時のシャドウ表示 — スクロールするとヘッダーにシャドウが付いて浮いている感が出る。CSS Transition でなめらかに変化する
  • IntersectionObserver による検知 — スクロールイベントを使わずヘッダー直前のセンチネル要素を監視することで軽量に実装する

実装のポイント・注意点

スクロール状態の検知に scroll イベントではなく IntersectionObserver を使っています。scroll イベントはスクロールのたびに高頻度で発火するためパフォーマンスに影響しますが、IntersectionObserver は対象要素の表示・非表示が切り替わったときだけ発火するため、ブラウザ負荷が少なくなります。

センチネル要素(.sh-sentinel)はヘッダーの直前に置く高さ1pxの不可視要素です。ヘッダー自体を IntersectionObserver で監視しても「常に表示されている」とみなされてしまうため、代わりにセンチネルを使います。センチネルがスクロールで画面外に消えたタイミングでヘッダーに is-scrolled クラスを付与します。

position: sticky が効かない原因として最も多いのは、親要素に overflow: hiddenoverflow: auto が設定されているケースです。sticky はスクロールコンテナを探して追従するため、スクロールを妨げる overflow 指定があると機能しません。意図せず overflow が付いていないか確認してください。

※ このデモではスクロールコンテナがデモエリア(.demo-container)なので、IntersectionObserverroot にデモコンテナを指定しています。実際のサイトに組み込む場合はサンプルソースのとおり root: null(ビューポート基準)を使ってください。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

スクロールを試してみてください

下にスクロールするとヘッダーにシャドウが現れます。
上に戻すとシャドウが消えます。

サービス紹介

ここにサービスの説明テキストが入ります。スクロールしてヘッダーの変化を確認してください。

料金プラン

ここに料金プランの説明テキストが入ります。

よくある質問

ここによくある質問の内容が入ります。

お問い合わせ

ここにお問い合わせの説明が入ります。

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>スティッキーヘッダー サンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<!-- センチネル要素: スクロール判定用の高さ 1px の不可視要素 -->
<div class="sh-sentinel" id="sh-sentinel" aria-hidden="true"></div>

<header class="sh-header" id="sh-header" role="banner">
  <div class="sh-header__inner">
    <a href="#" class="sh-header__logo">SiteLogo</a>
    <nav class="sh-header__nav" aria-label="メインナビゲーション">
      <ul class="sh-header__list">
        <li><a href="#" class="sh-header__link">ホーム</a></li>
        <li><a href="#" class="sh-header__link">サービス</a></li>
        <li><a href="#" class="sh-header__link">料金</a></li>
        <li><a href="#" class="sh-header__link">お問い合わせ</a></li>
      </ul>
    </nav>
  </div>
</header>

<!-- ヒーローエリア -->
<div class="sh-hero">
  <h1>サービス名</h1>
  <p>スクロールするとヘッダーにシャドウが現れます。</p>
</div>

<!-- ページ本文 -->
<main class="sh-main">
  <section class="sh-section">
    <h2>サービス紹介</h2>
    <p>ここにサービスの説明テキストが入ります。</p>
  </section>
  <section class="sh-section">
    <h2>料金プラン</h2>
    <p>ここに料金プランの説明テキストが入ります。</p>
  </section>
  <section class="sh-section">
    <h2>よくある質問</h2>
    <p>ここによくある質問の内容が入ります。</p>
  </section>
  <section class="sh-section">
    <h2>お問い合わせ</h2>
    <p>ここにお問い合わせフォームの説明が入ります。</p>
  </section>
</main>

<script src="./script.js"></script>
</body>
</html>
:root {
  --sh-header-height: 56px;
  --sh-header-bg: #fff;
  --sh-header-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  --sh-duration: 0.25s;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: sans-serif;
  color: #1A2332;
}

/* ===== センチネル(スクロール判定用) ===== */
/* ヘッダーの直前に置く高さ 1px の不可視要素。
   IntersectionObserver でこの要素が画面外に出たらヘッダースタイルを切り替える */
.sh-sentinel { height: 1px; }

/* ===== スティッキーヘッダー ===== */
.sh-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--sh-header-bg);
  box-shadow: none;
  border-bottom: 1px solid #E8EDF2;
  transition: box-shadow var(--sh-duration) ease, border-color var(--sh-duration) ease;
}

/* スクロール後: シャドウが現れてコンテンツの上に浮いている感が出る */
.sh-header.is-scrolled {
  box-shadow: var(--sh-header-shadow);
  border-bottom-color: transparent;
}

.sh-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--sh-header-height);
  padding: 0 20px;
  max-width: 1100px;
  margin: 0 auto;
}

.sh-header__logo {
  font-weight: 700;
  font-size: 18px;
  color: #1A2332;
  text-decoration: none;
}

.sh-header__nav { display: flex; }

.sh-header__list {
  display: flex;
  gap: 20px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sh-header__link {
  color: #5A6A7A;
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  transition: color 0.15s;
}

.sh-header__link:hover { color: #2B7FE8; }

/* ===== ヒーローエリア ===== */
.sh-hero {
  background: #F0F4F8;
  padding: 60px 20px;
  text-align: center;
}

.sh-hero h1 {
  margin: 0 0 12px;
  font-size: 28px;
  color: #1A2332;
}

.sh-hero p {
  margin: 0;
  color: #5A6A7A;
  font-size: 14px;
}

/* ===== ページ本文 ===== */
.sh-main {
  max-width: 800px;
  margin: 0 auto;
  padding: 0 20px;
}

.sh-section {
  padding: 40px 0;
  border-bottom: 1px solid #E8EDF2;
}

.sh-section:last-child { border-bottom: none; }

.sh-section h2 {
  font-size: 20px;
  margin: 0 0 12px;
}

.sh-section p {
  font-size: 14px;
  line-height: 1.7;
  color: #5A6A7A;
  margin: 0;
}
var header   = document.getElementById('sh-header');
var sentinel = document.getElementById('sh-sentinel');

// センチネルが画面外に出たら is-scrolled を付与する
var observer = new IntersectionObserver(function(entries) {
  if (!entries[0].isIntersecting) {
    header.classList.add('is-scrolled');
  } else {
    header.classList.remove('is-scrolled');
  }
}, {
  root: null,   // ビューポート基準(ページ全体のスクロールに対応)
  threshold: 0
});

observer.observe(sentinel);

AI用プロンプト

以下のプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。ライブラリ指定や列数変更など、要件を追記して使うのがおすすめです。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

# スティッキーヘッダー 作成依頼

## 概要
ページをスクロールしても画面上部に固定されるナビゲーションヘッダーを作成してください。
ヘッダーは常に白背景で表示されており、スクロール時にシャドウが現れます。

## 要件
- position: sticky でスクロールしてもヘッダーが画面上部に固定される
- ヘッダーは常に白背景で表示される(透明にはならない)
- 少しスクロールするとヘッダーにボックスシャドウが付いてコンテンツの上に浮いている感が出る
- IntersectionObserver を使ってスクロール状態を検知する(scroll イベントは使わない)
- シャドウの表示・非表示は CSS Transition でなめらかに演出する

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
- ヘッダー構成: 左にロゴ、右にナビリンク(ホーム・サービス・料金・お問い合わせ)
- センチネル要素: ヘッダーの直前に height:1px の不可視要素を置き IntersectionObserver で監視する
- スクロール後スタイル: .is-scrolled クラスを付与して box-shadow を表示する
- ヒーローエリア: ヘッダー直下に薄いグレー背景(#F0F4F8)のヒーローセクションを設置する

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。