CSSで要素の重なり順を調整したいときに使うのがz-indexです。
ところが、実際に指定してみると、
- z-index: 9999;にしているのに前面に出ない
- ヘッダーよりモーダルが後ろに隠れる
- ドロップダウンメニューが画像やスライダーの下に潜る
- position: fixed;なのに期待通り重ならない
ということがあります。
「数字を大きくすれば前に出るはず」と思いがちですが、z-indexは単純な数字勝負ではありません。重要なのは、どの要素同士が同じ重なり順のグループにいるのかです。
この記事では、CSSのz-indexが効かない原因と、実際に確認すべきポイント、よくある修正例をわかりやすく解説します。
z-indexとは
z-indexは、要素の重なり順を指定するCSSプロパティです。
横方向をX軸、縦方向をY軸とすると、画面の手前・奥にあたる方向がZ軸です。z-indexは、このZ軸方向の表示順を調整するために使います。基本的には、値が大きい要素ほど前面に表示されます。
.box-a {
position: relative;
z-index: 1;
}
.box-b {
position: relative;
z-index: 10;
}
この場合、同じ条件であれば.box-bの方が前面に表示されます。
ただし、ここで大切なのが「同じ条件であれば」という部分です。実際には、親要素やposition、transform、opacityなどの影響で、単純に数字を大きくしても前面に出ないことがあります。
z-indexが効かない主な原因
z-indexが効かない場合、よくある原因は次の通りです。
- positionが指定されていない
- 親要素の重なり順に閉じ込められている
- 別のstacking contextが作られている
- transformやopacityが影響している
- position: fixed;が親要素の影響を受けている
- HTMLの配置順や親子関係が想定と違っている
順番に見ていきます。
原因1:positionが指定されていない
まず基本として、z-indexはすべての要素に常に効くわけではありません。多くの場合、次のようなposition指定と一緒に使います。
- position: relative;
- position: absolute;
- position: fixed;
- position: sticky;
たとえば、以下のようにz-indexだけ指定しても、期待通りに効かないことがあります。
.menu {
z-index: 100;
}
この場合は、次のようにpositionを追加します。
.menu {
position: relative;
z-index: 100;
}
ヘッダーやドロップダウン、ボタンなどでz-indexが効かない場合は、まずpositionが指定されているか確認しましょう。
原因2:親要素のz-indexに閉じ込められている
z-indexで特にハマりやすいのが、親要素の影響です。子要素にどれだけ大きなz-indexを指定しても、親要素が低い重なり順のグループに入っていると、別の親要素の上には出られないことがあります。
たとえば、次のような構造です。
<div class="area-a">
<div class="modal">モーダル</div>
</div>
<div class="area-b">
<div class="header">ヘッダー</div>
</div>
CSSが以下のようになっていたとします。
.area-a {
position: relative;
z-index: 1;
}
.area-b {
position: relative;
z-index: 10;
}
.modal {
position: absolute;
z-index: 9999;
}
この場合、.modalにz-index: 9999;を指定していても、親の.area-aがz-index: 1;なので、.area-bより前に出られないことがあります。数字だけ見ると9999の方が強そうですが、実際には親要素の階層が影響します。
原因3:stacking contextが作られている
z-indexを理解するうえで重要なのが、stacking contexです。stacking contextは、簡単に言うと「重なり順を管理する独立したグループ」tのようなものです。
同じグループ内ではz-indexで重なり順を比較できます。しかし、別のグループに入っている要素同士では、単純に数字だけでは比較できません。stacking contextは、たとえば次のような条件で作られます。
- positionが指定され、z-indexがauto以外
- opacityが1未満
- transformが指定されている
- filterが指定されている
- isolation: isolate;が指定されている
- will-changeが指定されている
特に最近のサイトでは、アニメーションやスライダー、画像効果でtransformが使われることが多いため、意図せずstacking contextが作られているケースがあります。
原因4:transformが親要素に指定されている
意外と多いのが、親要素にtransformが指定されているケースです。
.wrapper {
transform: translateZ(0);
}
.fixed-menu {
position: fixed;
z-index: 9999;
}
このような場合、position: fixed;の要素がビューポート基準ではなく、親要素の影響を受けることがあります。アニメーションやスライダーのために、親要素へtransformが指定されていないか確認しましょう。
原因5:opacityやfilterが影響している
opacity: 0.99;やfilterを指定している場合も、stacking contextが作られることがあります。
.card {
opacity: 0.99;
}
見た目にはほとんど透明度が変わっていなくても、CSSの重なり順には影響する場合があります。画像のフェード演出、ホバーエフェクト、ぼかし処理などを使っている場合は、opacityやfilterも確認してください。
よくある修正例
ヘッダーを常に前面に出したい場合
固定ヘッダーを前面に出したい場合は、次のように指定します。
.site-header {
position: sticky;
top: 0;
z-index: 1000;
background: #fff;
}
position: sticky;を使えば、JavaScriptを使わずにスクロール追従ヘッダーを作れる場合があります。ただし、親要素にoverflow: hidden;などがあると、stickyが期待通りに動かないことがあるので注意してください。
モーダルを前面に出したい場合
モーダルは、できるだけbody直下に近い場所へ配置すると管理しやすくなります。
.modal-overlay {
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.6);
}
.modal-content {
position: relative;
z-index: 10000;
}
親要素の中に深く入れすぎると、親のstacking contextに影響されることがあります。
ドロップダウンメニューが隠れる場合
ドロップダウンが画像やメインビジュアルの下に隠れる場合は、メニュー側と周辺要素の両方を確認します。
.global-nav {
position: relative;
z-index: 1000;
}
.dropdown {
position: absolute;
z-index: 1001;
}
さらに、メインビジュアルやスライダー側にz-indexやtransformが指定されていないかも確認しましょう。
z-indexが効かない時の確認手順
原因が分からない場合は、次の順番で確認すると切り分けしやすいです。
- 対象要素にpositionが指定されているか確認する
- 対象要素のz-indexが適用されているか確認する
- 親要素にz-indexが指定されていないか確認する
- 親要素や祖先要素にtransformがないか確認する
- opacity、filter、isolationの指定を確認する
- 開発者ツールで実際のCSS適用状況を確認する
- HTML構造を見直し、必要なら要素の配置場所を変更する
いきなりz-index: 999999;のような極端な数値を指定するより、まずは構造を確認した方が解決しやすいです。
まとめ
z-indexが効かない原因は、単に数値が小さいからとは限りません。重要なのは、次のポイントです。
- z-indexはposition指定とセットで考える
- 親要素の重なり順に影響される
- stacking contextが作られると別グループになる
- transformやopacityが原因になることがある
- モーダルやヘッダーはHTML構造も含めて見直す
z-indexは数字を大きくすれば解決する、という単純なものではありません。前面に出したい要素だけでなく、その親要素や周辺要素のCSSも確認することが大切です。