ヘッダー 中級

スクロール追従・縮小ヘッダー

ヒーロー上では背の高い透明ヘッダー、読み進めるとコンパクトな白帯へ縮小+ロゴ縮小+影付き。スクロール量で is-stuck を切替える定番の追従ヘッダーで、LPやコーポレートサイトに使えます。

#header#sticky#scroll#navigation

ライブデモ

使用例(お題: SaaS FlowDesk)

この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- FlowDesk:スクロール縮小ヘッダーをSaaSのトップに適用 -->
<div class="hsk-frame">
  <header class="hsk-head" id="hskHead">
    <a class="hsk-logo" href="#" onclick="return false">◇ FlowDesk</a>
    <nav class="hsk-nav">
      <a href="#" onclick="return false">機能</a>
      <a href="#" onclick="return false">料金</a>
      <a href="#" onclick="return false">導入事例</a>
      <a href="#" onclick="return false">ブログ</a>
    </nav>
    <button class="hsk-cta" type="button">14日間無料で試す</button>
  </header>

  <div class="hsk-scroll" id="hskScroll">
    <section class="hsk-hero">
      <p class="hsk-hero__tag">PROJECT OS</p>
      <h1>チームの段取りを、<br>ひとつの画面に。</h1>
      <p>タスク・ドキュメント・予定をまとめて、迷わない毎日へ。</p>
    </section>
    <section class="hsk-body">
      <p>ヒーローの上では透明ヘッダーで世界観を見せ、スクロールするとコンパクトな白帯に切り替わります。</p>
      <p>製品サイトでは「常にCTAへ手が届く」ことが重要。縮小ヘッダーなら本文を邪魔せず申込導線を保てます。</p>
      <p>導入企業1,200社。あなたのチームの“探す時間”をゼロに近づけます。</p>
    </section>
  </div>
</div>
CSS
/* FlowDesk(SaaS):縮小追従ヘッダーの再スキン */
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

.hsk-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #0b1020; }

.hsk-head {
  position: absolute; top: 0; left: 0; right: 0; z-index: 10;
  display: flex; align-items: center; gap: 20px; padding: 22px 28px;
  color: #eef1ff;
  transition: padding .3s ease, background .3s ease, box-shadow .3s ease, color .3s ease;
}
.hsk-head.is-stuck {
  padding: 11px 28px; background: rgba(255,255,255,.95);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: 0 6px 22px rgba(13,18,40,.12); color: #18204a;
}
.hsk-logo { font-size: 21px; font-weight: 800; letter-spacing: .01em; color: inherit; text-decoration: none; transition: font-size .3s ease; }
.hsk-head.is-stuck .hsk-logo { font-size: 18px; }
.hsk-nav { display: flex; gap: 4px; margin-left: auto; }
.hsk-nav a { color: inherit; text-decoration: none; font-size: 13.5px; font-weight: 600; padding: 7px 12px; border-radius: 8px; opacity: .92; transition: background .2s ease; }
.hsk-nav a:hover { background: rgba(125,135,255,.18); }
.hsk-cta { font: inherit; font-size: 13px; font-weight: 700; border: none; cursor: pointer; padding: 9px 18px; border-radius: 10px; background: #4f6bff; color: #fff; transition: transform .15s ease, filter .15s ease; }
.hsk-cta:hover { filter: brightness(1.08); transform: translateY(-1px); }

.hsk-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }
.hsk-hero {
  min-height: 300px; display: grid; place-content: center; text-align: center; color: #eef1ff; padding: 96px 24px 40px;
  background: radial-gradient(130% 130% at 50% 0%, #1e2a6b 0%, #0b1020 68%);
}
.hsk-hero__tag { margin: 0 0 12px; font-size: 11px; letter-spacing: .3em; color: #8aa0ff; font-weight: 700; }
.hsk-hero h1 { margin: 0; font-size: 30px; font-weight: 800; line-height: 1.2; }
.hsk-hero p { margin: 12px 0 0; font-size: 14px; opacity: .82; }

.hsk-body { background: #f7f8fc; color: #2a2f45; padding: 30px 26px 80px; line-height: 1.85; }
.hsk-body p { max-width: 560px; margin: 0 auto 18px; }

@media (prefers-reduced-motion: reduce) { .hsk-head, .hsk-logo, .hsk-cta { transition: none; } }
JavaScript
// (デモと同じフックを流用)スクロール量で is-stuck を切り替えヘッダーを縮小
(() => {
  const sc = document.getElementById('hskScroll');
  const head = document.getElementById('hskHead');
  if (!sc || !head) return;
  const apply = () => head.classList.toggle('is-stuck', sc.scrollTop > 40);
  let ticking = false;
  sc.addEventListener('scroll', () => {
    if (ticking) return; ticking = true;
    requestAnimationFrame(() => { apply(); ticking = false; });
  }, { passive: true });
  apply();
  const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
  let auto = !reduce, dir = 1;
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev => sc.addEventListener(ev, () => { auto = false; }, { passive: true }));
  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      sc.scrollTop += dir * 1.6;
      if (sc.scrollTop >= 170) dir = -1; else if (sc.scrollTop <= 0) dir = 1;
      requestAnimationFrame(step);
    }, 900);
  }
})();

実装ガイド

使いどころ

LP やコーポレート、SaaS のトップなど「ヒーローの世界観を見せたいが、読み進めたら常にナビとCTAへ到達させたい」ページに。最初は透明で背景に溶け込み、スクロールでコンパクトな白帯へ切り替わります。

実装時の注意点

状態は is-stuck クラス1つで管理し、高さ・ロゴサイズ・背景・影をまとめて transition します。スクロール監視は requestAnimationFrame で間引き、しきい値(40px)を超えたかだけを見ています。プレビュー枠内で完結させるため window ではなく内部スクロール領域の scrollTop を基準にしています。実サイトでは window のスクロールに置き換えてください。

対応ブラウザ

position・transition・backdrop-filter いずれも全モダンブラウザで動作します。縮小時のぼかしに使う backdrop-filter は Safari 向けに -webkit- を併記しています(未対応環境では単に半透明になるだけで破綻しません)。

よくある失敗

しきい値を 0 にするとヒーロー最上部でも縮んでチラつきます。透明時に白文字、ソリッド時に黒文字へ色を切り替えないと、白背景で文字が消えるので color も一緒にトランジションさせること。padding を大きく変えるとレイアウトシフトを感じやすいので、高さ差は控えめに。

応用例

縮小時にロゴをシンボルのみへ差し替える、CTAの文言を短くする、スクロール下方向でだけ隠す挙動(hide-on-scroll)と組み合わせる、などの発展ができます。

コード

HTML
<!-- スクロールで縮むヘッダー(プレビュー枠内で完結) -->
<div class="hsk-frame">
  <header class="hsk-head" id="hskHead">
    <a class="hsk-logo" href="#" onclick="return false">AURORA</a>
    <nav class="hsk-nav">
      <a href="#" onclick="return false">製品</a>
      <a href="#" onclick="return false">事例</a>
      <a href="#" onclick="return false">料金</a>
      <a href="#" onclick="return false">会社</a>
    </nav>
    <button class="hsk-cta" type="button">無料で始める</button>
  </header>

  <div class="hsk-scroll" id="hskScroll">
    <section class="hsk-hero">
      <h1>スクロールで引き締まるヘッダー</h1>
      <p>下に読み進めると、背の高い透明ヘッダーが<br>コンパクトな白帯へ縮みます。</p>
    </section>
    <section class="hsk-body">
      <p>ヒーローの上では余白をたっぷり取った透明ヘッダー。視界を遮らず、世界観をそのまま見せられます。</p>
      <p>一定量スクロールすると <code>is-stuck</code> が付与され、高さ・ロゴサイズ・背景・影が一気に切り替わります。</p>
      <p>切替の閾値はスクロール量40px。requestAnimationFrame でスクロールイベントを間引き、再描画を最適化しています。</p>
      <p>縮小後は白背景+ぼかしで本文の可読性を確保。長いページでも常にナビへ到達できます。</p>
      <p>そのままトップへ戻ると透明ヘッダーへ復帰。状態はクラス1つで管理しているので拡張も簡単です。</p>
    </section>
  </div>
</div>
CSS
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

/* プレビュー枠を満たす固定高フレーム */
.hsk-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #0e1117; }

/* ヘッダー本体(フレーム上端に重なる) */
.hsk-head {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 22px 26px;            /* 通常=背が高い */
  color: #fff;
  transition: padding .3s ease, background .3s ease, box-shadow .3s ease, color .3s ease;
}
/* スクロール時=縮小+白帯+影 */
.hsk-head.is-stuck {
  padding: 11px 26px;
  background: rgba(255,255,255,.92);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 22px rgba(0,0,0,.14);
  color: #15181f;
}

.hsk-logo {
  font-size: 22px; font-weight: 800; letter-spacing: .04em;
  color: inherit; text-decoration: none;
  transition: font-size .3s ease;
}
.hsk-head.is-stuck .hsk-logo { font-size: 18px; }

.hsk-nav { display: flex; gap: 4px; margin-left: auto; }
.hsk-nav a {
  color: inherit; text-decoration: none;
  font-size: 13.5px; font-weight: 600;
  padding: 7px 12px; border-radius: 999px;
  opacity: .92; transition: background .2s ease;
}
.hsk-nav a:hover { background: rgba(128,128,128,.18); }

.hsk-cta {
  font: inherit; font-size: 13px; font-weight: 700;
  border: none; cursor: pointer;
  padding: 9px 18px; border-radius: 999px;
  background: linear-gradient(135deg, #6366f1, #ec4899);
  color: #fff;
  transition: transform .15s ease, filter .15s ease;
}
.hsk-cta:hover { filter: brightness(1.08); transform: translateY(-1px); }

/* 内部スクロール領域 */
.hsk-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }

.hsk-hero {
  min-height: 300px;
  display: grid; place-content: center; text-align: center;
  color: #fff; padding: 96px 24px 40px;
  background: radial-gradient(120% 120% at 50% 0%, #312e81 0%, #0e1117 70%);
}
.hsk-hero h1 { margin: 0; font-size: 30px; font-weight: 800; letter-spacing: -.01em; }
.hsk-hero p { margin: 12px 0 0; font-size: 14px; line-height: 1.7; opacity: .82; }

.hsk-body { background: #fbfaf7; color: #23232b; padding: 30px 26px 80px; line-height: 1.85; }
.hsk-body p { max-width: 560px; margin: 0 auto 18px; }
.hsk-body code { background: rgba(99,102,241,.12); color: #4f46e5; padding: 1px 6px; border-radius: 5px; font-size: .92em; }

@media (prefers-reduced-motion: reduce) {
  .hsk-head, .hsk-logo, .hsk-cta { transition: none; }
}
JavaScript
// スクロール量で is-stuck を切り替え、ヘッダーを縮小させる
(() => {
  const sc = document.getElementById('hskScroll');
  const head = document.getElementById('hskHead');
  if (!sc || !head) return;

  const apply = () => head.classList.toggle('is-stuck', sc.scrollTop > 40);

  // rAF でスクロールを間引く
  let ticking = false;
  sc.addEventListener('scroll', () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => { apply(); ticking = false; });
  }, { passive: true });
  apply();

  // 操作がなくても縮小→復帰が見えるよう、ゆっくり往復オートスクロール
  const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
  let auto = !reduce, dir = 1;
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
    sc.addEventListener(ev, () => { auto = false; }, { passive: true }));

  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      sc.scrollTop += dir * 1.6;
      if (sc.scrollTop >= 170) dir = -1;
      else if (sc.scrollTop <= 0) dir = 1;
      requestAnimationFrame(step);
    }, 900);
  }
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「スクロール追従・縮小ヘッダー」の効果を追加してください。

# 追加してほしい効果
スクロール追従・縮小ヘッダー(ヘッダー)
ヒーロー上では背の高い透明ヘッダー、読み進めるとコンパクトな白帯へ縮小+ロゴ縮小+影付き。スクロール量で is-stuck を切替える定番の追従ヘッダーで、LPやコーポレートサイトに使えます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- スクロールで縮むヘッダー(プレビュー枠内で完結) -->
<div class="hsk-frame">
  <header class="hsk-head" id="hskHead">
    <a class="hsk-logo" href="#" onclick="return false">AURORA</a>
    <nav class="hsk-nav">
      <a href="#" onclick="return false">製品</a>
      <a href="#" onclick="return false">事例</a>
      <a href="#" onclick="return false">料金</a>
      <a href="#" onclick="return false">会社</a>
    </nav>
    <button class="hsk-cta" type="button">無料で始める</button>
  </header>

  <div class="hsk-scroll" id="hskScroll">
    <section class="hsk-hero">
      <h1>スクロールで引き締まるヘッダー</h1>
      <p>下に読み進めると、背の高い透明ヘッダーが<br>コンパクトな白帯へ縮みます。</p>
    </section>
    <section class="hsk-body">
      <p>ヒーローの上では余白をたっぷり取った透明ヘッダー。視界を遮らず、世界観をそのまま見せられます。</p>
      <p>一定量スクロールすると <code>is-stuck</code> が付与され、高さ・ロゴサイズ・背景・影が一気に切り替わります。</p>
      <p>切替の閾値はスクロール量40px。requestAnimationFrame でスクロールイベントを間引き、再描画を最適化しています。</p>
      <p>縮小後は白背景+ぼかしで本文の可読性を確保。長いページでも常にナビへ到達できます。</p>
      <p>そのままトップへ戻ると透明ヘッダーへ復帰。状態はクラス1つで管理しているので拡張も簡単です。</p>
    </section>
  </div>
</div>

【CSS】
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

/* プレビュー枠を満たす固定高フレーム */
.hsk-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #0e1117; }

/* ヘッダー本体(フレーム上端に重なる) */
.hsk-head {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 22px 26px;            /* 通常=背が高い */
  color: #fff;
  transition: padding .3s ease, background .3s ease, box-shadow .3s ease, color .3s ease;
}
/* スクロール時=縮小+白帯+影 */
.hsk-head.is-stuck {
  padding: 11px 26px;
  background: rgba(255,255,255,.92);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 22px rgba(0,0,0,.14);
  color: #15181f;
}

.hsk-logo {
  font-size: 22px; font-weight: 800; letter-spacing: .04em;
  color: inherit; text-decoration: none;
  transition: font-size .3s ease;
}
.hsk-head.is-stuck .hsk-logo { font-size: 18px; }

.hsk-nav { display: flex; gap: 4px; margin-left: auto; }
.hsk-nav a {
  color: inherit; text-decoration: none;
  font-size: 13.5px; font-weight: 600;
  padding: 7px 12px; border-radius: 999px;
  opacity: .92; transition: background .2s ease;
}
.hsk-nav a:hover { background: rgba(128,128,128,.18); }

.hsk-cta {
  font: inherit; font-size: 13px; font-weight: 700;
  border: none; cursor: pointer;
  padding: 9px 18px; border-radius: 999px;
  background: linear-gradient(135deg, #6366f1, #ec4899);
  color: #fff;
  transition: transform .15s ease, filter .15s ease;
}
.hsk-cta:hover { filter: brightness(1.08); transform: translateY(-1px); }

/* 内部スクロール領域 */
.hsk-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }

.hsk-hero {
  min-height: 300px;
  display: grid; place-content: center; text-align: center;
  color: #fff; padding: 96px 24px 40px;
  background: radial-gradient(120% 120% at 50% 0%, #312e81 0%, #0e1117 70%);
}
.hsk-hero h1 { margin: 0; font-size: 30px; font-weight: 800; letter-spacing: -.01em; }
.hsk-hero p { margin: 12px 0 0; font-size: 14px; line-height: 1.7; opacity: .82; }

.hsk-body { background: #fbfaf7; color: #23232b; padding: 30px 26px 80px; line-height: 1.85; }
.hsk-body p { max-width: 560px; margin: 0 auto 18px; }
.hsk-body code { background: rgba(99,102,241,.12); color: #4f46e5; padding: 1px 6px; border-radius: 5px; font-size: .92em; }

@media (prefers-reduced-motion: reduce) {
  .hsk-head, .hsk-logo, .hsk-cta { transition: none; }
}

【JavaScript】
// スクロール量で is-stuck を切り替え、ヘッダーを縮小させる
(() => {
  const sc = document.getElementById('hskScroll');
  const head = document.getElementById('hskHead');
  if (!sc || !head) return;

  const apply = () => head.classList.toggle('is-stuck', sc.scrollTop > 40);

  // rAF でスクロールを間引く
  let ticking = false;
  sc.addEventListener('scroll', () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => { apply(); ticking = false; });
  }, { passive: true });
  apply();

  // 操作がなくても縮小→復帰が見えるよう、ゆっくり往復オートスクロール
  const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
  let auto = !reduce, dir = 1;
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
    sc.addEventListener(ev, () => { auto = false; }, { passive: true }));

  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      sc.scrollTop += dir * 1.6;
      if (sc.scrollTop >= 170) dir = -1;
      else if (sc.scrollTop <= 0) dir = 1;
      requestAnimationFrame(step);
    }, 900);
  }
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。