スクロールでヘッダーが変化する、よくあるあのUI
Figmaのデザインでよく見かける「最初は透明なヘッダー、スクロールしたら背景がついて固定化」というUI、実装経験のある方も多いと思います。
私も過去に何度もこのパターンをコーディングしてきましたが、シンプルそうに見えて、意外と“どのタイミングで背景を変えるか”に悩まされる構成なんですよね。
JavaScriptを使えば柔軟に制御できますが、最近ではCSSだけでもかなり自然に近い表現が可能になってきています。
今回はその中でも、実務にフィットする「sticky+背景切り替え型のスティッキーヘッダー」のベストな実装方法をご紹介します。
結論:position: sticky
× 背景切り替えが最適解
このパターンのヘッダー実装で私が最もよく使っている構成は、以下のシンプルなものです。
ポイントは以下の通りです:
position: sticky
を使って、スクロールしても常に上部に張り付く挙動を実現- 初期状態は
background-color: transparent
にして、メイン画像などの背景を透かす - スクロール後に
.scrolled
クラスを付けて、背景色と影(box-shadow)を付与
この構成は、HTMLやCSSの記述量も少なく、パフォーマンス的にも非常に軽いので、LPからブログ、企業サイトまで幅広く対応できます。
なぜ sticky × 背景切り替えなのか?
fixed
よりも自然なスクロール挙動(スクロール開始時の“引っかかり感”が少ない)- JSを併用してもクラス切り替えだけで済むので保守が楽
- CSSだけでも
:has()
やscroll-behavior
を使えば擬似的な実装も可能
こうした構成にすることで、見た目の変化をなめらかに演出しつつ、コードの保守性も高められます。
あとはスクロール位置をどう検出して、いつ背景や影を付けるか――その具体的な方法を見ていきましょう。
基本構成:stickyヘッダーのレイアウト
スクロールに連動して背景が変わるヘッダーを実装するためには、まず「初期状態の透明なヘッダー」+「スクロール後に固定化される見た目」という2つの状態を想定する必要があります。
ここでは、実務でよく使われる構成パターンをベースに、HTML・CSSの基本セットを紹介します。
実務での設計ポイント
position: sticky
は親要素がoverflow: hidden
だと効かないので注意z-index
は他のUI(モーダルやナビ)との重なりに備えて十分な数値をtransition
を設定しておくことで、背景がふわっと変化して自然な印象にbackdrop-filter
を使えば、半透明ガラスのような印象も簡単に実現可能(※Safariは特にきれいに表示されます)
スマホ対応も前提に設計
- スマホではヘッダーの高さをやや狭くしたり、ナビゲーションを折りたたみに変更したりすることが多いです
- また、iOS Safariでは
sticky
の挙動に癖があるため、挙動テストを忘れずに
@media (max-width: 600px) {
.header__inner {
height: 56px;
}
}
このように、まずはレイアウトとしての“土台”をしっかり組むことで、スクロール時の背景切り替えにも対応しやすくなります。
次は、実際に「スクロール位置をどう検出するか」という問題に対して、JavaScript/CSSの両面から解決方法を見ていきます。
スクロールで背景を変える方法(JavaScript / CSS両パターン)
ヘッダーの背景や影をいつ切り替えるか――
この判断には「ユーザーがどこまでスクロールしたか」を検出する必要があります。
ここでは、JavaScriptによる汎用的な方法と、CSSのみで擬似的に実現する軽量な方法の2パターンを紹介します。
(1)JavaScript(IntersectionObserverを使った確実な判定)
最も確実で柔軟なのが、IntersectionObserverを使った方法です。
これにより、「ある要素が画面から消えたらヘッダーにクラスを付与する」といった制御が可能になります。
この構成の特徴は、こちらです:
メリット | デメリット |
---|---|
判定が正確・自由度が高い | JSに依存するため初期表示でバグる可能性あり |
スクロール位置を柔軟に制御できる | JavaScriptが無効な環境では機能しない |
(2)CSSのみで行う“なんちゃって”背景切り替え(擬似的)
CSSだけでも「スクロールしたら変化するように見せる」ことは可能です。
ただし精密な判定はできないため、演出として軽めに使う場合に限ります。
:has()
を使った例(Chrome/Safari限定・試験的)
💡 注意:このコードは CodePen 上では動作しません。:in-viewport
は現在一部の実験的なブラウザ環境でのみ利用可能な擬似クラスであり、
2025年時点ではまだ標準仕様には含まれていません(今後の仕様策定に期待)。
:target
を利用した代替(やや強引)
💡 注意:このコードは CodePen 上で動作させるために手を加えています。
この #main
はCodePen上でのみ動作確認のために追加した疑似的要素です。本番コードでは header
に id="main"
を付けて <a href="#main">
とします。
※ :target
はクリックが必要なため、スクロール連動としては実用的ではありません。
◯ どちらを使うべき?
条件 | おすすめの手法 |
---|---|
スクロール量に応じて確実に制御したい | IntersectionObserver(JS) |
JSを使いたくない / 軽量な演出だけでいい | :has() + 擬似的スタイル変化 |
パフォーマンスや保守性を最優先したい | JS + CSSで役割分離する構成 |
実務では「初期読み込みが早い方がいい」「JSバグを減らしたい」などの理由で、まずCSSベースで設計 → 必要に応じてJS追加という流れが一般的です。
次に、スクロール時の見た目を滑らかにするためのtransition
やアニメーションの工夫を紹介します。
スクロール時の滑らかなトランジションを実現するには?
スクロールに合わせてヘッダーの背景が変化する場面では、変化そのものよりも“その変わり方”のほうがUXに大きな影響を与えます。
突然のカラーチェンジや影の出現は、ユーザーに違和感を与えることもあるため、自然でスムーズなトランジションを心がけましょう。
(1)transition
の指定でふわっと感を出す
もっとも基本かつ効果的なのが、CSSの transition
プロパティを活用する方法です。
ease
は開始と終了を滑らかにする補間関数- 時間は 0.2〜0.4秒あたりが実用的(長すぎると“もっさり”感じる)
複数のプロパティをまとめて指定することで、統一感のある動きになります。
(2)will-change
の指定でチラつき防止
ヘッダーにアニメーションを加える際、とくにモバイル端末で発生しやすい“ちらつき”や“遅延”を防ぐために、will-change
を使ってブラウザに事前通知するのがおすすめです。
- これにより、GPU最適化が効きやすくなり、より滑らかな描画に
- 過剰な使用は逆にパフォーマンス悪化を招くため、本当に変化するプロパティのみに限定するのがベスト
◯ その他の調整ポイント
要素 | 調整例 | 意図 |
---|---|---|
背景の透明度 | rgba(255,255,255,0.9) | 背景画像とのなじみを良くする |
シャドウの柔らかさ | box-shadow: 0 2px 8px rgba(0,0,0,0.1) | 固さを避けて自然な立体感に |
境界線の追加 | border-bottom: 1px solid rgba(0,0,0,0.05) | シャドウなし構成の代替案 |
フェード感を保ったまま高速表示を実現するには
- 背景やシャドウを一括でトランジション
will-change
で対象を明示- 変化量は大きくしすぎず、画面内で「自然な動き」にとどめる
こうした設計を意識することで、ヘッダーの状態変化を演出ではなくUIの一部として違和感なく組み込むことができます。
このあと解説するのは、スマホ対応時に注意すべき点や、よくある落とし穴についてです。
特に iOS Safari の癖や sticky
の効かないケースへの対処法など、現場ならではの視点で整理していきます。
スマホ対応と実務的な注意点
スティッキーヘッダーは、スマホでも非常に多く使われるUIパターンです。
しかし、PCと同じ実装をそのまま適用すると不具合が出ることも少なくありません。
ここでは、実務でよく遭遇する「モバイル特有の落とし穴」とその対策をまとめます。
(1)iOS Safariの position: sticky
バグに注意
iOSのSafariでは、position: sticky
が期待通りに動作しないケースがあります。
よくある現象:
- スクロール中にヘッダーが「ピタッ」と止まらない
- スクロール後に
sticky
の位置がズレる - 特定の
overflow
構造と干渉してstickyが無効化される
対策:
- 親要素に
overflow: hidden
を使わない body
またはhtml
に不要なheight
指定をしないtransform
を親要素にかけない(GPUレイヤー化によりstickyが効かなくなる)
body,
html {
overflow-x: hidden; /* OK */
height: auto; /* OK */
}
main {
transform: none; /* GPUバグ回避 */
}
(2)ヘッダーの高さをスマホで調整
スマホ表示では、ヘッダーが高すぎると画面の占有率が増えてUXが悪化します。
以下のように、メディアクエリでヘッダーの高さを調整しましょう。
.header__inner {
height: 64px;
}
@media (max-width: 600px) {
.header__inner {
height: 56px;
}
}
また、ボタンのタップ領域が狭くならないよう、ナビゲーションアイコンやリンクのpaddingも確保しておきます。
(3)ヘッダーの高さ分だけ本文に余白を確保
ヘッダーを position: sticky
にしただけでは、本文と重なってしまい、上部のコンテンツが隠れることがあります。
対策として padding-top
を body
または main
に追加
body {
padding-top: 64px; /* ヘッダーの高さ分 */
}
スマホ用には、JavaScriptで高さを取得して動的に調整することも可能ですが、高さが固定されている場合はCSSだけでも対応可能です。
(4)Android Chrome でのスクロールバー挙動にも注意
- Android Chromeではスクロール時にアドレスバーの表示/非表示がトリガーとなり、レイアウトが再描画されることがあります。
- これが
sticky
の挙動に影響し、「チラつき」や「カクつき」が出る場合があります。
対処法:
- レイアウトシフトが少ないように
min-height
を使って安定させる - 背景に
background-attachment: scroll
を使うことで動的なリサイズに対応しやすくする
◯ 実務向けTipsまとめ
課題 | 解決策 |
---|---|
ヘッダーが隠れる | padding-top を本文に追加 |
stickyが効かない | 親要素に overflow / transform を使わない |
モバイルでの高さ調整 | メディアクエリで height を変更 |
Safariでのズレ | CSS構造の見直し(html , body に余計な指定がないか確認) |
このように、スマホでは「表示領域が狭い」「ブラウザUIの影響を受けやすい」など特有の制約があるため、テストと調整を丁寧に行う必要があります。
次は、これまでの構成をベースに、実務向けのミニマルなサンプルコードをご紹介します。
サンプルコード:透明→固定化するsticky header
ここでは、これまで解説してきた構成をもとに、実務でそのまま使える最小限のサンプルコードを紹介します。
透明な状態から、スクロールによって背景と影がつく、シンプルなスティッキーヘッダーです。
動作イメージ
- 初期状態:ヘッダーは背景なし(透明)
- スクロールで
.scroll-trigger
を通過すると、ヘッダーに.scrolled
クラスが追加される - 背景が白くなり、
box-shadow
による立体感が出る
実装の主なポイント
position: sticky
で自然な固定IntersectionObserver
で背景切り替えのトリガーを制御transition
で変化を滑らかにwill-change
でモバイルでもちらつきにくい
補足
scroll-trigger
要素の位置が「変化のタイミング」を決めます。
必要に応じて高さや配置を調整してください。padding-top: 64px
をbody
に指定しているのは、ヘッダーとコンテンツが重ならないようにするためです。- レスポンシブ調整(スマホの高さやメニュー切り替え)はCodePenのスコープ外ですが、後から簡単に追加できます。
この構成をベースに、ヘッダーの背景色・影・高さ・トリガー位置などを微調整することで、案件ごとのデザインに合わせてカスタマイズできます。
まとめ:スティッキーヘッダーは“自然さと実用性”のバランスが鍵
「スクロールで背景が変化するヘッダー」は一見地味ですが、サイト全体の印象やナビゲーションの使いやすさに直結する重要なUI要素です。
今回紹介したように、position: sticky
をベースに設計すれば、
必要最小限のHTML/CSS/JavaScriptで、軽量かつ自然なスティッキーヘッダーを実現できます。
特に以下のポイントを押さえておくと、さまざまなデザインに対応しやすくなります:
- ヘッダーは構造として固定化、見た目はクラスで切り替え
- スクロール判定は JS or CSS の使い分けを状況に応じて選ぶ
- スマホ特有の挙動・バグも想定して事前に設計しておく
コード量が少なくても、実装の意図と目的が明確であれば、十分に安定したUIが構築可能です。
次回は、最小限のJavaScript(またはJSレス)で実装するモーダルの外側クリックで閉じる挙動とフォーカス制御について解説します。
アクセシビリティを意識したモダンなモーダル設計のポイントに踏み込みます。
どうぞお楽しみに!