No.145
SCROLL CSSJS

Shrinking Header

スクロールを始めると高さが縮み、背景がすりガラスに変わる固定ヘッダー。IntersectionObserver で判定し、transform だけで縮めるので動きがカクつきません。

DEMO

下へスクロールすると、ヘッダーが縮んで背景がすりガラスになります。

CODE

HTML
<div class="demo-stage shh-stage">
  <div class="shh-scroller" id="shh-scroller" tabindex="0" aria-label="縮むヘッダーのデモ(スクロールしてください)">
    <header class="shh-header" id="shh-header">
      <div class="shh-inner"><span class="shh-logo">MOTION</span><nav class="shh-nav"><a href="#" onclick="return false">Docs</a><a href="#" onclick="return false">Blog</a></nav></div>
    </header>
    <div class="shh-sentinel" id="shh-sentinel" aria-hidden="true"></div>
    <div class="shh-body">
      <p>下へスクロールすると、ヘッダーが縮んで背景がすりガラスになります。</p>
      <div class="shh-card"></div><div class="shh-card"></div><div class="shh-card"></div><div class="shh-card"></div>
    </div>
  </div>
</div>
CSS
.shh-stage { display:flex; align-items:center; justify-content:center; padding:1.5rem; background:#0B0B10; min-height:300px; }
.shh-scroller { position:relative; width:100%; max-width:440px; height:270px; overflow-y:auto; border-radius:14px; border:1px solid rgba(255,255,255,.1); background:linear-gradient(180deg,#2B1040 0%,#15151B 45%); }
.shh-scroller:focus-visible { outline:2px solid #E91B89; outline-offset:3px; }
.shh-header { position:sticky; top:0; z-index:2; height:72px; margin-bottom:-72px; }
.shh-header::before {
  content:''; position:absolute; inset:0; background:rgba(21,21,27,.72); backdrop-filter:blur(10px);
  border-bottom:1px solid rgba(255,255,255,.1); opacity:0; transform:scaleY(.62); transform-origin:top;
  transition: opacity .3s ease, transform .3s cubic-bezier(.22,1,.36,1);
}
.shh-inner { position:relative; height:72px; display:flex; align-items:center; justify-content:space-between; padding:0 1.2rem; transform-origin:top; transition: transform .3s cubic-bezier(.22,1,.36,1); }
.shh-logo { font-family:'Oswald',sans-serif; color:#fff; font-size:1.3rem; letter-spacing:.2em; transform-origin:left center; transition: transform .3s cubic-bezier(.22,1,.36,1); }
.shh-nav { display:flex; gap:1rem; }
.shh-nav a { color:#C9C9D1; font-size:.8rem; text-decoration:none; }
.shh-nav a:focus-visible { outline:2px solid #E91B89; outline-offset:2px; }
.shh-header.is-shrunk::before { opacity:1; transform:scaleY(.62); }
.shh-header.is-shrunk .shh-inner { transform:translateY(-14px); }
.shh-header.is-shrunk .shh-logo { transform:scale(.8); }
.shh-sentinel { height:1px; }
.shh-body { padding:92px 1.2rem 1.2rem; }
.shh-body p { margin:0 0 1rem; color:#C9C9D1; font-size:.85rem; }
.shh-card { height:70px; margin-bottom:.8rem; border-radius:10px; background:rgba(255,255,255,.07); }
@media (prefers-reduced-motion: reduce) {
  .shh-header::before, .shh-inner, .shh-logo { transition-duration:.01ms; }
}
JS
(function(){
  var sc = document.getElementById('shh-scroller');
  var header = document.getElementById('shh-header');
  var sentinel = document.getElementById('shh-sentinel');
  if (!sc || !header || !sentinel) return;
  if (!('IntersectionObserver' in window)) {
    sc.addEventListener('scroll', function(){ header.classList.toggle('is-shrunk', sc.scrollTop > 8); }, { passive: true });
    return;
  }
  new IntersectionObserver(function(entries){
    header.classList.toggle('is-shrunk', !entries[0].isIntersecting);
  }, { root: sc, threshold: 0 }).observe(sentinel);
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
スクロールを始めると高さが縮み、背景がすりガラスに変わる固定ヘッダーを作って。ヘッダーは position: sticky; top: 0。縮む判定は scroll イベントではなく、ヘッダー直下に置いた高さ1pxの見えない要素(sentinel)を IntersectionObserver で監視し、画面から外れたら .is-shrunk を付ける(毎フレームの計算が要らない)。縮める時は height を変えずに transform で動かすこと:背景は ::before に置いて transform: scaleY(.62)(transform-origin: top)、中身は translateY(-14px)、ロゴは scale(.8)。背景色は半透明+backdrop-filter: blur(10px) で下のコンテンツが透ける質感にする。IntersectionObserver が無い環境では scroll イベント(passive)で代替する。@media (prefers-reduced-motion: reduce) で transition を .01ms にする。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 UIデザインの教科書 新版 マルチデバイス時代のインターフェース設計 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

RELATED EFFECTS

↗ MORE
→ STICKY SECTIONS → SCROLL PROGRESS BAR → HAMBURGER MENU

Shrinking Header(スクロールで縮むヘッダー)とは

ページの先頭では大きく表示し、スクロールを始めると高さを詰めて画面を広く使えるようにする固定ヘッダーです。ロゴやナビゲーションを常に見せたいが、読んでいる本文の面積も確保したい、という両立のための定番の作りです。

当サイトの Sticky Sections はセクションが積み重なるスクロール演出、Hamburger Menu はメニューの開閉です。本ページはヘッダー自身がスクロール位置に応じて姿を変える別テーマで、コーポレートサイトやメディアのヘッダーにそのまま使えます。

要点は、縮む判定を軽くすることと、レイアウトを動かさないことの2つです。IntersectionObserver と transform の組み合わせにすると、スクロールが重いページでもヘッダーの切り替えがカクつきません。

よくある質問

height を直接変えずに transform で縮めるのはなぜですか?

height を変えるとヘッダーの下にある本文全体の位置が再計算され、スクロール中に本文がガクッと動く原因になります。背景を疑似要素に分けて scaleY で縮め、中身は translateY で上へずらすと、レイアウトは一切変わらずに見た目だけが縮みます。

scroll イベントではなく IntersectionObserver を使う理由は?

scroll イベントはスクロール中に何十回も発火し、毎回位置を読むとメインスレッドを圧迫します。見えない sentinel 要素が画面から外れた瞬間だけ通知される IntersectionObserver なら、判定は状態が変わる2回だけで済みます。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。