CSSのz-indexで要素の重なり順を制御する
複数の要素が重なったとき、どちらを手前に表示するかを決めるのが z-index プロパティだ。値が大きいほど手前に描画される。ただし、z-index は position が static 以外の要素にしか効かないという前提条件がある。
基本的な使い方
z-index には整数値を指定する。正の値で手前に、負の値で奥に配置できる。値を指定しなければ auto(≒ 0)として扱われ、HTML に後から書かれた要素ほど手前に表示される。
<div class="container">
<div class="box box-a">A (z-index: 1)</div>
<div class="box box-b">B (z-index: 3)</div>
<div class="box box-c">C (z-index: 2)</div>
</div>.container {
position: relative;
height: 160px;
}
.box {
position: absolute;
width: 120px;
height: 80px;
color: #fff;
font-size: 13px;
padding: 8px;
border-radius: 6px;
}
.box-a {
background: #ef4444;
top: 10px;
left: 10px;
z-index: 1;
}
.box-b {
background: #3b82f6;
top: 30px;
left: 50px;
z-index: 3;
}
.box-c {
background: #22c55e;
top: 50px;
left: 90px;
z-index: 2;
}B の z-index が最も大きいため、重なりの一番手前に描画される。HTML の記述順に関係なく、数値が優先される点がポイントだ。
position: static では効かない
z-index が効くのは position が relative / absolute / fixed / sticky のいずれかに設定されている要素だけだ。static のままでは z-index を書いても無視される。
position が relative / absolute / fixed / sticky の要素
position が static(初期値)の要素
「z-index を指定したのに重なり順が変わらない」というトラブルの多くは、position の指定漏れが原因になっている。
スタッキングコンテキスト
z-index を理解するうえで避けて通れないのがスタッキングコンテキストという仕組みだ。z-index の比較は同じスタッキングコンテキスト内でのみ有効であり、異なるコンテキスト間では親の z-index が優先される。
/* 親 A の z-index: 1 */
.parent-a {
position: relative;
z-index: 1;
}
/* 親 A の子: z-index: 999 でも親 B の手前には出ない */
.child-a {
position: absolute;
z-index: 999;
}
/* 親 B の z-index: 2 */
.parent-b {
position: relative;
z-index: 2;
}この例では、child-a に z-index: 999 を設定しても、親の parent-a が z-index: 1 なので parent-b(z-index: 2)より奥に表示される。子要素がどれだけ大きな値を持っていても、親のコンテキストを超えることはできない。
新しいスタッキングコンテキストは z-index を持つ position 要素のほか、opacity が 1 未満の要素や transform が指定された要素でも生成される。意図しない重なりが起きたときは、祖先要素にこれらのプロパティがないか確認するとよい。
値の設計指針
プロジェクトが大きくなると z-index の値が散らかりやすい。あらかじめレイヤーごとに値の範囲を決めておくと管理しやすくなる。
| レイヤー | 値の目安 | 用途 |
|---|---|---|
| ベース | 1〜10 | 通常コンテンツ |
| ドロップダウン | 100〜199 | メニュー・ポップオーバー |
| モーダル | 1000〜1999 | ダイアログ・オーバーレイ |
z-index: 9999 のような極端な値をその場しのぎで使うと、後から別の要素を手前に出したいときに困る。チームで値の方針を共有しておくことが、保守性の高い CSS につながる。