😻

z-index 同士の競合を isolation: isolate; で解消する

2024/01/22に公開

この記事で分かること

isolation: isolate; がどのような場面で有用かを解説します。
複数のコンポーネントを配置したとき、それぞれの z-index が競合したケースを例に用います。

前提

それぞれ position: absolute; の要素を内包する、 2 つのコンポーネントを横並びで配置します。

image

image

image

デモ

メニューに hover してみてください。デフォルトでは、表示される子メニューが他のコンポーネントの z-index に負け、表示が崩れてしまいます。
「z-index の競合を解決する」ボタンを押下すると、子メニューが正しく表示されるようになります。

解説

実装コード

2 つのコンポーネントを、 マウスオーバーメニューカード と命名します。それぞれは、構成要素で以下の z-index を保有しています。

.mouseover-menu {
  &__child {
    z-index: 1;
  }
}

.card {
  &__bar {
    z-index: 1;
  }

  &__badge {
    z-index: 2;
  }
}

個別のコンポーネント単位で見れば、これらの z-index の付与は適切であると言えます。しかし、同じ画面に配置したとき、互いの z-index が競合して表示崩れが起きてしまいました。
では、「z-index の競合を解決する」ボタンはどのような魔法を使ったのでしょうか?

.card {
  &[data-is-isolate="true"] {
    isolation: isolate;
  }
}

CodePen 上で行っているのは、 カード のデータ属性の値を変更し、 isolation: isolate; が適用されるようにしただけです。
マウスオーバーメニューz-index1000000000 にするなど、レベルを上げて物理で殴ったわけではありません。

isolation: isolate; をざっくり説明する

MDN の該当ページはこちらです。
https://developer.mozilla.org/ja/docs/Web/CSS/isolation

新しい重ね合わせコンテキストが必ず作成されます。

上記 MDN のリンクを辿ればより詳しく、正しい仕様が説明されているので、この記事では isolation: isolate; をざっくりレベルで解説します。

デフォルトでは、 マウスオーバーメニューカードz-index はこのような状態にあります。

image

親要素のレイヤー(コンテキスト)に、同じ z-index が配置されています。 競合した結果、 カード の重なりの方が勝ってしまいました。
それでは、 isolation: isolate;カード を囲ってみましょう。

image

カード に新しいコンテキストが作成されたことにより、 カード が内包する要素の z-index はそのコンテキスト内で留まるようになりました。他のコンテキストに影響することはありません。
これで、親要素直下に置かれた重ね合わせの指定は マウスオーバーメニューz-index: 1; のみになります。同じコンテキストで競合する重なりがなくなったため、 マウスオーバーメニュー が意図通りに表示されました!

isolation: isolate; を用いる際の注意点

z-index を用いるコンポーネント全てに isolation: isolate; を付与すればよいのかというと、そうではありません。
マウスオーバーメニュー の例を挙げると、この UI は「マウスオーバーのメニューは、他のコンポーネントよりも上位に表示する」という仕様があります。他のコンテキストより上に表示される必要があるため、保持する z-index が自身のコンテキストに留まってはならないのです。

試しに マウスオーバーメニュー にも カード と同様の指定をすると、見切れが発生してしまいます。

image

コンポーネントが持つ役割に応じて、他のコンポーネントとの z-index の関係性を考慮し、適切に isolation: isolate; を指定する必要があります。
z-index 設計については、スタイル分科会メンバーである hiro さんの記事も併せてご覧いただければと思います。
https://zenn.dev/hiro/articles/7609d2c7af30df

まとめ

この記事を読んでくださった何名かは、「せっかくだから俺はこの 1000000 の数字を選ぶぜ!」「じゃあ俺は桁を 1 つ増やして 10000000 にするぜ!」「誰にも負けたくねぇから 9999999999999 にしとくわ」という不毛な z-index 戦争を体験したことがあるのではないでしょうか。そしてこれらの戦いが綺麗に終結した姿を、私は見たことはありません。
しかし、適切な z-index 設計や isolation: isolate; を用いることができれば、争いは未然に防ぐことができるのです。
プロダクトの治安維持のため、ぜひお試しいただければと思います。

株式会社ZOZO

Discussion