Flexbox(reverse)で並べた子要素の重なり順がChrome 108以降で変わっている話

昨年末にリリースされた Chrome と Edge の 108 以降では、Flexbox で並べた子要素の重なり順がこれまでと逆になるという現象が発生しています。一方、Firefox や Safari ではこれまでの Chrome と同じ表示のままで、変化はありません。

読者の方からいただいたメールで気付くことができましたので、原因と対処方法をまとめておきます(本当にありがとうございました)。

エビスコムの書籍でこの現象の影響を受けたのは『HTML&CSS コーディング・プラクティスブック 4』です。この本の作例に対処方法を反映する手順やコードについては、ダウンロードデータを更新しましたので参照してください。

原因

この現象の原因を調べてみたところ、どうやら Chrome と Edge(いずれも Blink)のバグのようです。

1395330 - Using flex-direction: row-reverse now layers child elements in the opposite order. - chromium
https://bugs.chromium.org/p/chromium/issues/detail?id=1395330

Flexbox の flex-direction: row-reverse や column-reverse で子要素の並び順を変え、z-index なしで重ねたときに発生します。


並び順を変えていない場合

たとえば、HTML で[画像 <figure>] → [テキスト <h1>]の順に記述したものを Flexbox で横並びにし、並び順を変えずにネガティブマージン(マイナスマージン)で重ねてみます。

z-index は指定していないため、重なり順は DOMツリーに従い、後から記述したテキストが上になります。この場合、Chrome 108 以降でも Firefox や Safari と同じ重なり順になり、問題は発生しません。

Flexboxで横並びにし、並び順を変えずに画像とテキストを重ねたもの。Firefox、Safari、Chromeで「テキストが上になる」同じ表示になります。
<div class="flex">
<figure><img src="lemon.jpg" alt="" /></figure>
<h1>Fresh Lemon</h1>
</div>
.flex {
display: flex;
}
.flex > h1 {
margin-left: -80px;
}

並び順を変えた場合

しかし、flex-direction: row-reverse で並び順を逆にすると、Chrome 108 以降では重なり順が逆になってしまいます。

Flexboxで横並びにし、flex-direction: row-reverse で並び順を逆にして画像とテキストを重ねたもの。Firefox、Safariではテキストが上になりますが、Chrome 108以降では画像が上になります。
.flex {
display: flex;
flex-direction: row-reverse;
}
.flex > h1 {
margin-right: -80px;
}

Flexboxの仕様書では、flex-direction の項目に「reverse 値はボックスの順序を逆転させない」「ペイント順、読み上げ順、ナビゲーション順も影響を受けない」との注意書きがあります。

NOTE: The reverse values do not reverse box ordering

https://w3c.github.io/csswg-drafts/css-flexbox/#flex-direction-property

そのため、row-reverse を適用してもボックスの順序(重なり順)を逆転させない、Firefox や Safari によるこれまで通りの表示が正しいと言えるでしょう。

対処方法

Chrome 108 以降で重なり順が逆になるのを防ぐためには、z-index で重なり順を明示します。

たとえば、<h1> に z-index: 1 を適用すると、row-reverse で並びを逆にしていてもテキストが上になり、ほかのブラウザと表示がそろいます。

Flexboxで横並びにし、flex-direction: row-reverse で並び順を逆にして画像とテキストを重ねたもの。テキストにz-indexを適用することで、Firefox、Safariに加え、Chrome 108以降でもテキストが上に重った表示になります。
.flex {
display: flex;
flex-direction: row-reverse;
}
.flex > h1 {
margin-right: -80px;
z-index: 1;
}

現時点では、Chrome の canary 版(112)でも修正は入っていません。こうした問題が発生することを考えると、要素を重ねるときには確実に z-index を指定した方がよいのかもしれません。