🗂️
[CSS設計] 破綻しにくいz-indexの指定を考える
概要
各コンポーネントに指定するz-indexの値を定義する。
モチベーション
-
z-indexの衝突を避けたい -
z-indexの指定を理由を明確にしたい - コンポーネントが増えるたびに
z-indexの定義を増やしたくない
前提知識
重ね合わせコンテキスト(スタッキングコンテキスト)という概念があることを前提に話を進めるため、以下のページを合わせて読むと理解がしやすい。
z-indexのルール
複雑さを避けるため以下のルールを設ける。
- 各コンポーネント内の
z-index指定を極力少なくする -
z-indexは0から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-indexはLayer Stack のルールに従って指定する - コンポーネントのルートには
isolation: isolateを指定する
Discussion