2026年02月01日
こんにちは!今日もプログラミングを楽しんでいますか?
これまでの3回にわたり、CSSの「重ね合わせコンテキスト」の仕組みと、それがどのように「レイヤー」として機能するかを解説してきました。
最終回となる今回は、これまでの知識を総動員して、重ね順を『設計』し、コントロールする方法を解説します。もう「とりあえず z-index: 9999;を打つ」必要はありません。
今回作るWebサイトの要件
以下の要素を持つ、標準的なモダンサイトを想定します。
- ヘッダー: 画面上部に常に固定。
- メインコンテンツ: 内容に応じてスクロール。
- ハンバーガーメニュー: 画面左からスライドして出現する。出現時は背景をぼかす。
- モーダル(
<dialog>): ボタンクリックで中央に出現。背景をぼかす。
まずは「レイヤー構造」を可視化する
いきなりCSSを書き始めるのは、設計図なしでビルを建てるようなものです。まずは今回作るサイトの構成を、横から見た「層(レイヤー)」として整理します。これができていれば、実装で迷うことはありません。
こんかいは、このように整理してみました。
- Layer 0 (底面): 本文(背景、通常のテキスト、画像)。
- Layer 1 (固定要素): ヘッダー (
position: fixed)。 - Layer 2 (オーバーレイ): ハンバーガーメニュー (
position: fixed)。 - 最上位層:
<dialog>要素。
ここで重要なのは、「何が、何を追い越すべきか」という優先順位の確定です。
ヘッダーと本文の「力関係」を固定する
まず、ヘッダーを position: fixed; で固定します。
header {
position: fixed;
top: 0;
background: rgba(255, 255, 255, 0.8);
}
main {
/* いろんな設定 */
}ここで「メインコンテンツ内の画像に transform をかけたら、ヘッダーを突き抜けて上にきてしまった!」……というトラブルは「あるある」のひとつです。
header {
position: fixed;
top: 0;
background: rgba(255, 255, 255, 0.8);
}
main {
/* いろんな設定 */
}
main img {
transform: translateX(10px);
}

これを z-index: 10000; を使わずに解決するには、どうすればいいのでしょうか?
答え
突き抜けたのは、transform によって、メインコンテンツ側に新しい「重ね合わせコンテキスト」が発生したからです。重ね合わせコンテキストは、きょうだいレイヤー同士であればあとから登場したほうが手前に来ます。レイヤー設計していない失敗例の典型です。なので、最初に考えた設計を、実装に反映させましょう。
header {
position: fixed;
/* ヘッダーの強さは 1 でしたね */
z-index: 1;
top: 0;
background: rgba(255, 255, 255, 0.8);
}
main {
position: relative; /* デフォルト(static)だと、z-index の対象にできません! */
/* 本文の強さは 0 でしたね */
z-index: 0;
/* これで main は、晴れて「レイヤーコンテナ」となりました! */
}
main img {
/* img は、「main レイヤーコンテナ」の子要素となりました!
もう、header の「きょうだいレイヤー」にはなりません! */
transform: translateX(10px);
}すると、結果は……。

ぶじ、期待通りの動作になりました。header と main がきょうだいレイヤーとなった以上、もう main の中で重ね合わせコンテキストがどんなに暴れまわっても、header の上に来ることは決してありません! 設計の重要さが、わかってきたでしょうか?
ハンバーガーメニューと「ボカし」の設計
次に、画面を覆うハンバーガーメニューです。ここで「背景をボカしたい(backdrop-filter)」という要望に応えてみましょう。
よくある失敗は、メニューの背後に「黒い半透明の板(オーバーレイ)」を、メニューの子要素として配置してしまうこと。これでは、ボカしたい対象(メインコンテンツ)のきょうだいレイヤーではなくなるため、うまくボカせないことがあります。
実戦的な設計: メニュー自体を z-index: 2; とし、「その要素の疑似要素」で背景を覆います。
nav {
position: fixed;
inset: 0;
/* ハンバーガーメニューの強さは 2 でしたね */
z-index: 2;
}
/* メニューの背景としてのオーバーレイ */
nav::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px); /* 確実に背後をボカす */
z-index: -1; /* メニュー内の各リンクより下に配置 */
}
はい、ご覧の通りです。z-index: 2 という小さな数字で、ヘッダー(1)もメイン(0)もすべてを安全に覆い隠せました。
最強のカード <dialog> を切る
最後に、モーダルウインドウです。 これまでの CSS の常識では、モーダルは「HTML 構造の最後に置く」のが鉄則でした。そうしないと、どこかの親要素の overflow: hidden; や z-index に妨げられるという、デバッグの困難な不具合を引き起こしてしまっていたからです。
しかし、現代に生きる私たちに、そんな必要はありません。そう、 <dialog> が生み出す最上位層が、多くのケースで解決してくれるからです!
// JSで呼び出す
const modal = document.querySelector('#my-modal');
modal.showModal(); // これが魔法の呪文
<dialog> については第2回で説明していますので、そちらを参照してください。
まとめ:z-indexのインフレを止めるのは「あなたの設計」
全4回の連載を通じてお伝えしたかったのは、「z-indexは、場当たり的な修正のためのツールではない」ということです。
- 「きょうだいレイヤー」と「レイヤーコンテナ」を意識して、親の階層を「設計」する
- 大きな数字ではなく、意味のある小さな数字(0, 1, 2…)で管理する
- ブラウザの標準機能(
<dialog>)のお世話になる
この3点を守るだけで、CSS における「重ね順」のメンテナンス性は劇的に向上します!
最後に、今までの集大成のコード例をお見せします。
お疲れさまでした。それでは、よきコーディングライフを!