スタッキングコンテキスト
問題
スタッキングコンテキスト(stacking context)はCSSで最も誤解されている概念です。AIエージェントは、要素が他の要素の後ろに表示される理由を理解せずに、z-index の値を途方もない数値(99999)に引き上げることを繰り返します。根本的な原因はほぼ常に、要素が異なるスタッキングコンテキストに属しているということであり、z-indexをいくら上げても親のスタッキングコンテキストから抜け出すことはできません。
解決方法
スタッキングコンテキストは、独立したレイヤリンググループです。1つのスタッキングコンテキスト内の要素はz-orderの比較対象になりますが、異なるスタッキングコンテキストの要素とは比較されません。何がスタッキングコンテキストを作成するかを理解し、isolation プロパティを使って意図的に作成すれば、z-indexの戦いは解消されます。
スタッキングコンテキストを作るもの
以下のプロパティが要素に新しいスタッキングコンテキストを作成します:
常にスタッキングコンテキストを作成するもの
- ルート要素(
<html>) position: fixedposition: sticky
条件を満たすと作成するもの
position: relativeまたはposition: absoluteでz-indexがauto以外の場合opacityが1未満transformがnone以外の値filterがnone以外の値backdrop-filterがnone以外の値perspectiveがnone以外の値clip-pathがnone以外の値mask/mask-image/mask-borderがnone以外の値mix-blend-modeがnormal以外の値isolation: isolate- 上記のプロパティのいずれかを指定した
will-change contain: layout、contain: paint、またはcontain: strict
コード例
典型的な z-index バグ
.sidebar {
position: relative;
z-index: 1;
}
.sidebar .dropdown {
position: absolute;
z-index: 99999; /* Will NOT appear above .main-content */
}
.main-content {
position: relative;
z-index: 2;
}
この例では、.dropdown は z-index: 99999 を持っていますが、.main-content の後ろに表示されます。これは .sidebar が z-index: 1 でスタッキングコンテキストを作成しているためです。ドロップダウンはそのスタッキングコンテキスト内に閉じ込められています。.main-content(z-index: 2)の視点からは、.sidebar グループ全体のz-indexは1です。
修正: 親のスタッキングコンテキストを取り除く
.sidebar {
position: relative;
/* Remove z-index to avoid creating a stacking context */
}
.sidebar .dropdown {
position: absolute;
z-index: 10;
}
.main-content {
position: relative;
/* No z-index needed */
}
isolation プロパティ
isolation: isolate はスタッキングコンテキストを作成する最もクリーンな方法です。新しいスタッキングコンテキストを作成するという1つのことだけを行います。副作用なし、視覚的な変化もありません。
.card {
isolation: isolate; /* Creates stacking context */
}
.card .background {
position: absolute;
inset: 0;
z-index: -1; /* Stays behind card content but inside card's context */
}
.card .content {
position: relative;
/* z-index: auto, but still above .background due to DOM order */
}
<div class="card">
<div class="background"></div>
<div class="content">Card text</div>
</div>
コンポーネントの漏洩防止
コンポーネントのルート要素に isolation: isolate を使い、z-indexが外部に漏れるのを防ぎましょう。
/* Each component is self-contained */
.modal-overlay {
isolation: isolate;
position: fixed;
inset: 0;
z-index: 100;
}
.tooltip {
isolation: isolate;
position: absolute;
}
.dropdown-menu {
isolation: isolate;
position: absolute;
}
意図しないスタッキングコンテキスト
レイヤリングとは無関係に見えるプロパティが、サイレントにスタッキングコンテキストを作成することがあります。
/* This creates a stacking context! */
.fading-element {
opacity: 0.99;
}
/* This also creates a stacking context! */
.animated-element {
transform: translateZ(0); /* Often added for "GPU acceleration" */
}
/* This too! */
.blurred-element {
filter: blur(0px);
}
これらのいずれも、子要素のz-index値をその要素のスタッキングコンテキスト内に閉じ込めます。
スタッキングコンテキストの問題をデバッグする
ステップバイステップの診断
- 正しくレイヤリングされていない要素を特定します。
- その要素からルートまでDOMツリーを上にたどります。
- 各祖先について、スタッキングコンテキストを作成しているかを確認します(上記のリストを使用)。
- レイヤリングが崩れるスタッキングコンテキストの境界を見つけます。
- 不要なスタッキングコンテキストを取り除くか、既存のコンテキスト内で動作するようにz-index値を再構成します。
ブラウザ DevTools
Chrome DevToolsでは、Layersパネル(More Tools > Layers)でスタッキングコンテキストを可視化できます。Firefox DevToolsではLayoutパネルにスタッキングコンテキスト情報が表示されます。
AIがよくやるミス
- z-indexの値をエスカレーションする。 要素が最前面に表示されない場合、AIエージェントはz-indexを大きな数値に増やします。これはスタッキングコンテキストの問題を解決しません。修正するには、要素がどのスタッキングコンテキストに属しているかを理解する必要があります。
opacity、transform、filterがスタッキングコンテキストを作成することを知らない。 パフォーマンスのためにopacity: 0.99やtransform: translateZ(0)を追加すると、子要素のz-indexの動作が壊れる可能性があります。- 位置指定されていない要素にz-indexを使う。
z-indexは位置指定された要素(relative、absolute、fixed、sticky)とflex/gridアイテムにのみ作用します。静的に配置された要素には効果がありません。 - コンポーネント境界に
isolation: isolateを使わない。 内部でz-indexを使う再利用可能なコンポーネントはすべて、z-index値が親コンテキストに漏れるのを防ぐためにisolation: isolateを使うべきです。 - 一般的な「修正」として
position: relative; z-index: 1を追加する。 これは新しいスタッキングコンテキストを作成し、目の前の問題は解決するかもしれませんが、すべての子孫のz-index値を閉じ込め、別の場所で新たな問題を引き起こします。
使い分け
isolation: isolate を使うべき場合
- 内部でz-indexを使う再利用可能なコンポーネントの構築
- 視覚的な副作用なしでスタッキングコンテキストが必要な場合
z-index: -1の要素を親要素内に留めたい場合- mix-blend-mode が透過するのを防ぎたい場合
z-index を使うべき場合
- 同じスタッキングコンテキスト内の位置指定された要素の順序を制御する場合
- ページレベルでオーバーレイ、モーダル、ドロップダウンをレイヤリングする場合
スタッキングコンテキストを監査すべき場合
- z-indexの値が「効かない」場合
- 高いz-indexを持つ要素が低いz-indexの要素の後ろに表示される場合
opacity、transform、filterを追加して予期しないレイヤリングの変化が起きた場合