z-indexが効かない理由はこれだった?CSSの「スタッキングコンテキスト」をやさしく解説

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

がいます。