z-index: 9999;にしても、画像のラベルやメニューが手前に出ない。値をさらに大きくする前に、隠れ方を確認してみましょう。
z-indexは、ページ全体の要素を一つの順位表に並べる指定ではありません。値が効く条件に加え、親要素が作る重なりのグループや、はみ出した部分を切る設定が表示を左右します。
この記事では、カードに付けた「NEW」ラベルを例に、確認する要素と直す場所を分けて説明します。まず症状に近い項目を選び、直ったら最後の表示確認へ進めば大丈夫です。
まず「後ろに隠れる」と「親の端で切れる」を見分ける
最初に、見えない部分がどこから始まっているかを見ます。次の表は原因を絞るための目安です。複数の設定が同時に影響している場合もあります。
| 見え方・条件 | 最初に見る場所 | 確認すること |
|---|---|---|
| 値を変えても重なりが変わらない | 前に出したい要素自身 | position、Flex/Gridの子か、値が有効か |
| 別のカードやヘッダーの後ろに隠れる | 両方の要素の親をたどる | 重なりのグループと、その親同士の順序 |
| 親の四角や角丸に沿って途中で切れる | 切れている境界を持つ親 | overflowやclip-pathなどの切り取り |
重なり順の変更と、切り取りの解除は別の修正です。 親の外へ描画できない状態では、子のz-indexを大きくしても欠けた部分は戻りません。
z-indexが効く対象と値になっているか確認する
通常の要素はpositionと整数の指定を確認する
通常の要素でz-indexを使うときは、positionがrelative、absolute、fixed、stickyのいずれかになっているかを確認します。初期値のstaticでは、後述するFlex/Gridのアイテムを除き、z-indexを指定しても重なり順を変えられません。
たとえば、今ある場所を基準にラベルの重なりだけを調整するなら、ラベルのCSSに次の指定を加えます。
.card__badge {
position: relative;
z-index: 1;
}
relativeだけなら、topなどでずらさない限り、元の位置にとどまります。一方、すでにabsoluteで配置しているラベルをrelativeへ変える必要はありません。重なりの修正のために、配置方法まで一律に変えないようにします。
z-indexの数値には単位を付けません。z-index: 10;は有効ですが、z-index: 10px;やz-index: 1.5;は無効です。初期値のautoと整数の0も同じ意味ではなく、relative・absoluteの要素では、整数を指定すると独立した重なりのグループを作ります。
配置の基準自体が分からない場合は、positionのrelative・absolute・fixed・stickyの違いを先に確認してください。値の定義はMDNのz-indexの説明でも確認できます。
Flex/Gridでは「直接の子」にもz-indexが効く
親がdisplay: flex;またはdisplay: grid;のとき、レイアウトに並ぶ直接の子は、それぞれFlexアイテム・Gridアイテムになります。これらのアイテムには、position: static;のままでもz-indexが効きます。
次の例では、.cardがGridアイテムです。カード同士が同じマスで重なるため、z-index: 2;のカードAがカードBの前に出ます。
<div class="cards">
<div class="card card--a">カードA</div>
<div class="card card--b">カードB</div>
</div>
.cards {
display: grid;
}
.cards > .card {
grid-area: 1 / 1;
padding: 24px;
background: #f5f6f2;
border: 2px solid #536048;
}
.cards > .card--a {
z-index: 2;
}
.cards > .card--b {
z-index: 1;
margin: 24px 0 0 24px;
}
これはこの節だけの独立した例です。以下のコードと混ぜずに確認してください。CSSは対応するHTMLを置いたページのスタイルシートへ記述します。
カードのさらに内側にあるラベルまで、自動的にGridアイテムになるわけではありません。「どの親がFlex/Gridで、その直接の子はどれか」を確かめます。並べ方の基本はFlexboxの使い方とGridの使い方で補足しています。
子を9999にしても変わらないときは、親の重なりを見る
親の中の大きな値は、外のグループを追い越せない
重なりの順序をひとまとまりで扱うグループを、重ね合わせコンテキスト(スタッキングコンテキスト)と呼びます。子のz-indexは、その子が属するグループの中で比較されます。
たとえば、カードAの親側の順序が1、カードBが2なら、カードAの中にあるNEWラベルを9999にしても、カードBより手前には出られません。下の図では、カード同士が同じ外側のグループに属しているものとします。

見るべき比較は、ラベルの9999とカードBの2ではなく、カードAの1とカードBの2です。さらに外側にもグループがある場合は、両方の親をたどり、同じグループ内で比較できる階層まで戻ります。
カード同士が重なる例で、直す場所を確認する
次のHTMLとCSSを、ほかの例とは別に組み合わせて使います。カードAからはみ出したNEWラベルの右側を、カードBが覆う例です。
<div class="stage">
<div class="card card--a">
カードA
<span class="card__badge">NEW</span>
</div>
<div class="card card--b">カードB</div>
</div>
.stage {
display: flex;
align-items: flex-start;
padding: 32px;
}
.stage > .card {
position: relative;
box-sizing: border-box;
flex: 0 0 160px;
min-height: 110px;
padding: 16px;
background: #f5f6f2;
border: 2px solid #536048;
}
.stage > .card--a {
z-index: 1;
}
.stage > .card--b {
z-index: 2;
margin: 28px 0 0 -24px;
}
.stage .card__badge {
position: absolute;
top: 36px;
right: -24px;
z-index: 9999;
padding: 6px 12px;
background: #c28a3d;
color: #fff;
}
.cardのposition: relative;はラベルの配置基準です。right: -24px;ではみ出させ、カードBの左側に付けた負の余白で、カード同士を重ねています。この例には切り取りの指定はありません。
カードA全体を前に出したいなら、上の.stage > .card--aの値を次のように変えます。
.stage > .card--a {
z-index: 3;
}
これでカードAのグループがカードBより前になります。3は今回の2より大きい値として選んだだけで、すべてのサイトに共通する推奨値ではありません。
親の順序を上げると、その中の画像やボタンもまとめて前に出ます。ラベルだけを前に出したい場合は、カード全体を上げる修正が目的に合うか確認しましょう。合わない場合は、ラベルを重なりのグループの外へ移すなど、HTMLの構造から検討します。
transformやopacityでもグループができる
すべての親要素が、独立した重なりのグループを作るわけではありません。代表的な条件は次のとおりです。
| 親にある指定 | グループを作る条件 |
|---|---|
positionとz-index | relativeまたはabsoluteで、z-indexがauto以外 |
position: fixed/sticky | z-index: auto;でも作る |
Flex/Gridアイテムのz-index | auto以外の値を指定する |
transform | none以外。移動量が0でも作る |
opacity | 1より小さい値。0.99でも作る |
isolation | isolateを指定する |
アニメーション用のtransform: translateY(0);や、透明度のopacity: 0.99;が原因になることもあります。ほかにもfilterやcontainなどに条件があるため、該当しない場合はMDNの重ね合わせコンテキストの作成条件を確認します。
原因かどうかを調べるときは、一時的にその指定を無効にして表示を比べます。ただし、transformは位置や大きさにも関わります。無効化して直ったからといって、そのまま削除せず、必要な見た目を保てる修正を選んでください。
親の端で切れるときはoverflowを確認する
overflowは、要素の領域からはみ出した内容をどう扱うかを決める指定です。hiddenやclipでは外側を切り取り、autoやscrollでも表示領域の外側はそのまま見せません。
この切り取りは重なり順とは別に働きます。NEWラベルが親の境界で欠けているなら、子を前に出す前に「どの親が外側を切っているか」を確認します。

角丸のカード全体にoverflow: hidden;を付けると、はみ出したラベルも切れることがあります。画像だけを角丸に収めたいなら、切り取る役割を内側の画像用要素へ移す方法が使えます。
次は、カードから出るラベルと、切り取る画像領域を分けた独立例です。色の付いた領域を画像に見立てています。
<div class="clip-card">
<div class="clip-card__media">画像の領域</div>
<span class="clip-card__badge">NEW</span>
</div>
.clip-card {
position: relative;
width: 240px;
margin: 32px;
overflow: visible;
}
.clip-card__media {
min-height: 140px;
padding: 16px;
border-radius: 16px;
overflow: hidden;
background: #f5f6f2;
}
.clip-card__badge {
position: absolute;
top: -12px;
right: -12px;
z-index: 1;
padding: 6px 12px;
background: #c28a3d;
color: #fff;
}
ラベルは画像用要素の子ではなく、兄弟として置いています。これにより、画像側のoverflow: hidden;の切り取り対象から外れます。さらに外側の親が切り取っている場合は、その親も確認が必要です。
スクロール領域のoverflow: auto;を安易にvisibleへ変えると、スクロールやレイアウトが崩れることがあります。切り取りが必要な親なら、ラベルやメニューをその外へ置く構造を検討します。clip-pathやマスクによる切り取りも、z-indexだけでは解除できません。
また、overflow-x: hidden;とoverflow-y: visible;を組み合わせても、縦方向が必ずはみ出せるとは限りません。この組み合わせでは縦方向の算出値はautoになります。両方向の実際の値を確認してください。詳しい組み合わせはMDNのoverflowの説明を参照できます。
修正は一つずつ試し、保存後の表示で確かめる
原因を調べる段階では、複数の指定を一度に変えず、変更前と変更後を比べます。検証用のHTML・CSSを用意し、元に戻せるようにしてから試してください。
ブラウザーの開発者ツールを使うと、ページに適用されているCSSを一時的に変えて比較することもできます。Chromeでの操作方法は、Chrome公式のCSSの表示と変更の説明を参照してください。以下は、使うエディターにかかわらず共通する修正の確認順です。
症状に合う節で確認した指定だけを変えます。親の重なりの例ならカードAの1を3へ、切り取りの例なら画像とラベルの置き場所を分けます。NEWラベルが見えるかを比べ、変わらなければいったん元に戻して別の親を調べます。
表示が直る条件を確認できたら、実際に読み込んでいるスタイルシートへ必要な修正を保存します。構造を変えた場合はHTMLも保存します。開発者ツール上だけの変更は、通常は再読み込みで失われます。
ページを再読み込みし、修正が残っていることを確認します。画面の幅を狭くしたときやスクロールしたときも、ラベル・メニューが欠けず、ほかのボタンやリンクを覆っていないかを見ます。意図した要素が見え、必要な場所をクリックできれば完了です。
まとめ:数値を増やす前に、変更する階層を決める
z-indexを変えても前に出ないときは、対象に値が効くか、親のグループに制限されていないか、親の外側で切られていないかを確認します。
子だけの順序なら子を、カード全体の順序なら比較する親を修正します。切り取りが原因なら、overflowの役割とHTMLの構造を見直しましょう。原因に合う修正で表示が直り、スクロールや操作にも問題がなければ、そこで確認を終えて大丈夫です。

