Blog ブログ

「z-indexが効かない」を解決。9999にしても前に出ない理由

うぇぶわんこ うぇぶわんこ

z-index: 9999 にしたのに、まだ後ろにいるワン……。99999にしたら勝てる?

さとけん さとけん

その9、いったん増やすのを止めましょう。z-indexの数字より、親要素とstacking contextを見たほうが早いことがあります。

z-indexを指定したのに、なぜか前に出てこない。そんなとき、とりあえず数字を増やしてみた経験はないでしょうか。9999まで増やしても直らないなら、数字の大きさではなく、その要素がどのstacking contextに属しているか、そして親要素がどうなっているかを確認するのがおすすめです。この記事では、z-indexが効かないときによくある原因と、DevToolsを使って私が確認している順番を実例コードと一緒に整理します。

この記事でわかること

  • z-indexの数字を増やしても直らない理由
  • stacking contextを「重なり順のグループ」として考える方法
  • transformやopacityが重なり順へ影響する理由
  • overflowで切れているケースとの見分け方
  • DevToolsで親要素をたどって原因を探す順番

まず確認したいポイント

「仕組みはあとでいいので、とりあえず今のz-indexを直したい」という場合は、まず次の4点を確認してみてください。

クイックチェック

  • 対象要素のpositionとz-indexを確認する
  • 親・祖先要素に別のstacking contextがないか確認する
  • transform・opacity・filterなどが親にないか確認する
  • overflow: hiddenやoverflow: clipで切られていないか確認する

特にz-index: 9999まで増やしても直らないなら、数字を増やすより、親要素をひとつずつ上へたどるほうが原因を見つけやすいです。

まず確認。z-indexはどうやって重なり順を決める?

z-indexは、要素が重なったときの前後関係を指定するプロパティです。同じ重なりのグループ内なら、基本的には数値が大きい要素が前に表示されます。

css
.box-a {
  position: relative;
  z-index: 1;
}

.box-b {
  position: relative;
  z-index: 2;
}

この例ではBがAより前になります。ただし、これは比較する要素が同じstacking contextにいる場合の話です。

z-indexが効かないとき、まず確認したい3つ

1.その要素でz-indexが使える状態か

position: relativeやabsoluteなどのpositioned elementでは、z-indexで重なり順を指定できます。まずDevToolsで、指定が実際に適用されているか確認します。

2.FlexboxやGridならpositionなしでも使える

Flex itemやGrid itemでは、position: staticのままでもz-indexを指定できます。「効かない=とりあえずpositionを付ける」ではなく、レイアウト方式も確認しましょう。

3.そもそもz-indexの問題なのか

css
.card {
  position: relative;
  overflow: hidden;
}

.tooltip {
  position: absolute;
  z-index: 9999;
}

`.tooltip`が`.card`の外側へ出たところで消えるなら、原因は重なり順ではなくoverflow: hiddenによるclippingです。z-indexをさらに増やしても直りません。

9999でも効かない原因。stacking contextとは?

stacking context(重ね合わせコンテキスト)は、この記事では「重なり順を比べるグループ」くらいに考えてください。

子の9999が、親の2に勝てないことがある

css
.group-a {
  position: relative;
  z-index: 1;
}

.group-a .child {
  position: relative;
  z-index: 9999;
}

.group-b {
  position: relative;
  z-index: 2;
}

`.child`の9999は、`.group-a`というstacking contextの中での9999です。外側では`.group-a`の1と`.group-b`の2が比較されるため、子だけを99999にしても逆転できません。

ここだけ覚えても大丈夫です

9999が弱いのではなく、9999が戦っている場所が違います。

z-index 9999の子要素が親のstacking contextを飛び越えられない仕組みを示した図

知らないうちにstacking contextを作るCSS

stacking contextは、z-indexを書いたときだけ作られるわけではありません。アニメーションや画像調整など、別の目的で書いたCSSが原因になることもあります。

実務でよく確認する指定

指定 確認ポイント
transformnone以外で作る
opacity1未満で作る
filternone以外で作る
position: fixed作る
position: sticky作る
isolation: isolate意図的に作る
container-type: size / inline-size作る
Flex / Grid item + z-index条件により作る

特に見落としやすいのがtransformやopacity: .99です。hoverアニメーションやスライダーを追加したあとに重なりがおかしくなったら、対象要素だけでなく親側も確認します。

position: stickyそのものが効かない場合は、「position: stickyが効かない原因」の記事で確認ポイントをまとめています。

isolation: isolateは整理にも使える

isolation: isolateを使うと、意図的にstacking contextを作れます。コンポーネント内の重なりをその中で完結させたいときには便利ですが、原因が分からないまま付けるのではなく、境界を作る目的があるときに使います。

実務でよくある「z-indexが効かない」4パターン

実際の制作では、次の4パターンに当たることが多いです。

症状 まず見る場所 よくある原因
モーダルが固定ヘッダーの後ろ modalの親とheader側 親同士のstacking context
transform追加後に崩れた 追加した要素と親 新しいstacking context
ドロップダウンが後ろ HEADER側とMAIN側 子ではなく親の重なり順
途中から見えなくなる 親・祖先要素 overflowによるclipping

モーダルが9999でもヘッダーより後ろになる例

css
.header {
  position: fixed;
  z-index: 100;
}

.main {
  position: relative;
  z-index: 1;
}

.modal {
  position: fixed;
  z-index: 9999;
}

`.modal`が属する`.main`はz-index: 1のstacking contextです。見るべきなのは`.modal`の9999ではなく、親の`.main`と`.header`の関係です。

DevToolsでz-indexが効かない原因を調べる順番

私はz-indexで困ったとき、対象要素だけを眺め続けず、対象から親へ向かって上にたどるようにしています。

  1. 前に出したい要素を選ぶ
    Elementsパネルで対象を選び、positionとz-indexの適用状態を確認します。
  2. 重なっている相手を確認する
    header、hero、modalなど、何より前に出したいのかを特定します。
  3. 関連するCSSを見る
    transform、opacity、filter、isolation、container-type、overflowなどを確認します。
  4. 親要素をひとつずつ上へたどる
    親・祖先要素にstacking contextを作る指定がないか確認します。
  5. overflowも確認する
    重なりの問題なのか、clippingの問題なのかを切り分けます。
  6. 数字ではなく構造を直す
    不要なstacking contextや親のz-index、HTML上の配置を見直します。

対象要素だけで粘らない

9999を99999にする前に、親を見る。原因を探す方向がかなり変わります。

z-indexが効かないときに親要素をたどって原因を確認する手順を示した図

直ったあとに。z-indexで困りにくくする3つの考え方

1.z-indexは役割で管理する

stacking contextが整理されていれば、巨大な数字にする必要はありません。例えばheader・drawer・modalのように役割ごとに管理すると、あとから見ても意図が分かりやすくなります。

css
:root {
  --z-header: 10;
  --z-drawer: 20;
  --z-modal: 30;
}

30なら必ずmodalが一番前という意味ではありません。目的は数字を大きくすることではなく、サイト内の「重なりの役割」を整理することです。

2.必要ならisolationで範囲を区切る

コンポーネント内だけで重なり順を完結させたい場合は、isolation: isolateで意図的に境界を作る方法があります。万能な修正ではなく、設計上の境界が必要なときに使います。

3.モーダルやPopoverならtop layerも知っておく

showModal()で表示した<dialog>や表示中のPopoverは、ブラウザのtop layerに配置されます。「モーダルを一番前にする=巨大なz-index」だけではないことを知っておくと、設計の選択肢が増えます。

参考情報

今回の記事では、z-index、stacking context、isolation、top layer、Chrome DevToolsのLayersについて、MDN Web DocsとChrome for Developersの情報も確認しています。

まとめ|9999にする前に、親を見る

z-indexが効かないときは、数字を増やす前にstacking contextと親要素を確認します。

z-indexが効かないときのポイント

  • z-indexは数字の大きさだけで決まらない
  • stacking contextごとに重なり順が決まる
  • transformやopacityなどでもstacking contextは作られる
  • overflowで切られている場合は別問題
  • 原因が分からないときは対象から親へ向かって調べる

無理やり数字を足して終わらせるより、なぜそうなったのかを整理して、あとから触っても分かる状態にするほうが保守もしやすくなります。

Contact 「なぜこうなる?」まで整理しながら、
あとで困りにくいWebサイトを考えます

KU NEL WEBLEでは、見た目だけを直すのではなく、HTMLの構造やほかの画面幅への影響まで見ながら実装しています。

こんなご相談も大丈夫です

  • 既存サイトのCSSが複雑で修正しづらい
  • 一か所直すと別のところが崩れる
  • スマホ表示まで含めて見直したい
  • 部分的な改修から相談したい

「ここだけなぜか崩れる」「触るのが少し怖い」という段階でも大丈夫です。制作する本人がコードを見ながら、何が起きているのかを整理します。

Related 関連記事

お問い合わせCONTACT

お仕事のご依頼、
ご相談はこちらから

お問い合わせフォームより
お気軽にご相談ください。

お問い合わせする