【CSS】追従ヘッダーの作り方|fixed・sticky・スクロール連動の実装パターン

【CSS】追従ヘッダーの作り方|fixed・sticky・スクロール連動の実装パターン HTML/CSS

CSSで追従ヘッダー(固定ヘッダー)を作る方法を解説します。

基本の position: fixed から、position: sticky との使い分けスクロール時の影やぼかし効果上スクロールで表示・下スクロールで非表示にするパターン、コンテンツのずれ防止まで、実務で必要になる実装パターンを網羅します。

スポンサーリンク

position: fixed で追従ヘッダーを作る(基本)

もっとも基本的な方法は、position: fixed でヘッダーをビューポートに固定することです。

<header class="header">
  <nav>
    <a href="/">サイト名</a>
    <ul>
      <li><a href="#about">About</a></li>
      <li><a href="#service">Service</a></li>
      <li><a href="#contact">Contact</a></li>
    </ul>
  </nav>
</header>
<main class="main">
  <p>コンテンツ</p>
</main>
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background: #fff;
  z-index: 1000;
}

position: fixed はビューポート(画面)を基準に位置を固定するため、スクロールしても常に画面上部に表示されます。

コンテンツのずれを防ぐ

position: fixed を使うと、ヘッダーが通常のフロー(文書の流れ)から外れるため、後続のコンテンツがヘッダーの裏に隠れてしまいます。

/* ヘッダーの高さ分だけ余白を追加 */
.main {
  padding-top: 60px; /* ヘッダーの高さと同じ値 */
}

padding-top(または margin-top)にヘッダーの高さと同じ値を指定して、コンテンツがヘッダーの下から始まるようにします。

CSS変数で高さを一元管理する

ヘッダーの高さを変更するたびに複数箇所を修正するのは手間なので、CSS変数で管理すると便利です。

:root {
  --header-height: 60px;
}
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: var(--header-height);
  background: #fff;
  z-index: 1000;
}
.main {
  padding-top: var(--header-height);
}

position: sticky で追従ヘッダーを作る

position: sticky は、通常のフローに含まれたまま、スクロール時に指定位置で固定されるプロパティです。

.header {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: #fff;
}

fixed との最大の違いは、コンテンツのずれが発生しないことです。sticky は通常フローに残るため、padding-top の調整が不要です。

fixed と sticky の違い

比較項目 position: fixed position: sticky
基準 ビューポート(画面全体) 親要素のスクロール領域
通常フロー 外れる(コンテンツがずれる) 維持される(ずれない)
padding-top の調整 必要 不要
固定される範囲 常にビューポートに固定 親要素の範囲内で固定
ブラウザ対応 すべて すべて(IE非対応)
おすすめの用途 常に画面に固定したいヘッダー ページ内の特定セクションで固定

一般的なサイトのヘッダーには position: sticky が手軽でおすすめです。ただし、sticky が効かない場合は親要素に overflow: hidden が指定されていないか確認してください。

スクロール時に影を付ける

ヘッダーが固定されていることをユーザーに視覚的に伝えるため、スクロール時に影を付けるパターンがよく使われます。

JavaScript でスクロール位置を検知して影を付ける

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background: #fff;
  z-index: 1000;
  transition: box-shadow 0.3s ease;
}
.header.scrolled {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
window.addEventListener('scroll', function() {
  const header = document.querySelector('.header');
  if (window.scrollY > 0) {
    header.classList.add('scrolled');
  } else {
    header.classList.remove('scrolled');
  }
});

半透明 + ぼかし効果(すりガラス風)

backdrop-filter を使うと、ヘッダーの背景をすりガラスのようにぼかすモダンなデザインが作れます。

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  z-index: 1000;
}

Apple のサイトなどで採用されているデザインです。背景を半透明にし、backdrop-filter: blur() で背後のコンテンツをぼかします。

上スクロールで表示・下スクロールで非表示にする

画面領域を有効活用するため、下にスクロールするとヘッダーが隠れ、上にスクロールすると再表示されるパターンが人気です。

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background: #fff;
  z-index: 1000;
  transition: transform 0.3s ease;
}
.header.hidden {
  transform: translateY(-100%);
}
let lastScrollY = 0;

window.addEventListener('scroll', function() {
  const header = document.querySelector('.header');
  const currentScrollY = window.scrollY;

  if (currentScrollY > lastScrollY && currentScrollY > 60) {
    // 下スクロール:ヘッダーを隠す
    header.classList.add('hidden');
  } else {
    // 上スクロール:ヘッダーを表示
    header.classList.remove('hidden');
  }

  lastScrollY = currentScrollY;
});
処理 説明
lastScrollY 前回のスクロール位置を保持
currentScrollY > lastScrollY 下方向にスクロールしたかを判定
currentScrollY > 60 ページ上部ではヘッダーを常に表示
translateY(-100%) ヘッダーを上方向に完全に移動して隠す
transition: transform 0.3s 表示・非表示をなめらかにアニメーション

ちらつきを防ぐ(閾値の設定)

少しのスクロールでヘッダーが頻繁に出たり隠れたりするのを防ぐには、閾値(デッドゾーン)を設けます。

let lastScrollY = 0;
const THRESHOLD = 10; // 10px以上スクロールしたら反応

window.addEventListener('scroll', function() {
  const header = document.querySelector('.header');
  const currentScrollY = window.scrollY;
  const diff = currentScrollY - lastScrollY;

  if (diff > THRESHOLD && currentScrollY > 60) {
    header.classList.add('hidden');
    lastScrollY = currentScrollY;
  } else if (diff < -THRESHOLD) {
    header.classList.remove('hidden');
    lastScrollY = currentScrollY;
  }
});

スクロール時にヘッダーを縮小する

スクロール前は大きなヘッダー、スクロール後はコンパクトなヘッダーに切り替えるパターンです。

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px;
  background: #fff;
  z-index: 1000;
  transition: height 0.3s ease, box-shadow 0.3s ease;
}
.header.compact {
  height: 50px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header .logo {
  height: 40px;
  transition: height 0.3s ease;
}
.header.compact .logo {
  height: 28px;
}
window.addEventListener('scroll', function() {
  const header = document.querySelector('.header');
  if (window.scrollY > 100) {
    header.classList.add('compact');
  } else {
    header.classList.remove('compact');
  }
});

ロゴやナビゲーションのサイズも transition で一緒に変化させると、自然な動きになります。

z-index の管理

追従ヘッダーでは z-index の管理が重要です。ヘッダーが他の要素の下に隠れてしまう問題がよく発生します。

要素 推奨 z-index 理由
ヘッダー 1000 ページ内のほとんどの要素より上に表示
モーダル / ダイアログ 2000 ヘッダーよりさらに上に表示
トースト通知 3000 すべての要素の上に表示

z-indexスタッキングコンテキスト内でのみ比較されます。親要素に position: relativez-index が指定されていると、新しいコンテキストが作られるため注意が必要です。

アンカーリンクの位置ずれを修正する

追従ヘッダーがあると、ページ内アンカーリンク(#section)でジャンプしたときに、ヘッダーの裏にコンテンツが隠れる問題が発生します。

/* 各セクションにスクロールマージンを設定 */
section {
  scroll-margin-top: 70px; /* ヘッダー高さ + 余裕10px */
}

/* CSS変数を使う場合 */
section {
  scroll-margin-top: calc(var(--header-height) + 10px);
}

scroll-margin-top はスクロール先の要素の上にマージンを確保するプロパティです。ヘッダーの高さ分を指定するだけで、アンカーリンクの位置ずれが解消されます。

レスポンシブ対応のポイント

考慮点 対応方法
モバイルでヘッダーが大きすぎる メディアクエリで高さを変更(PC: 80px → SP: 50px)
ナビメニューが入りきらない ハンバーガーメニューに切り替える
ヘッダーの高さが変わると padding-top もずれる CSS変数 + メディアクエリで一元管理
モバイルで画面を圧迫する 下スクロールで非表示にする
:root {
  --header-height: 80px;
}
@media (max-width: 768px) {
  :root {
    --header-height: 50px;
  }
}

追従ヘッダーが効かないときのチェックリスト

症状 原因 対処法
ヘッダーが他の要素の裏に隠れる z-index が低い、またはスタッキングコンテキストの問題 z-index: 1000 を指定し、親要素の z-index を確認
コンテンツがヘッダーの裏に隠れる position: fixed でフローから外れている padding-top をヘッダーの高さ分追加
position: sticky が効かない 親要素に overflow: hidden が指定されている 親要素の overflowvisible に変更
アンカーリンクの位置がずれる ヘッダーの高さ分だけ対象がヘッダーの裏に入る scroll-margin-top をヘッダーの高さ分指定
ヘッダーの幅が画面いっぱいにならない width: 100% が未指定 width: 100%left: 0 を追加

まとめ

パターン 実装方法 特徴
常に固定 position: fixed シンプル。padding-top の調整が必要
常に固定(モダン) position: sticky padding-top 不要。親要素の overflow に注意
スクロールで影 JS でクラス切替 + box-shadow スクロール状態をユーザーに伝える
すりガラス風 backdrop-filter: blur() モダンなデザイン
上スクロールで表示 JS でスクロール方向を検知 画面領域を有効活用
スクロールで縮小 JS でクラス切替 + transition ファーストビューと通常時で差をつける

ポイント

  • 基本は position: sticky が手軽でおすすめ
  • position: fixed を使う場合は padding-top でコンテンツのずれを防ぐ
  • CSS変数で高さを管理すると、レスポンシブ対応やアンカーリンク修正が楽になる
  • 下スクロールで非表示にする場合は閾値(THRESHOLD)を設けてちらつきを防ぐ
  • アンカーリンクの位置ずれは scroll-margin-top で解決
  • z-index1000 を目安に設定する

あわせて読みたい