Web制作をしていると、一度はこう思ったことがあるはずです。
「z-indexを9999にしたのに前に出てこないんだけど…?」
その原因の多くが スタッキングコンテキスト(stacking context) です。
この記事では、
✔ スタッキングコンテキストとは何か
✔ なぜz-indexが効かなくなるのか
✔ 実務でよくある落とし穴
✔ 解決方法
を、初心者にもわかるように整理します。
そもそもスタッキングコンテキストとは?
一言でいうと
要素の“重なり順の世界”を分離する仕組み
です。
通常、HTML要素はz-indexによって前後関係を決めます。
しかし、ある条件を満たすと、その要素は「独立した重なり空間」を持ちます。
これが スタッキングコンテキスト です。
図でイメージするとこうなる
[ 親コンテナ A ] ← スタッキングコンテキスト
├ B (z-index: 9999)
└ C (z-index: 1)
[ D (z-index: 10) ]
ここでAがスタッキングコンテキストを作っている場合、
Bはどれだけz-indexを上げてもDより前には出られません。
なぜなら、Bは「Aの中の世界」に閉じ込められているからです。
スタッキングコンテキストを作る条件
実務でよく出るパターンをまとめます。
① position + z-index
.box {
position: relative;
z-index: 0;
}
positionが指定され、z-indexがauto以外になると作られます。
② transformがある
.box {
transform: translateY(0);
}
これ、めちゃくちゃ多い原因です。
transformが付いた瞬間にスタッキングコンテキストが作られます。
③ opacityが1未満
.box {
opacity: 0.99;
}
透明度を下げると発生します。
④ filter
.box {
filter: blur(5px);
}
これも独立空間を作ります。
⑤ isolation
.box {
isolation: isolate;
}
明示的にスタッキングコンテキストを作る指定です。
なぜz-indexが効かなくなるの?
スタッキングコンテキストがあると、
- 子要素のz-indexはその中でしか意味を持たない
- 外側の要素との優先順位は親のz-indexで決まる
つまり、
子がいくら頑張っても、親の順位は超えられない
ということです。
実務でよくあるパターン
パターン①:transformの罠
GSAPやアニメーションで
.wrapper {
transform: translateZ(0);
}
これが入っていると、
その中のモーダルが前に出なくなることがあります。
パターン②:opacity 0.99 の最適化
GPU最適化のために入れていたopacityが原因になることも。
パターン③:親がz-index:0
.parent {
position: relative;
z-index: 0;
}
これだけで独立空間になります。
解決方法
✔ 方法① 親のスタッキングコンテキストを消す
不要なら
- transform削除
- z-index削除
- opacityを1に戻す
✔ 方法② モーダルをbody直下に出す
実務ではこれが最も安全です。
<body>
<main>...</main>
<div class="modal"></div>
</body>
✔ 方法③ isolation: isolate を使う
意図的に制御したい場合に使えます。
スタッキングコンテキストを理解すると強くなる理由
CSSのバグの半分は「重なり問題」です。
これを理解すると:
- モーダルが正しく表示できる
- ドロワーメニューが崩れない
- ヘッダー固定が安定する
- アニメーションとUIが共存できる
つまり、UI実装のレベルが一段上がります。
まとめ
スタッキングコンテキストとは
✔ 重なり順を分離する仕組み
✔ z-indexが効かない原因の大半
✔ transformやopacityが犯人になりがち
✔ 子は親の世界を超えられない
最後に
もし今「z-indexが効かない…」
となっているなら、まずはDevToolsで祖先要素をチェックしてみてください。
きっとどこかに
transform
opacity
z-index: 0
がいます。

