メインコンテンツまでスキップ
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

スタッキングコンテキスト

問題

スタッキングコンテキスト(stacking context)はCSSで最も誤解されている概念です。AIエージェントは、要素が他の要素の後ろに表示される理由を理解せずに、z-index の値を途方もない数値(99999)に引き上げることを繰り返します。根本的な原因はほぼ常に、要素が異なるスタッキングコンテキストに属しているということであり、z-indexをいくら上げても親のスタッキングコンテキストから抜け出すことはできません。

解決方法

スタッキングコンテキストは、独立したレイヤリンググループです。1つのスタッキングコンテキスト内の要素はz-orderの比較対象になりますが、異なるスタッキングコンテキストの要素とは比較されません。何がスタッキングコンテキストを作成するかを理解し、isolation プロパティを使って意図的に作成すれば、z-indexの戦いは解消されます。

スタッキングコンテキストを作るもの

以下のプロパティが要素に新しいスタッキングコンテキストを作成します:

常にスタッキングコンテキストを作成するもの

  • ルート要素(<html>
  • position: fixed
  • position: sticky

条件を満たすと作成するもの

  • position: relative または position: absolutez-indexauto 以外の場合
  • opacity1 未満
  • transformnone 以外の値
  • filternone 以外の値
  • backdrop-filternone 以外の値
  • perspectivenone 以外の値
  • clip-pathnone 以外の値
  • mask / mask-image / mask-bordernone 以外の値
  • mix-blend-modenormal 以外の値
  • isolation: isolate
  • 上記のプロパティのいずれかを指定した will-change
  • contain: layoutcontain: 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;
}

この例では、.dropdownz-index: 99999 を持っていますが、.main-content の後ろに表示されます。これは .sidebarz-index: 1 でスタッキングコンテキストを作成しているためです。ドロップダウンはそのスタッキングコンテキスト内に閉じ込められています。.main-content(z-index: 2)の視点からは、.sidebar グループ全体のz-indexは1です。

スタッキングコンテキスト: z-indexバグ(壊れた状態)

修正: 親のスタッキングコンテキストを取り除く

.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 */
}
スタッキングコンテキスト: z-indexバグ(修正後)

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

コンポーネントの漏洩防止

コンポーネントのルート要素に 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値をその要素のスタッキングコンテキスト内に閉じ込めます。

スタッキングコンテキストの問題をデバッグする

ステップバイステップの診断

  1. 正しくレイヤリングされていない要素を特定します。
  2. その要素からルートまでDOMツリーを上にたどります。
  3. 各祖先について、スタッキングコンテキストを作成しているかを確認します(上記のリストを使用)。
  4. レイヤリングが崩れるスタッキングコンテキストの境界を見つけます。
  5. 不要なスタッキングコンテキストを取り除くか、既存のコンテキスト内で動作するようにz-index値を再構成します。

ブラウザ DevTools

Chrome DevToolsでは、Layersパネル(More Tools > Layers)でスタッキングコンテキストを可視化できます。Firefox DevToolsではLayoutパネルにスタッキングコンテキスト情報が表示されます。

AIがよくやるミス

  • z-indexの値をエスカレーションする。 要素が最前面に表示されない場合、AIエージェントはz-indexを大きな数値に増やします。これはスタッキングコンテキストの問題を解決しません。修正するには、要素がどのスタッキングコンテキストに属しているかを理解する必要があります。
  • opacitytransformfilter がスタッキングコンテキストを作成することを知らない。 パフォーマンスのために opacity: 0.99transform: translateZ(0) を追加すると、子要素のz-indexの動作が壊れる可能性があります。
  • 位置指定されていない要素にz-indexを使う。 z-index は位置指定された要素(relativeabsolutefixedsticky)と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の要素の後ろに表示される場合
  • opacitytransformfilter を追加して予期しないレイヤリングの変化が起きた場合

参考リンク