HTML/CSS
投稿日:

このデザイン、どうコーディングする? #5|スクロールで変化する透明ヘッダー、CSSだけで自然に固定する方法

スクロールでヘッダーが変化する、よくあるあの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限定・試験的)

:target を利用した代替(やや強引)

💡 注意:このコードは CodePen 上で動作させるために手を加えています。
この #mainCodePen上でのみ動作確認のために追加した疑似的要素です。本番コードでは headerid="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)シャドウなし構成の代替案

フェード感を保ったまま高速表示を実現するには

  1. 背景やシャドウを一括でトランジション
  2. will-change で対象を明示
  3. 変化量は大きくしすぎず、画面内で「自然な動き」にとどめる

こうした設計を意識することで、ヘッダーの状態変化を演出ではなくUIの一部として違和感なく組み込むことができます。

このあと解説するのは、スマホ対応時に注意すべき点や、よくある落とし穴についてです。
特に iOS Safari の癖や sticky の効かないケースへの対処法など、現場ならではの視点で整理していきます。

スマホ対応と実務的な注意点

スティッキーヘッダーは、スマホでも非常に多く使われるUIパターンです。
しかし、PCと同じ実装をそのまま適用すると不具合が出ることも少なくありません。
ここでは、実務でよく遭遇する「モバイル特有の落とし穴」とその対策をまとめます。

(1)iOS Safariの position: sticky バグに注意

iOSのSafariでは、position: sticky期待通りに動作しないケースがあります。

よくある現象:

  • スクロール中にヘッダーが「ピタッ」と止まらない
  • スクロール後に sticky の位置がズレる
  • 特定のoverflow構造と干渉してstickyが無効化される

対策:

  1. 親要素に overflow: hidden を使わない
  2. body または html に不要な height 指定をしない
  3. 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-topbody または 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: 64pxbody に指定しているのは、ヘッダーとコンテンツが重ならないようにするためです。
  • レスポンシブ調整(スマホの高さやメニュー切り替え)はCodePenのスコープ外ですが、後から簡単に追加できます。

この構成をベースに、ヘッダーの背景色・影・高さ・トリガー位置などを微調整することで、案件ごとのデザインに合わせてカスタマイズできます。

まとめ:スティッキーヘッダーは“自然さと実用性”のバランスが鍵

「スクロールで背景が変化するヘッダー」は一見地味ですが、サイト全体の印象やナビゲーションの使いやすさに直結する重要なUI要素です。

今回紹介したように、position: sticky をベースに設計すれば、
必要最小限のHTML/CSS/JavaScriptで、軽量かつ自然なスティッキーヘッダーを実現できます。

特に以下のポイントを押さえておくと、さまざまなデザインに対応しやすくなります:

  • ヘッダーは構造として固定化、見た目はクラスで切り替え
  • スクロール判定は JS or CSS の使い分けを状況に応じて選ぶ
  • スマホ特有の挙動・バグも想定して事前に設計しておく

コード量が少なくても、実装の意図と目的が明確であれば、十分に安定したUIが構築可能です。

次回は、最小限のJavaScript(またはJSレス)で実装するモーダルの外側クリックで閉じる挙動とフォーカス制御について解説します。
アクセシビリティを意識したモダンなモーダル設計のポイントに踏み込みます。

どうぞお楽しみに!