CSS)z-indexのベストプラクティスを考える
CSSのz-indexプロパティについてツイートしたところ、色んな人から参考意見をいただきました。
要素の重ね順の制御をその場しのぎで乗り切っているWebサイト、実はけっこう多いような気がします。今回はz-indexのベストプラクティスを自分なりに探ってみたいと思います。
z-indexに詳しいゼットインデクサーのみなさま、意見やアドバイスがあれば是非コメントを残していってください。
z-indexについて考えるときの前提
まず、ざっくりと考え方をまとめておきます。ウェブページの要素の重ね順を理解するには「スタッキングコンテキスト(重ね合わせコンテキスト)」を知っておく必要があります。
詳しくはz-indexとスタッキングコンテキストの関係 - ics.mediaがとても分かりやすいのですが、ここではざっくりとした説明だけ載せておきます。
z-indexは数字が大きい方が上にいくとは限らない
↑ 例えば、z-index:3の要素と、z-index:99の要素があるとします。どちらもが同じ親要素のすぐ下にいるような場合は、z-index:99が上にきます。これなら直感的で分かりやすいですね。
↑ しかし、どちらかが属する親要素にz-indexが指定されている場合は話が変わってきます。z-index:99がz-index:1の要素内に入っている場合、たとえ99であってもz-index:3より下になります。
↑ なぜならz-index:3の要素とz-index:99は別のスタッキングコンテキストに属しているからです。z-index:99は、親要素がつくったスタッキングコンテキストの中では最強かもしれませんが、しょせんそのコンテキスト内での強さになってしまうわけです。
スタッキングコンテキストを作るのはz-indexだけではない
より話をややこしくするのが「スタッキングコンテキストを作るのがz-indexだけではない」ということです。たとえばopacityプロパティや、transformプロパティを使ったときなんかにもスタッキングコンテキストが作られます。position: fixedを使うとz-indexを使わなくても、スタッキングコンテキストが作られます。
他にも色々あるのですが、詳しくはMDNの重ね合わせコンテキストにまとまっています。
z-indexの値が同じ場合は、後ろに配置された要素が上にくる
z-indexの値が同じ場合、後ろに配置された要素が上に重なります。
↑ この2つの要素にはz-indexを指定していないため、どちらもz-index:0となっています。そのため、後ろに配置された要素が、重なり順では上になります。
つまり、前後の要素どうしを重ねたいような場合にz-indexを使う必要はありません。
z-indexのベストプラクティス
ここからが本題です。現時点での個人的な意見であるため、後から修正する可能性ありです。
1. 重ね順はできる限り、コンテキスト内でのみ制御する
スタッキングコンテキストが作られるケースを全て把握し、管理するのは困難です。そのため、そもそも離れた場所にいる要素どうしの並び順の制御はできる限りしないようにするのが良いと思います。
<section>
<div>😺</div>
</section>
...
<section>
<div>
<p>🦁</p>
</div>
</section>
↑ 離れた場所にいる要素どうしの重ね順をz-indexでコントロールするのは困難です。
たとえば、離れた場所にいるネコ要素😺をライオン要素🦁より上に重ねたい…みたいなことをやるとカオスになります。スタッキングコンテキストがバラバラで、z-indexの値の大きさだけでコントロールできなくなってしまう可能性があるからです。
そのうえ、後からヒョウ🐆が登場し、「ヒョウ🐆は、ネコ😺より上で、ライオン🦁よりは下で……」みたいなことになりがちだからです。
(分かりづらい例えですね)
コンポーネント内で重ね順をコントロールする
基本的には、ひとつのコンポーネントの中で重ね順のコントロールを完結するべきです。[コンポーネントAの中のある要素]と、[コンポーネントBの中のある要素]の重ね順をコントロールするべきではありません。
小さいコンポーネントであればz-indexを使う必要はない
さきほど書きましたが、z-indexの値が同じ場合は、後ろに配置された要素が上にきます。そのため、上に重ねたい要素を後ろに配置すれば、z-indexを使う必要はありません。
↑ 例えば、画像の上にテキストを重ねるようなコンポーネントの場合、画像よりもテキストの要素を後ろに位置すれば良いだけです。
使うとしてもz-index:1だけを使う
大きなコンポーネントになってしまった場合もz-index:1さえ使えれば、コンポーネント内での重ね順の制御は十分できると思います。それ以上に値のバリエーションが必要となる場合、コンポーネント自体の設計を見直すべきでしょう。
2. コンポーネントをまたぐ場合には変数を使う
実際にWebサイトを作っていくと、複数のコンポーネントをまたいで重ね順をコントロールする必要が出てくることもあります。よくあるのが「上部に固定されたヘッダー」、「下部に固定されたヘッダー」、「モーダル」といった組み合わせです。
↑ こちらはWeb版のTwitterのスクショです。固定ヘッダーは、コンテンツ部分より上でなければならないが、モーダルよりは下でなければならないという状態ですね。
モーダルは「半透明のバックドロップの部分」と「本体部分」に分かれますが、この2つは同じコンポーネント内で制御すればOKだと思います。つまり、ひとつのスタッキングコンテキストに閉じ込めてしまい、常にバックドロップよりモーダル本体が上に来るようにします。
ややこしいのはヘッダーコンポーネントと、モーダルコンポーネントの重ね順のコントロールです。常にこの2つのコンポーネントが並ぶときに、モーダルが後ろに配置されるようにすればそれだけで事足りますが、z-indexで「モーダル > ヘッダー」と決めておきたい場合もあると思います。
変数で役割ごとのz-indexの値を決めておく
そのような場合は、役割ごとのz-indexの値を変数に入れてしまって、数字を直接書かないようにするのが良いと思います。
(こちらのツイートを参考にさせていただきました)
たとえばCSS変数を使う場合、以下のようにします。
:root {
--z-index-header: 100;
--z-index-modal: 200;
}
あとはヘッダーとモーダルそれぞれのコンポーネント内のルートとなる要素にこの変数をあてておきます。
.modal {
position: relative;
z-index: var(--z-index-modal);
}
.header {
position: relative;
z-index: var(--z-index-header);
}
このようにすれば、コンポーネントを使うときに重ね順について意識する必要が無くなります。また、プロジェクト内に複数のモーダルコンポーネントが存在するような場合にも同じ変数を使い回すことができます。
使って良いz-indexの値を決めておく
複数人が触るプロジェクトの場合「z-indexの値としては1 もしくは 変数 以外使ってはいけない」というような規約をあらかじめ決めておくのが良いと思います。このルールを徹底しないと、さまざまなz-indexの値と変数が入り乱れてよりカオスになってしまう可能性があります。
変数を増やさない
また、z-index用の変数は最小限に抑えるべきだと思います。z-index-modalとz-index-modal-upperが存在するようになってしまったらオワコンです。そうならないために「z-index用の変数を増やすときは上長に申請書類を提出し、ハンコで承認を得たうえで厳密に保管する」などとルールを決めておくべきでしょう。
個人的z-indexのベストプラクティスまとめ
- できる限りコンポーネント内で重ね順をコントロールする
- コンポーネント内の要素の重ね順のコントロールには、
z-index:1だけを使う もしくはz-indexを使わない - 複数のコンポーネントをまたぐ重ね順のコントロールにはCSSやScssの変数を使う
Discussion