🗂️

[CSS設計] 破綻しにくいz-indexの指定を考える

2022/05/18に公開

概要

各コンポーネントに指定するz-indexの値を定義する。

モチベーション

  • z-indexの衝突を避けたい
  • z-indexの指定を理由を明確にしたい
  • コンポーネントが増えるたびにz-indexの定義を増やしたくない

前提知識

重ね合わせコンテキスト(スタッキングコンテキスト)という概念があることを前提に話を進めるため、以下のページを合わせて読むと理解がしやすい。

z-indexのルール

複雑さを避けるため以下のルールを設ける。

  • 各コンポーネント内のz-index指定を極力少なくする
  • z-index0から4までの値を使用する(後述のLayer Stackに基づく指定をする)
    • -1などの負数は使用しない
    • 数値を飛び抜かして指定をしない

Layer Stack のルール

  • コンポーネントが持つべきz-indexの値を役割ごとにレイヤー(階層)上に定義する
  • 階層が重なるにつれてz-indexの値を増やしていく

Layer Stack 4 : z-index: 4

いかなる状態でも上に配置される要素。主に以下のようなコンポーネントを想定している。

  • モーダル
  • ドロワー
  • オーバーレイ

Layer Stack 3 : z-index: 3

常に上に配置されるような要素。Layer Stack 4(モーダルなど)よりは下の層に位置するが、コンテンツよりは上に位置するコンポーネント。

  • グローバルヘッダー(例: スクロールで追随する固定ヘッダー)

Layer Stack 2 : z-index: 2

  • ツールチップ
  • ポップオーバー
  • フローティングボタン(例: カルーセルなどの矢印ボタン)

Layer Stack 1 : z-index: 1

スタックを持たない要素の上に配置される。

  • バッジ

z-index の定義

:root {
  --layer-stack-1: 1;
  --layer-stack-2: 2;
  --layer-stack-3: 3;
  --layer-stack-4: 4;
}

.modal {
  z-index: var(--layer-stack-4);
}

.modal-in-modal {
  z-index: calc(var(--layer-stack-4) + 1);
}

isolationのルール

Layer Stack の定義だけだとコンポーネント同士でz-indexが競合し、意図しない重なりになってしまうケースも考えられる。その場合、isolationを利用してコンポーネントのルートにスタッキングコンテキストを生成させることで問題を解決することができる。

.special-components {
  isolation: isolate; /* スタッキングコンテキストを生成させる */
  position: absolute;
}

.special-components__item {
  position: absolute;
  z-index: 100;
}

.modal {
  position: absolute;
  z-index: var(--layer-stack-4);
}

基本的にz-indexの指定だけで問題がない状態での HTML 構造が好ましいが、z-index: calc(var(--layer-stack-2) + 1); のような指定をした場合に他のコンポーネントとの重なりが意図しない状態になる可能性が高い。子要素にz-indexの指定があるコンポーネントのルートにはisolationの指定をしておくのが好ましい。

ちなみに、isolation登場以前はスタッキングコンテキストの生成のためにtransformを副作用のない指定をして生成するハックが存在していた(例:transform: scale(1))。


まとめ

  • z-indexLayer Stack のルールに従って指定する
  • コンポーネントのルートにはisolation: isolateを指定する
株式会社ZOZO

Discussion