背景画像の上にテキストを表示したり、コンテンツを重ねるときに、CSSのz-indexだけだとどうしてもうまくいかない場合があります。
直感的にはz-indexでレイヤーの順序を変えてるので問題なく動きそうですが、なぜか効かないことも多いです。
ここからはz-indexが効かない場合の原因と解決方法をよくある実装例とサンプルコードで解説します。
擬似要素で背景を入れたときにz-indexが効かない場合の解決方法
実際によくある例として、背景画像の上にテキストなどのコンテンツを乗せるようなデザインがあります。
以下のHTMLコードのような構造で、bg-image-wrapperに擬似要素で背景画像を入れ、z-indexでコンテンツを上に表示しようするとこのようになります。
<div class="bg-image-wrapper">
  <div class="content-box">
    <h1>見出しテキスト</h1>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque ullam magni distinctio reprehenderit vel natus error similique quaerat dicta.</p>
  </div>
</div>.bg-image-wrapper {
  width: 1000px;
  position: relative;
  padding: 120px 100px;
}
.bg-image-wrapper:before {
  position: absolute;
  top: 0;
  left: 0;
  content: "";
  width: 100%;
  height: 100%;
  background-image: url('https://unsplash.it/800/500');
  background-repeat: no-repeat;
  background-position: center;
  /* background-size: cover; */
  z-index: 0;
}
.content-box {
  z-index: 999;
}結果はこちらのようになります。ここでは分かりやすくするため画像の幅を狭くしています。
             
          
CSSコードを見るとわかるようにテキスト部分のz-indexは999にしても最前面になってくれません。
背景画像の上にテキストなどの要素を表示するにはposition: relative;を使います。
.content-box {
  position: relative; /* この1行を追加 */
  z-index: 999;
}これでテキストが背景画像の上に表示されるようになりました(画像はランダムなので変わっちゃってます)。
             
          
背景だけ半透明にしたいときにz-indexが効かない場合の解決方法
半透明の背景の上にテキストを表示するのはWebサイトでよくあるデザインです。
             
          
この場合は背景画像の上に半透明の背景があるテキストなのレイヤーは3つあることになります。このとき半透明の画像を用意することで対応可能ですが、ここではCSSだけで解決してみます。
HTMLコードは先ほどと同じ。CSSでテキストボックスに背景をつけて半透明にしてみます。
<div class="bg-image-wrapper">
  <div class="content-box">
    <h1>見出しテキスト</h1>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque ullam magni distinctio reprehenderit vel natus error similique quaerat dicta.</p>
  </div>
</div>.bg-image-wrapper {
  width: 800px;
  position: relative;
  padding: 120px 100px;
}
.bg-image-wrapper:before {
  position: absolute;
  top: 0;
  left: 0;
  content: "";
  width: 100%;
  height: 100%;
  background-image: url('https://unsplash.it/800/500');
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}
.content-box {
  position: relative;
  z-index: 999;
  padding: 40px;
  background-color: #fff;
  opacity: 0.5;
}結果はこちらのようになります。
             
          
背景色は半透明になっていますが、テキストまで半透明になってしまっています。
この問題を解決するには背景を擬似要素で入れて、先ほどと同じようにpositionプロパティでテキストが最前面になるように調整します。
<div class="bg-image-wrapper">
  <div class="content-box">
    <div class="content-box-inner">
      <h1>見出しテキスト</h1>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque ullam magni distinctio reprehenderit vel natus error similique quaerat dicta.</p>
    </div>
  </div>
</div>.content-box {
  position: relative;
  z-index: 999;
  padding: 40px;
}
.content-box:before {
  position: absolute;
  top: 0;
  left: 0;
  content: "";
  width: 100%;
  height: 100%;
  background-color: #fff;
}
.content-box-inner {
  position: relative;
  z-index: 999;
}結果はこちらのようになります。
             
          
この例のように複数レイヤーで背景を重ねるときもz-indexとpositionプロパティで実装することができます。
まとめ
z-indexを使った表示順序の変更はコーディングでもよく使うので解決方法を覚えておくといろんな場面で活用できます。
z-indexが効かないときはとりあえずpositionプロパティで対応してみると大体うまきます。