2026年5月20日
2026年5月20日
WordPressでスティッキーヘッダー(追従ヘッダー)を実装する方法
WordPressでスクロールしてもヘッダーが上部に残る「スティッキーヘッダー」を作るなら、まずCSSの position: sticky を使うのが最も手軽で、表示崩れやレイアウトのずれ(CLS)も起きにくい方法です。この記事では、position: sticky の基本、効かないときの原因、ログイン時に管理バーと重なる問題の対処、JavaScriptが必要になる場面とCLSを出さない書き方、ブロックテーマでの設定までを順に解説します。
position: stickyで固定する
スティッキーヘッダーの基本は、ヘッダー要素に次の3行を指定するだけです。
/* 子テーマの style.css または「追加CSS」 */
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: #fff; /* 背景を指定しないと下の本文が透けて見える */
}
position: sticky は、要素が通常の位置にある間は普段どおり配置され、スクロールで top に指定した位置まで来るとそこに留まる指定です。top の指定がないと固定されません。z-index は、本文中の画像やボタンがヘッダーの上に重なって見えるのを防ぐためのものです。
.site-header の部分は使っているテーマに合わせて変えてください。ブラウザの開発者ツールでヘッダー全体を囲む要素を選び、その class を確認します。クラシックテーマでは header.php の中の <header> 要素にクラスが付いていることが多く、親テーマを直接編集すると更新で消えるため、CSSは子テーマか管理画面の「追加CSS」に書きます。
この方法の利点は、ヘッダーが文書の流れから外れない点です。ヘッダー分の高さは最初から確保されているので、固定が始まった瞬間に本文が上へずれることがありません。position: fixed を使う方法と比べて、本文の上に余白を足す調整も不要です。
固定されないときに確かめること
position: sticky を指定しても動かない場合、原因の多くは次の3つです。
親要素がヘッダーと同じ高さしかない
stickyは、その要素を含む親要素の範囲の中でしか固定されません。テーマによってはヘッダーが <div class="header-wrapper"> などで囲まれていて、その親要素の高さがヘッダーと同じになっています。この場合、親要素が画面外に出た時点でヘッダーも一緒に流れていきます。
対処は、sticky の指定を外側の要素に付け直すことです。開発者ツールで親をたどり、<body> 直下やページ全体を囲む要素の子になっている要素に position: sticky を指定します。
祖先要素にoverflowが指定されている
ヘッダーの祖先要素のどこかに overflow: hidden、overflow: auto、overflow: scroll が指定されていると、その要素がスクロールの基準になり、ページのスクロールでは固定されなくなります。横はみ出し対策として body や全体のラッパーに overflow-x: hidden を付けているテーマで起きやすい問題です。
横方向のはみ出しを防ぐ目的なら、overflow-x: clip に置き換えると、stickyを壊さずにはみ出しを隠せます。clip はスクロールの基準を作らないためです。
指定が上書きされている
テーマのCSSが同じ要素に position: relative などを指定していると、後から読み込まれた側や詳細度の高い側が勝ちます。開発者ツールの「スタイル」で、自分の指定に取り消し線が付いていないか確認してください。
ログイン時に管理バーと重なる問題
WordPressにログインした状態でサイトを見ると、画面上部に管理バーが固定表示されます。top: 0 のままだと、スティッキーヘッダーが管理バーの裏に潜り込んでしまいます。
ログイン中の <body> には admin-bar クラスが付くので、それを使ってずらします。近年のWordPressでは、管理バーの高さが --wp-admin--admin-bar--height というCSS変数で出力されているため、これを参照すると画面幅ごとの高さの違いにも追従できます。
body.admin-bar .site-header {
/* 変数が無い環境では32pxを使う */
top: var(--wp-admin--admin-bar--height, 32px);
}
/* 幅600px以下では管理バーが固定表示されずスクロールで流れるため、0に戻す */
@media screen and (max-width: 600px) {
body.admin-bar .site-header {
top: 0;
}
}
管理バーの高さは、パソコン幅では32px、幅782px以下では46pxです。さらに幅600px以下では管理バー自体が固定されなくなるため、上の例のように top: 0 に戻しておかないと、スクロール後にヘッダーの上に不自然な隙間ができます。
この問題はログインしている人にしか見えないため、気づかないまま公開されがちです。動作確認はログイン中と、ログアウトした状態(またはシークレットウィンドウ)の両方で行ってください。
ページ内リンクの見出しがヘッダーに隠れる
目次などのページ内リンクで見出しへ移動すると、見出しがスティッキーヘッダーの下に隠れることがあります。これは scroll-padding-top で解決できます。
html {
/* ヘッダーの高さ+少しの余白(80pxは実際のヘッダーの高さに合わせる)。
管理バーの変数が無いとき(ログアウト時)は0pxとして計算する */
scroll-padding-top: calc(80px + var(--wp-admin--admin-bar--height, 0px));
}
scroll-padding-top はスクロールする要素(通常のページでは html)に指定しないと効きません。body に書いても反映されない点に注意してください。ヘッダーの高さが画面幅で変わる場合は、メディアクエリで値を切り替えます。
JavaScriptが必要な場面とCLSを避ける書き方
「スクロールしたら影を付けたい」「スクロールしたらヘッダーを小さくしたい」といった演出には、JavaScriptでクラスを付け外しする必要があります。ここで注意したいのが、CLS(Cumulative Layout Shift、予期しないレイアウトのずれ)です。
CLSが悪化しやすいのは次のような実装です。
- スクロール量を見て、ヘッダーを
position: staticからposition: fixedに切り替える。ヘッダーが流れから外れた瞬間に、本文がヘッダーの高さ分だけ上へ飛ぶ - スクロールに合わせてヘッダーの
heightやpaddingを小さくする。ヘッダーの高さが変わるたびに、下の本文が動く
これを避けるには、固定自体は position: sticky に任せ、JavaScriptでは見た目だけを変えるのが基本です。影、背景色、ロゴの transform: scale() など、周りの要素の位置に影響しないプロパティを変えれば、レイアウトのずれは起きません。
スクロールの検知には、scroll イベントで毎回位置を計算するより、IntersectionObserver で「ページ先頭の目印が見えなくなったか」を監視するほうが負荷が軽くなります。
.site-header {
transition: box-shadow 0.2s ease;
}
.site-header.is-scrolled {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
@media (prefers-reduced-motion: reduce) {
.site-header {
transition: none;
}
}
// assets/js/sticky-header.js
document.addEventListener('DOMContentLoaded', function () {
var header = document.querySelector('.site-header');
if (!header || !('IntersectionObserver' in window)) {
return;
}
// ページ先頭に高さ1pxの目印を置き、それが画面外に出たらクラスを付ける
var sentinel = document.createElement('div');
sentinel.setAttribute('aria-hidden', 'true');
sentinel.style.cssText = 'position:absolute;top:0;left:0;width:1px;height:1px;';
document.body.prepend(sentinel);
var observer = new IntersectionObserver(function (entries) {
header.classList.toggle('is-scrolled', !entries[0].isIntersecting);
});
observer.observe(sentinel);
});
目印は position: absolute で配置しているので、ページのレイアウトには影響しません。このスクリプトは子テーマの functions.php から読み込みます。
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_script(
'mytheme-sticky-header',
get_stylesheet_directory_uri() . '/assets/js/sticky-header.js',
array(),
'1.0.0',
array(
'strategy' => 'defer',
'in_footer' => true,
)
);
} );
第5引数の配列で strategy を指定する書き方はWordPress 6.3以降のものです。それより古い環境では第5引数に true を渡してフッターで読み込んでください。
スマートフォンでの注意点
スマートフォンでは画面の高さが限られるため、ヘッダーが高いと本文の表示領域が大きく削られます。追従させるのはロゴとメニューボタンだけの細い帯にする、狭い画面では追従させない、といった判断も必要です。
/* 例: 横向きのスマートフォンなど、高さの低い画面では追従させない */
@media (max-height: 500px) {
.site-header {
position: static;
}
}
メニューを開いたときにヘッダー内で展開する作りの場合、展開したメニューが画面の高さを超えると下の項目に届かなくなります。メニュー部分に max-height と overflow-y: auto を指定し、メニュー内でスクロールできるようにしておくと安心です。
ブロックテーマでの設定
Twenty Twenty-Fourなどのブロックテーマでは、サイトエディターでヘッダーのテンプレートパーツを開き、ヘッダー全体を囲むグループブロックを選ぶと、設定パネルの「位置」で固定(sticky)を選べます。この設定はWordPress 6.2で追加されたもので、CSSを書かずに position: sticky 相当を指定できます。ただし、この設定はテンプレートの最上位に近いブロック向けのもので、入れ子の深いブロックでは選択肢が表示されないことがあります。その場合はヘッダー全体を囲む外側のブロックを選び直してください。また背景色を設定しないと本文が透けて見える点はCSSで実装する場合と同じです。画面の表記はバージョンによって変わることがあります(2026年10月現在)。
まとめ
- スティッキーヘッダーはCSSの
position: sticky; top: 0;が基本。背景色とz-indexも指定する - stickyは流れから外れないため、固定が始まっても本文がずれずCLSが起きにくい
- 効かないときは、親要素の高さ、祖先の
overflow、指定の上書きを確認する。はみ出し対策はoverflow-x: clipが使える - ログイン時は
body.admin-barと--wp-admin--admin-bar--heightで管理バーの分ずらし、幅600px以下ではtop: 0に戻す - ページ内リンクの見出し隠れは
scroll-padding-topで防ぐ - JavaScriptでは高さや
positionを変えず、影やtransformなど見た目だけを変える。検知はIntersectionObserverが軽い - ブロックテーマはグループブロックの「位置」設定でも固定できる