z-index と聞いてヴェェー!!って思うみなさん、こんにちは。
ベーシック アドベントカレンダー 9日目(大遅刻)です。
今回は z-index と安全に付き合うおはなしをします。

z-index: 9999; というラノベ主人公みたいな値があまりよくないと感じつつも、予期せぬ要素同士で起こる z-index バトルをどう回避すればいいのか、もにょもにょしますよね🙄
これを解決していきます。

私の脳内で運用しているだけなので、提案として「ふーん」程度に受け止めていただければと思います。

結論は『 z-index を設定した親要素(近い先祖要素)に isolation: isolate; をする』です。

z-index の苦しみ

『この要素を前面に出したい』

.tuyoi {
  z-index: 1;
}

『この要素を必ず最前面に出したい!』

.motto-tuyoi {
  z-index: 9999;
}

『絶対これだけは本当になんとか最前面に出てきて!!!一生のお願い!!!』

.god {
  z-index: 2147483647;
}

こんな光景を何度も見てきましたし、私自身似たようなことをやった過去があります。
これの簡易的な回避方法として、「広告関連は N 桁、モーダルは N+1 桁…」といった数値設計がありますが、けっこう運用がつらそうというか、あまりきれいな手法とは感じませんでした(もちろん、ルールを決めて苦しみに立ち向かっている時点でだいぶグッドではあります)。

z-index の指標値ガイドラインがあった - Murga
http://neos21.hatenablog.jp/entry/2018/01/30/110000

もしくは、z-index に極力小さな数値を使用し、z-index バトルで負けたら数値をあげる、というのもよく見かけます。こっちはぱっと見きれいですが、確認漏れによる事故が頻繁に起きそうですね。

どうやら、苦しみの種は どの要素と z-index バトルが起こるのか把握できない ことにありそうです。

重ね合わせコンテキストでスコープを設ける

影響範囲の把握に困ったらスコープと言いますから(?)、z-index バトルにもスコープを設けて対応したいと思います。

そのためには、まず z-index と密接な関係にある 重ね合わせコンテキスト の働きについて理解する必要があります。

重ね合わせコンテキストは超意訳すると「 Z 軸に重なり合っていくレイヤー」です。
スタッキングコンテキスト、重ね合わせ文脈などとも呼ばれています。
スタイル規則の条件が揃うと生成され、DOM とは別の構造に保持されます。

MDN の記事が最高にわかりやすいので、こちらから詳細を確認してください。

重ね合わせコンテキスト - MDN
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

👇ちょっと図解してみました。色付き背景が重ね合わせコンテキストです。
stacking_context.png
(スマホだと見づらいですよね…ごめんなさい😢

重ね合わせコンテキストを持つ要素は、子の重ね合わせコンテキスト達のスコープとなるわけです。

となれば、z-index を設定した親要素に常に重ね合わせコンテキストを付与すれば、DOM 構造に沿ったスコープを作ることができそうです。

親要素に重ね合わせコンテキストを付与する方法を考えましょう。
重ね合わせコンテキストを生成する方法はいくつもあります(👆先程の MDN を参照してください)が、コンテキスト生成以外に描画に影響を与えるものは扱いがむずかしくなってしまいます。困りましたね…。

ところがどっこい、重ね合わせコンテキストを生成する専用のプロパティがあります。
isolation です。

isolation - MDN
https://developer.mozilla.org/ja/docs/Web/CSS/isolation

実際に適用すると、このような重ね合わせになります。
ELlu3PvUEAEVFNc.jpeg

懸念点

だいぶ良さそうですが、ふたつの懸念点があります。

  • スコープを突き抜けて z-index バトルに勝たせたいケース
  • 運用の漏れ

ひとつめ。スコープを突き抜けて z-index バトルに勝たせたいケース。そんなケースは DOM 構造から見直して欲しい気持ちが強いですが、大人の事情(?)でそうせざるを得ないときは詰みです。ざんねんでした…。

ふたつめ。運用の漏れです。親要素に対してプロパティを書く というのが難儀です。これを守っていくのはなかなか骨が折れると思うので、現実的な妥協案として コンポーネントごとにスコープを作る というのが良いのではないかと思っています。つまり、コンポーネントのルート要素に重ね合わせコンテキストを生成します。

IE 対応について

IE って誰?

isolation は IE で使えないので、大人の事情で IE 対応をしなければならない場合は代替の策を講じる必要があります。
そうですね… transform: scale(1); でも付けておきましょう。これで重ね合わせコンテキストが生成されます。
ほぼ意味を持たないコードなので置き換えるときも楽で、これといった大きな副作用もなさそうです。
しかし、うーん。本当にこれで良いのかな…。よりよい案がありましたら教えてください。

おわりの言葉

私の妄想はここまで。
内容に 対するご意見 ご感想 ご指摘などは Twitter まで!( 57577 )