9/7まで 最大30%OFF 初心者編|無料コーディング教材|HTML・CSS・jQueryを実践で学ぶ

CSS z-indexの使い方|重なり順と効かない原因を初心者向けに解説

z-index 1・2・3で重なり順が変わるWeb要素の図

CSSのz-indexは、重なった要素のうち「どれを手前に表示するか」を決めるプロパティです。固定ヘッダーが画像の後ろへ隠れる、メニューがモーダルより前に出ない、といった問題を直すときに使います。

ただし、単に大きな数値を指定しても効かないことがあります。この記事では基本構文から実際に要素を重ねるコード、スタッキングコンテキストの仕組み、効かない原因の調べ方まで、初心者向けに順番に解説します。

z-indexとは要素の重なり順を決めるCSSプロパティ

Webページには横方向のx軸、縦方向のy軸だけでなく、画面の奥から手前へ向かうz軸があると考えます。z-indexは、このz軸上で要素を並べる順序を整数で指定します。

同じスタッキングコンテキスト(重なりを比較するグループ)の中では、基本的に数値が大きい要素ほど手前に表示されます。負の整数、0、正の整数を使用でき、小数やpxなどの単位は付けません。

.back {
  position: relative;
  z-index: -1;
}

.middle {
  position: relative;
  z-index: 0;
}

.front {
  position: relative;
  z-index: 1;
}

z-indexの初期値はautoです。autoは親の数値をそのまま受け継ぐ値ではなく、その要素が新しいローカルなスタッキングコンテキストを作らず、現在のコンテキストではスタックレベル0として扱われる値です。

z-indexの基本的な使い方

まずは3枚のカードを少しずつずらし、z-indexで前後関係を指定してみましょう。次のHTMLをindex.htmlへ記述します。

<div class="stack-demo">
  <div class="card card--blue">z-index: 1</div>
  <div class="card card--green">z-index: 2</div>
  <div class="card card--orange">z-index: 3</div>
</div>

続いて、カードが重なるようにCSSを追加します。親要素を配置の基準にし、それぞれのカードへ異なるz-indexを設定します。

.stack-demo {
  position: relative;
  min-height: 260px;
}

.card {
  position: absolute;
  width: 220px;
  padding: 48px 24px;
  border-radius: 12px;
  color: #ffffff;
  font-weight: 700;
  text-align: center;
}

.card--blue {
  top: 0;
  left: 0;
  z-index: 1;
  background: #2563eb;
}

.card--green {
  top: 50px;
  left: 70px;
  z-index: 2;
  background: #059669;
}

.card--orange {
  top: 100px;
  left: 140px;
  z-index: 3;
  background: #ea580c;
}

表示すると、青、緑、オレンジの順に手前へ重なります。HTMLの記述順だけに頼らず、同じグループ内の重なり順を意図どおりに指定できました。

positionの基準やrelativeabsoluteの違いが分からない場合は、先に次の記事を確認すると理解しやすくなります。

z-indexが適用される要素

基本的には、positionrelativeabsolutefixedstickyのいずれかになっている「配置された要素」にz-indexを指定します。

.menu {
  position: absolute;
  z-index: 10;
}

よくある説明では「position: staticだとz-indexは効かない」とされます。通常のブロック要素ではその理解で構いませんが、Flexboxの子要素(flex item)とGridの子要素(grid item)は例外で、positionを変更しなくても整数のz-indexを利用できます。

.grid {
  display: grid;
}

.grid__item {
  /* Gridの子要素なのでpositionなしでも有効 */
  z-index: 1;
}

FlexboxやGridを含むdisplayの違いは、次の記事でサンプル付きで解説しています。

スタッキングコンテキストを理解する

z-indexが効かない問題の多くは、スタッキングコンテキストを見落としていることが原因です。スタッキングコンテキストとは、要素の重なり順を比較する独立したグループを指します。

子要素のz-indexは、まず所属する親のグループ内で比較されます。親同士の順序が決まった後は、各親とその子要素がひとまとまりとして扱われます。

親のz-indexを先に比較し、その中で子要素の重なり順が決まる仕組み
z-indexは子要素の数値だけでなく、親のスタッキングコンテキストごとに比較されます

たとえば親Aがz-index: 1、親Bがz-index: 2なら、親Aの子にz-index: 999を付けても親Bより手前には出ません。比べるべきなのは子の999と親Bの2ではなく、同じ階層にいる親Aの1と親Bの2だからです。

<div class="parent parent--a">
  <div class="child">子要素 z-index: 999</div>
</div>

<div class="parent parent--b">親B z-index: 2</div>
.parent {
  position: relative;
}

.parent--a {
  z-index: 1;
}

.parent--a .child {
  position: absolute;
  z-index: 999;
}

.parent--b {
  z-index: 2;
}

スタッキングコンテキストを作る主な条件

ルート要素であるhtmlのほか、次のような要素は新しいスタッキングコンテキストを作ります。すべてを暗記するより、DevToolsで親要素をたどるときの確認項目として使いましょう。

  • position: relativeまたはabsoluteで、z-indexがauto以外の要素
  • position: fixedまたはstickyの要素
  • z-indexがauto以外のflex itemまたはgrid item
  • opacityが1未満の要素
  • transformfilterperspectiveなどがnone以外の要素
  • isolation: isolateを指定した要素
  • contain: layoutcontain: paintを指定した要素

特にアニメーションでtransformを追加したときや、半透明にするためopacity: 0.99を付けたときは、意図せず重なりのグループが分かれやすいので注意してください。

z-indexが効かないときの確認ポイント

positionがstaticのままになっている

通常の要素にz-indexだけを追加しても変化しない場合は、まずpositionを確認します。レイアウトを大きく変えたくないなら、最初はposition: relativeを試せます。

.target {
  position: relative;
  z-index: 10;
}

親要素が別のスタッキングコンテキストを作っている

対象要素をz-index: 9999にしても前へ出ないなら、対象の数値をさらに増やす前に親要素を調べます。兄弟関係にある親要素同士のz-indexを調整するか、不要なスタッキングコンテキストを作る指定を見直してください。

transformやopacityが指定されている

親要素へtransform: translateZ(0)transform: translate(...)opacity: 0.99などが付いていると、新しいスタッキングコンテキストができます。表示高速化のために追加した古いCSSや、アニメーション用CSSも確認しましょう。

overflowで要素が切り取られている

要素が前へ出ないのではなく、親のoverflow: hiddenで外側が切り取られている場合もあります。これは重なり順とは別の問題なので、z-indexを増やしても解決しません。

dialogやpopoverがトップレイヤーにある

モーダル用のdialogやPopover APIで表示された要素などは、通常のスタッキングコンテキストより上の「トップレイヤー」へ配置されることがあります。通常要素のz-indexを大きくしても越えられないため、同じ仕組みの中で重なりを設計します。

DevToolsで親要素まで確認する

ブラウザの開発者ツールで対象要素を選び、Computed(計算済み)スタイルのpositionz-indexを確認します。次に親要素を一つずつたどり、transformopacityなど、スタッキングコンテキストを作る指定がないか調べると原因を絞れます。

固定ヘッダーでz-indexを使う例

実務でよく使うのが固定ヘッダーです。スクロール中もヘッダーを表示し、本文や画像より手前に置くには、position: stickyz-indexを組み合わせます。

<header class="site-header">
  <a href="/">Site Logo</a>
  <nav aria-label="メインナビゲーション">...</nav>
</header>
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: #ffffff;
  box-shadow: 0 1px 8px rgb(0 0 0 / 12%);
}

背景色を付けないと、ヘッダーの下に本文が透けて読みにくくなります。重なり順だけでなく、文字の読みやすさやフォーカス表示が隠れないかも確認してください。

ヘッダーを固定する一連の手順は、次の記事で詳しく解説しています。

z-indexの数値はルールを決めて管理する

「一番前にしたいから999999」のように場当たり的な値を増やすと、後から重なり順を変更しにくくなります。サイト内で用途ごとの段階を決め、必要な差だけを付ける方法がおすすめです。

:root {
  --z-base: 0;
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-overlay: 30;
  --z-modal: 40;
  --z-toast: 50;
}

.modal {
  position: fixed;
  z-index: var(--z-modal);
}

この値自体に世界共通の正解はありません。重要なのは、同じスタッキングコンテキストで比較される要素を把握し、プロジェクト内で一貫したルールを使うことです。

まとめ

z-indexは、重なった要素の表示順を整数で指定するCSSプロパティです。同じスタッキングコンテキストの中では大きな数値ほど手前になりますが、子要素の数値は別の親グループにいる要素とは直接比較されません。

効かないときは数値を増やし続けず、position、親要素のz-index、transform、opacity、overflow、トップレイヤーの順に確認しましょう。仕組みを理解すると、固定ヘッダー、ドロップダウン、モーダルの重なりを小さな数値で整理できます。

参考資料