【html&css】z-indexとは?使い方と効かない時の対処

css-z-index

positionを使って要素を動かせるようになると、要素同士が重なって、見せたいものが後ろに隠れるといった問題です。

その際に利用するのが、画面の奥行きをコントロールするz-index(ゼット・インデックス)プロパティです。

本記事は、重なり順や奥行きをコントロールするz-indexについて解説します。

また、HTML&CSSに関するカテゴリーページから学びたい内容を決めたい人は、以下のHTML&CSSページをご確認ください。

目次

CSSのz-indexとは

Webサイトをデザインしていると画像に文字を重ねたり、ヘッダーを画面上部に固定したりと要素同士が重なり合う場面があります。

重なり合った要素のどちらを上に表示するかといったルールを決めるのがz-indexプロパティの役割です。

その際に重要になるpositionプロパティを詳しく知りたい人は「【html&css】positionの使い方とabsolute・fixed・relativeの使い分け」を一読ください。

z-indexとは
  • z-indexの使い方
  • 要素の重なり順(奥行き)を決める
  • 数値が大きいほど手前に表示される

z-indexの使い方

{ z-index: ○○; }

z-indexの書き方は、z-index: 10;のように数値を指定するだけです。

ただし、z-indexpositionプロパティ(relativeabsolutefixedなど)と一緒に使わないと効かないということです。

初期状態であるposition: static;の要素にz-indexを指定しても無視されます。

「z-indexが効かない!」とパニックになった時は、「positionが指定されているか」を確認しましょう。

青い箱
(HTMLの2番目)
赤い箱
(z-index: 10)
HTMLコード表示
<div class="z-use-wrapper">
  <input type="radio" name="zu-pos" id="zu-error" class="zu-radio" checked>
  <input type="radio" name="zu-pos" id="zu-success" class="zu-radio">

  <div class="zu-controls">
    <label for="zu-error" class="zu-error-btn">❌ z-indexのみ指定(効かない)</label>
    <label for="zu-success" class="zu-success-btn">⭕️ positionを追加(手前に来る!)</label>
  </div>

  <div class="zu-demo-area">
    <div class="zu-box box-blue-1">青い箱<br>(HTMLの2番目)</div>
    
    <div class="zu-box target-red-1">赤い箱<br>(z-index: 10)</div>
  </div>

</div>
CSSコード表示
.z-use-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}

.zu-radio {
  display: none;
}

.zu-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.zu-error-btn,
.zu-success-btn {
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  transition: 0.2s;
  font-size: 14px;
}

.zu-error-btn {
  background-color: #f8d7da;
  color: #842029;
}

.zu-success-btn {
  background-color: #e9ecef;
  color: #495057;
}

#zu-error:checked ~ .zu-controls [for="zu-error"] {
  background-color: #dc3545;
  color: white;
}

#zu-success:checked ~ .zu-controls [for="zu-success"] {
  background-color: #198754;
  color: white;
}

.zu-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 150px;
  position: relative;
}

.zu-box {
  width: 120px;
  height: 100px;
  color: white;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}

.box-blue {
  background-color: #0d6efd;
  position: absolute;
  top: 20px;
  left: 80px;
}

/* ターゲットの赤い箱 */
.target-red {
  background-color: #dc3545;
  top: 30px;
  left: 40px;
  z-index: 10;
  transition: all 0.3s;
}

/* ❌ エラー状態:positionが無いので z-index: 10 が無視され、青の裏に隠れる */
#zu-error:checked ~ .zu-demo-area .target-red {
  position: static;
}

/* ⭕️ 成功状態:positionが追加されると z-index が発動し、手前に飛び出す! */
#zu-success:checked ~ .zu-demo-area .target-red {
  position: absolute;
  transform: scale(1.05); /* 少し大きくして強調 */
}

要素の重なり順(奥行き)を決める

Webデザインでは、横の広がりを「X軸」、縦の広がりを「Y軸」と呼びます。

画面から向かってくる「奥行き」のことを「Z軸」と呼びます。

z-indexの「z」はこのZ軸のことです。

HTMLの要素はコードの下の行に書かれたものほど、上に(手前に)重なる」といったルールを持ちます。

しかしz-indexを使えば、HTMLの順番を無視して指定した要素を手前に持ってきたり、奥に引っ込めたりとレイヤー(階層)をコントロールできます。

1. 緑の箱
2. 青い箱
3. 赤い箱
HTMLコード表示
<div class="z-order-wrapper">
  <input type="radio" name="zo-pos" id="zo-html" class="zo-radio" checked>
  <input type="radio" name="zo-pos" id="zo-blue" class="zo-radio">

  <div class="zo-controls">
    <label for="zo-html" class="zo-btn">① HTMLの順番(赤が一番手前)</label>
    <label for="zo-blue" class="zo-btn">② z-indexで青を一番手前へ!</label>
  </div>

  <div class="zo-demo-area">
    <div class="zo-box box-green-2">1. 緑の箱</div>
    <div class="zo-box box-blue-2 target-blue-2">2. 青い箱</div>
    <div class="zo-box box-red-2">3. 赤い箱</div>
  </div>

</div>
CSSコード表示
.z-order-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}

.zo-radio {
  display: none;
}

.zo-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.zo-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}

#zo-html:checked ~ .zo-controls [for="zo-html"],
#zo-blue:checked ~ .zo-controls [for="zo-blue"] {
  background-color: #0d6efd;
  color: white;
}

.zo-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 180px;
  position: relative;
}

.zo-box {
  width: 100px;
  height: 100px;
  color: white;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  position: absolute;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
  transition: all 0.4s;
}

/* 初期配置 */
.box-green {
  background-color: #198754;
  top: 20px;
  left: 10%;
}

.box-blue {
  background-color: #0d6efd;
  top: 40px;
  left: 30%;
}

.box-red {
  background-color: #dc3545;
  top: 60px;
  left: 50%;
}

/* ② 青い箱にz-indexを指定して一番手前に引き出す */
#zo-blue:checked ~ .zo-demo-area .target-blue-2 {
  z-index: 10;
  transform: translateY(-10px); /* 手前に来た感を出すために少し上にずらす */
}

数値が大きいほど手前に表示される

z-indexに設定する数値は、数値が大きい要素ほど手前に表示されるといった仕組みです。

実務では、123と連番で振るのではなく、102030のように間を大きく空けて数字を指定します。

理由は、要素A(10)と要素B(20)の間に要素Cを入れたい時に、15という数字を使って間に挟むことができます。

通常のWebページ

この上に、z-indexの数値が大きい要素を順番に被せていきます。

z-index: 10

お知らせ

ポップアップが一番手前に表示されました!

z-index: 20
HTMLコード表示
<div class="z-num-wrapper">
  <input type="radio" name="zn-state" id="zn-step1" class="zn-radio" checked>
  <input type="radio" name="zn-state" id="zn-step2" class="zn-radio">
  <input type="radio" name="zn-state" id="zn-step3" class="zn-radio">

  <div class="zn-controls">
    <label for="zn-step1" class="zn-btn">① ベース画面</label>
    <label for="zn-step2" class="zn-btn">② 暗い背景 (z-index: 10)</label>
    <label for="zn-step3" class="zn-btn">③ ポップアップ (z-index: 20)</label>
  </div>

  <div class="zn-demo-area">
    <div class="zn-base-content">
      <p style="font-weight:bold; color:#333;">通常のWebページ</p>
      <p style="color:#666; font-size:14px; margin-top:10px;">
        この上に、z-indexの数値が大きい要素を順番に被せていきます。
      </p>
    </div>

    <div class="zn-overlay">
      <span style="color:white; font-size:12px; position:absolute; bottom:10px; left:10px;">z-index: 10</span>
    </div>

    <div class="zn-modal">
      <p style="font-weight:bold; font-size:16px; color:#0d6efd; margin-bottom:10px;">お知らせ</p>
      <p style="font-size:13px; color:#333;">ポップアップが一番手前に表示されました!</p>
      <span style="color:#dc3545; font-size:12px; font-weight:bold; position:absolute; bottom:10px; right:10px;">z-index: 20</span>
    </div>
  </div>

</div>
CSSコード表示
.z-num-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}

.zn-radio {
  display: none;
}

.zn-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.zn-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}

#zn-step1:checked ~ .zn-controls [for="zn-step1"],
#zn-step2:checked ~ .zn-controls [for="zn-step2"],
#zn-step3:checked ~ .zn-controls [for="zn-step3"] {
  background-color: #0d6efd;
  color: #ffffff;
}

.zn-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 200px;
  position: relative;
  overflow: hidden;
  padding: 20px;
}

/* 中層:黒い半透明の背景(初期は見えない) */
.zn-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.6);
  z-index: 10; /* ベース画面より手前 */
  opacity: 0;
  pointer-events: none;
  transition: all 0.4s;
}

/* 上層:ポップアップ画面(初期は見えない) */
.zn-modal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.8);
  background-color: #ffffff;
  padding: 20px;
  border-radius: 8px;
  text-align: center;
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
  width: 80%;
  max-width: 250px;
  z-index: 20; /* 暗い背景よりもさらに手前! */
  opacity: 0;
  pointer-events: none;
  transition: all 0.4s;
}

/* ラジオボタンに応じた表示切り替え */
#zn-step2:checked ~ .zn-demo-area .zn-overlay,
#zn-step3:checked ~ .zn-demo-area .zn-overlay {
  opacity: 1;
  pointer-events: auto;
}

#zn-step3:checked ~ .zn-demo-area .zn-modal {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

z-indexが効かない時の対処

z-indexが全く効かない…」とパニックになった時は、以下の3つの原因をチェックしてください。

必ずどれかに当てはまるはずです。

z-indexが効かない時の対処
  • positionプロパティの指定を忘れてる
  • 親要素のz-indexが負けてる
  • opacitytransformが新しい階層を作ってる

positionプロパティの指定を忘れてる

z-indexが効かない原因で最も多いのがpositionプロパティの指定忘れです。

z-indexは、position: static;(初期値)のままでは効きません。

数値は大きくしたのに効かないという時は、要素自身にposition: relative;absolute;fixed;のいずれかが設定されているか確認しましょう。

青い箱
(z-index: 5)
赤い箱
(z-index: 100)
HTMLコード表示
<div class="zf1-wrapper">
  <input type="radio" name="zf1-state" id="zf1-off" class="zf1-radio" checked>
  <input type="radio" name="zf1-state" id="zf1-on" class="zf1-radio">

  <div class="zf1-controls">
    <label for="zf1-off" class="zf1-btn">❌ positionなし (z-index効かない)</label>
    <label for="zf1-on" class="zf1-btn">⭕️ position: relativeを追加</label>
  </div>

  <div class="zf1-demo-area">
    <div class="zf1-box zf1-blue">
      青い箱<br>
      (z-index: 5)
    </div>
    
    <div class="zf1-box zf1-red target-red-3">
      赤い箱<br>
      (z-index: 100)
    </div>
  </div>

</div>
CSSコード表示
.zf1-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}

.zf1-radio {
  display: none;
}

.zf1-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.zf1-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}

#zf1-off:checked ~ .zf1-controls [for="zf1-off"] {
  background-color: #dc3545;
  color: white;
}

#zf1-on:checked ~ .zf1-controls [for="zf1-on"] {
  background-color: #198754;
  color: white;
}

.zf1-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 180px;
  position: relative;
}

.zf1-box {
  width: 120px;
  height: 100px;
  color: white;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.zf1-blue {
  background-color: #0d6efd;
  position: absolute;
  top: 20px;
  left: 50px;
  z-index: 5;
}

/* ターゲットの赤い箱 */
.target-red-3 {
  background-color: #dc3545;
  /* positionが無いとこの100は無視される! */
  z-index: 100; 
  margin-top: 50px;
  margin-left: 90px;
  transition: all 0.3s;
}

/* ⭕️ positionを追加するとz-indexが発動! */
#zf1-on:checked ~ .zf1-demo-area .target-red-3 {
  position: relative;
  transform: scale(1.05);
}

親要素のz-indexが負けてる

positionも指定しているのに効かない!という時は、親要素のz-indexが負けてるといった原因が挙げられます。

子要素に巨大な数字(z-index: 9999;など)を指定しても、親要素のz-indexが他要素に負けていると絶対に手前には来られません。

赤の親 (z-index: 1)
赤の子
(z-index: 9999)
青の親 (z-index: 2)
HTMLコード表示
<div class="zf2-wrapper">
  <input type="radio" name="zf2-state" id="zf2-off" class="zf2-radio" checked>
  <input type="radio" name="zf2-state" id="zf2-on" class="zf2-radio">

  <div class="zf2-controls">
    <label for="zf2-off" class="zf2-btn">❌ 赤の子9999が効かない</label>
    <label for="zf2-on" class="zf2-btn">⭕️ 赤の親のz-indexを『10』に上げる</label>
  </div>

  <div class="zf2-demo-area">
    
    <div class="zf2-parent parent-red">
      <span class="parent-label">赤の親 (z-index: 1)</span>
      
      <div class="zf2-child child-red">
        赤の子<br>
        (z-index: 9999)
      </div>
    </div>

    <div class="zf2-parent parent-blue">
      <span class="parent-label">青の親 (z-index: 2)</span>
    </div>

  </div>
CSSコード表示
.zf2-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}
.zf2-radio {
  display: none;
}
.zf2-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.zf2-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}
#zf2-off:checked ~ .zf2-controls [for="zf2-off"] {
  background-color: #dc3545;
  color: white;
}
#zf2-on:checked ~ .zf2-controls [for="zf2-on"] {
  background-color: #198754;
  color: white;
}
.zf2-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 320px;
  position: relative;
}
/* 親要素の共通スタイル */
.zf2-parent {
  width: 280px; 
  height: 160px;
  position: absolute;
  border: 3px solid;
  border-radius: 8px;
  padding: 15px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  transition: all 0.4s;
}
.parent-label {
  font-size: 14px;
  font-weight: bold;
  background-color: #fff;
  padding: 6px 12px;
  border-radius: 4px;
  display: inline-block;
  white-space: nowrap;
}
/* 子要素の共通スタイル */
.zf2-child {
  width: 150px;
  height: 80px;
  color: white;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 6px;
  position: absolute;
  bottom: -25px;
  right: -25px;
  line-height: 1.5;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
/* グループ別の設定 */
.parent-blue {
  border-color: #0d6efd;
  background-color: rgba(13, 110, 253, 0.5);
  top: 100px;
  left: 150px;
  z-index: 2; /* 青の親は強さ「2」 */
}
.parent-red {
  border-color: #dc3545;
  background-color: rgba(220, 53, 69, 0.5);
  top: 30px;
  left: 30px;
  z-index: 1; /* 赤の親は強さ「1」 */
}
.child-red {
  background-color: #dc3545;
  /* 子が大きくても親が負けていれば無意味 */
  z-index: 9999; 
}
/* ⭕️ 解決策:赤の親のz-indexを青の親(2)より大きくする */
#zf2-on:checked ~ .zf2-demo-area .parent-red {
  z-index: 10;
  background-color: rgba(220, 53, 69, 0.3);
}
#zf2-on:checked ~ .zf2-demo-area .parent-red .parent-label::after {
  content: " → (10)に変更!";
  color: #dc3545;
}

opacityやtransformが新しい階層を作ってる

CSSはpositionz-indexを指定しなくても、opacity(透明度)が1未満になったり、transform(移動や変形)が指定されると、要素が新しい階層のグループ(スタッキングコンテキスト)を勝手に作るといったルールがあります。

これにより、先ほどの「親が負けている」状態が意図せず発生してしまい、中の要素のz-indexが閉じ込められて効かなくなります。

赤の親
赤の子
(z-index: 100)
青の親
HTMLコード表示
<div class="zf3-wrapper">
  <input type="radio" name="zf3-state" id="zf3-off" class="zf3-radio" checked>
  <input type="radio" name="zf3-state" id="zf3-on" class="zf3-radio">

  <div class="zf3-controls">
    <label for="zf3-off" class="zf3-btn">❌ 親に opacity: 0.9 を指定 (裏に沈む)</label>
    <label for="zf3-on" class="zf3-btn">⭕️ 親の opacity を外す (手前に来る)</label>
  </div>

  <div class="zf3-demo-area">
    
    <div class="zf3-parent parent-red-3">
      <span class="parent-label">赤の親</span>
      
      <div class="zf3-child child-red-3">
        赤の子<br>
        (z-index: 100)
      </div>
    </div>

    <div class="zf3-parent parent-blue-3">
      <span class="parent-label">青の親</span>
    </div>

  </div>
</div>
CSSコード表示
.zf3-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}
.zf3-radio {
  display: none;
}
.zf3-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.zf3-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}
#zf3-off:checked ~ .zf3-controls [for="zf3-off"] {
  background-color: #dc3545;
  color: white;
}
#zf3-on:checked ~ .zf3-controls [for="zf3-on"] {
  background-color: #198754;
  color: white;
}
.zf3-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 320px;
  position: relative;
}
/* 親要素の共通スタイル */
.zf3-parent {
  width: 260px;
  height: 150px;
  border: 3px solid;
  border-radius: 8px;
  padding: 15px;
  position: absolute;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  transition: all 0.4s;
}
.parent-label {
  font-size: 14px;
  font-weight: bold;
  background-color: #fff;
  padding: 6px 12px;
  border-radius: 4px;
  display: inline-block;
  white-space: nowrap;
}
/* グループ別の設定 */
.parent-blue-3 {
  border-color: #0d6efd;
  background-color: rgba(13, 110, 253, 0.1);
  top: 100px;
  left: 140px;
  /* 青にz-indexを持たせる */
  z-index: 1; 
}
.parent-red-3 {
  border-color: #dc3545;
  background-color: rgba(220, 53, 69, 0.1);
  top: 30px;
  left: 30px;
}
/* 子要素のスタイル */
.child-red-3 {
  width: 140px;
  height: 70px;
  background-color: #dc3545;
  color: white;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 6px;
  position: absolute;
  bottom: -25px;
  right: -25px;
  line-height: 1.5;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  /* 子は頑張って手前に来ようとしている */
  z-index: 100; 
}
/* ❌ エラー状態:親にopacityがつくと、勝手に階層が作られて負ける */
#zf3-off:checked ~ .zf3-demo-area .parent-red-3 {
  opacity: 0.9; /* 少しだけ透明にする */
}
/* ⭕️ 成功状態:opacityを外せば、子要素のz-index:100が正常に手前へ */
#zf3-on:checked ~ .zf3-demo-area .parent-red-3 {
  opacity: 1; /* 透明度をなくす */
}

z-indexの指定できる値

z-indexに指定できる値は、大きく分けて「auto(初期値)」と「整数(プラス、マイナス、0)」の2種類です。

小数を指定することはできません。

  • 最小値:-2147483647
  • 最大値:2147483647
  • 整数のみ:小数点はNG
z-indexの指定できる値
  • 初期値のautoと整数の違い
  • マイナスの数で奥に隠す
  • 「とりあえず9999」は危険

初期値のautoと整数の違い

CSSで何も指定していない時、全ての要素のz-indexは「auto(オート)」という初期値になります。

autoは実質的に0と同じ高さにいますが、明確にz-index: 0;z-index: 1;などの整数を指定することで、要素は重なり順のルール(スタッキングコンテキスト)に参加し、手前や奥に移動できます。

ターゲット
auto
基準の箱
(HTMLの2番目)
HTMLコード表示
<div class="zw1-wrapper">
  <input type="radio" name="zw1-state" id="zw1-auto" class="zw1-radio" checked>
  <input type="radio" name="zw1-state" id="zw1-num" class="zw1-radio">

  <div class="zw1-controls">
    <label for="zw1-auto" class="zw1-btn">❌ 初期値 (z-index: auto;)</label>
    <label for="zw1-num" class="zw1-btn">⭕️ 整数を指定 (z-index: 10;)</label>
  </div>

  <div class="zw1-demo-area">
    
    <div class="zw1-box box-red-w1 target-red-w1">
      ターゲット<br>
      <span class="val-label">auto</span>
    </div>

    <div class="zw1-box box-green-w1">
      基準の箱<br>
      (HTMLの2番目)
    </div>
    
  </div>
</div>
CSSコード表示
.zw1-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}
.zw1-radio {
  display: none;
}
.zw1-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.zw1-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}
#zw1-auto:checked ~ .zw1-controls [for="zw1-auto"] {
  background-color: #dc3545;
  color: white;
}
#zw1-num:checked ~ .zw1-controls [for="zw1-num"] {
  background-color: #198754;
  color: white;
}
.zw1-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 220px;
  position: relative;
}
.zw1-box {
  width: 150px;
  height: 100px;
  color: white;
  font-weight: bold;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
  position: absolute;
  transition: all 0.4s;
  line-height: 1.5;
}
/* 赤の箱(初期状態は奥) */
.box-red-w1 {
  background-color: #dc3545;
  top: 30px;
  left: 40px;
  z-index: auto; /* 初期値なので緑に負ける */
}
/* 緑の箱(HTMLで後に書かれているので手前) */
.box-green-w1 {
  background-color: #198754;
  top: 70px;
  left: 120px;
}
.val-label {
  background-color: #fff;
  color: #dc3545;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 12px;
  margin-top: 8px;
}
/* ⭕️ 整数を指定すると、ルールに参加して緑より手前に飛び出す! */
#zw1-num:checked ~ .zw1-demo-area .target-red-w1 {
  z-index: 10;
  transform: scale(1.05);
}
#zw1-num:checked ~ .zw1-demo-area .target-red-w1 .val-label::after {
  content: " → 10";
}

マイナスの数で奥に隠す

z-indexは10100といったプラスの数字だけでなく、-1のような「マイナスの整数」も指定できます。

実務では、装飾用の図形やアイコンを文字の邪魔にならないように背景の奥へ沈めるといったテクニックとして使われます。

マイナス値の魔法

z-indexに「-1」を指定すると、通常の文章よりも奥のレイヤー(階層)に要素を移動させることができます。
このテクニックを使えば、美しい背景装飾が簡単に作れます!

装飾
HTMLコード表示
<div class="zw2-wrapper">
  <input type="radio" name="zw2-state" id="zw2-front" class="zw2-radio" checked>
  <input type="radio" name="zw2-state" id="zw2-back" class="zw2-radio">

  <div class="zw2-controls">
    <label for="zw2-front" class="zw2-btn">❌ 装飾が文字の邪魔 (z-index: 1;)</label>
    <label for="zw2-back" class="zw2-btn">⭕️ マイナスで奥へ沈める (z-index: -1;)</label>
  </div>

  <div class="zw2-demo-area">
    
    <div class="zw2-card">
      <div class="zw2-title">マイナス値の魔法</div>
      <p class="zw2-text">
        z-indexに「-1」を指定すると、通常の文章よりも奥のレイヤー(階層)に要素を移動させることができます。<br>
        このテクニックを使えば、美しい背景装飾が簡単に作れます!
      </p>
      
      <div class="zw2-circle target-circle">装飾</div>
    </div>

  </div>
</div>
CSSコード表示
.zw2-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}
.zw2-radio {
  display: none;
}
.zw2-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.zw2-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}
#zw2-front:checked ~ .zw2-controls [for="zw2-front"] {
  background-color: #dc3545;
  color: white;
}
#zw2-back:checked ~ .zw2-controls [for="zw2-back"] {
  background-color: #198754;
  color: white;
}
.zw2-demo-area {
  padding: 20px;
  display: flex;
  justify-content: center;
}
/* カード本体(※マイナス要素がカードの外へ逃げないように z-index: 0; を設定) */
.zw2-card {
  background-color: #ffffff;
  width: 100%;
  max-width: 400px;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
  position: relative;
  z-index: 0; /* ★マイナス要素の沈む限界を決めるストッパー */
  overflow: hidden; /* 丸がカードからはみ出さないようにする */
}
.zw2-title {
  margin: 0 0 15px 0;
  color: #333;
  font-size: 20px;
}
.zw2-text {
  margin: 0;
  color: #555;
  line-height: 1.6;
  font-size: 14px;
}
/* 装飾用の丸(初期はプラスで手前にある) */
.zw2-circle {
  width: 150px;
  height: 150px;
  background-color: #ffc107;
  border-radius: 50%;
  position: absolute;
  top: -30px;
  right: -30px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: bold;
  transition: all 0.5s ease;
  z-index: 1; /* 初期状態は手前 */
  opacity: 0.9;
}
/* ⭕️ マイナス値を指定して、テキストの奥(背景色の上)へ沈める */
#zw2-back:checked ~ .zw2-demo-area .target-circle {
  z-index: -1;
  transform: scale(1.2); /* 奥に行った感を出すために少し広げる */
  opacity: 0.3; /* 背景っぽく薄くする */
}

「とりあえず9999」は危険

要素がうまく重ならない時、危険な設定が「とりあえずz-index: 9999;を指定して一番手前に持ってくる」といった解決策です。

これをやると、あとから「手前にポップアップ画面を出したい」となった時、次の人が99999を指定し、さらに次の人が999999を指定する…といった世にも恐ろしい「z-index戦争」が勃発します。

実務では、ヘッダーは100、ポップアップは200のように、プロジェクトごとのルールを決めて10〜100単位の数字で管理するのがおすすめです。

追従ヘッダー z-index: 9999

ここにWebサイトのコンテンツが入ります。

「ポップアップを開く」ボタンを押した想定です↓

ポップアップ画面

本来はこれが一番手前に来ないといけません!

z-index: 100
HTMLコード表示
<div class="zw3-wrapper">
  <input type="radio" name="zw3-state" id="zw3-bad" class="zw3-radio" checked>
  <input type="radio" name="zw3-state" id="zw3-good" class="zw3-radio">

  <div class="zw3-controls">
    <label for="zw3-bad" class="zw3-btn">❌ ヘッダーが9999の悲劇</label>
    <label for="zw3-good" class="zw3-btn">⭕️ 正しい数字の管理 (100と200)</label>
  </div>

  <div class="zw3-demo-area">
    
    <div class="zw3-header target-header">
      追従ヘッダー
      <span class="zw3-val">z-index: 9999</span>
    </div>

    <div class="zw3-content">
      <p style="margin-top: 60px;">ここにWebサイトのコンテンツが入ります。</p>
      <p>「ポップアップを開く」ボタンを押した想定です↓</p>
    </div>

    <div class="zw3-modal target-modal">
      <div>ポップアップ画面</div>
      <p>本来はこれが一番手前に来ないといけません!</p>
      <span class="zw3-val-modal">z-index: 100</span>
    </div>

  </div>
</div>
CSSコード表示
.zw3-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-family: sans-serif;
}
.zw3-radio {
  display: none;
}
.zw3-controls {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.zw3-btn {
  background-color: #e9ecef;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  color: #495057;
  transition: 0.2s;
  font-size: 14px;
}
#zw3-bad:checked ~ .zw3-controls [for="zw3-bad"] {
  background-color: #dc3545;
  color: white;
}
#zw3-good:checked ~ .zw3-controls [for="zw3-good"] {
  background-color: #198754;
  color: white;
}
.zw3-demo-area {
  background-color: #ffffff;
  border: 2px dashed #adb5bd;
  height: 300px;
  position: relative;
  overflow: hidden; /* モーダルのはみ出しを隠す */
}
.zw3-content {
  padding: 20px;
  color: #666;
  font-size: 14px;
}
/* 追従ヘッダー */
.zw3-header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 50px;
  background-color: #212529;
  color: white;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  box-sizing: border-box;
  font-weight: bold;
  transition: all 0.4s;
  /* ❌ 悲劇の始まり */
  z-index: 9999; 
}
.zw3-val {
  background-color: #dc3545;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
}
/* ポップアップ画面 */
.zw3-modal {
  position: absolute;
  top: 40px; /* わざとヘッダーに被さる位置に配置 */
  left: 50%;
  transform: translateX(-50%);
  width: 260px;
  background-color: #0d6efd;
  color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 10px 20px rgba(0,0,0,0.3);
  text-align: center;
  transition: all 0.4s;
  /* 一般的なモーダルの数値 */
  z-index: 100; 
}
.zw3-modal div {
  margin: 0 0 10px 0;
}
.zw3-modal p {
  font-size: 13px;
  margin: 0 0 15px 0;
}
.zw3-val-modal {
  background-color: #fff;
  color: #0d6efd;
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: bold;
}
/* ⭕️ 正しい管理:ヘッダーを100、モーダルを200に整理する */
#zw3-good:checked ~ .zw3-demo-area .target-header {
  z-index: 100;
  background-color: #000;
}
#zw3-good:checked ~ .zw3-demo-area .target-header .zw3-val {
  background-color: #fff;
  color: #198754;
}
#zw3-good:checked ~ .zw3-demo-area .target-header .zw3-val::after {
  content: " → 100に変更!";
}
#zw3-good:checked ~ .zw3-demo-area .target-modal {
  z-index: 200; /* ヘッダー(100)よりも大きくする! */
}
#zw3-good:checked ~ .zw3-demo-area .target-modal .zw3-val-modal::after {
  content: " → 200に変更!";
}

まとめ

分かりやすいようにまとめを記載します。

本記事のまとめ
  • 数値が大きいほど手前に表示される。
  • positionプロパティとセットで指定しないと効かない。
  • 数値は連番にせず10100など指定する(9999の乱用はNG)。
  • マイナス値(-1など)を使えば、背景の奥に要素を沈められる。
  • 効かない時は、「親要素のz-index」が負けていないかを疑う。
  • 親要素のopacitytransformも勝手に階層を作る原因になるため注意。

この記事を書いた人

sugiのアバター sugi Site operator

【経歴】玉川大学工学部卒業→新卒SIer企業入社→2年半後に独立→プログラミングスクール運営/受託案件→フリーランスエンジニア&SEOコンサル→Python特化のコンテンツサイトJob Code&UIコピペサイトCode Stock運営中

目次