特定の親要素を持つ子要素だけcssを当てない方法

特定の親要素に基づいて、その子要素にスタイルを適用したくない場面はよくあります。
実際、このブログを作成する際、<pre>タグ内の<code>要素にはスタイルを適用したくない状況が出たので、それについての備忘録です。

:notセレクタを使った例

:not()擬似クラスを使うと、特定の要素を除外してスタイルを適用することができます。

/* pre要素の外にあるcode要素だけにスタイルを適用する */
:not(pre) > code {
  background-color: #f0f0f0;
  padding: 0.2em 0.4em;
  border-radius: 4px;
}

この方法では、「直近の親がpreではないcodeにだけスタイルが適用されます。
ポイントは、not(pre) > code
子セレクタ
をすることです。

なぜ:not(pre) codeではだめなのか?

preの中にあるcode全てに適用したいから、:not(pre) codeだと思いがちですが、これだと意図しない挙動になってしまいます。
たとえば、以下のHTMLを見てください。

<div>
  <pre>
    <code>これはpre内のコードです</code>
  </pre>
</div>

この場合、divpreではないので、:not(pre)にマッチしてしまい、divの中にあるcodeにスタイルが当たってしまいます。
つまり、たとえpre内にあるcodeだとしても、スタイルが適用されてしまい、pre内のcodeにはスタイルを当てたくない」という目的とズレてしまいます。

all: unsetを利用した例

preは中にdivなどのブロック要素をいれることができませんが、例えば以下のようにcodepreの孫要素だとしてもスタイルを当てたくない場合、:not()だと指定が難しいです。

<pre>
  <span>
    <code>これはpre内のコードです</code>
  </span>
</pre>

:not()セレクタでは「親がpreじゃない」ことしか見れないため、孫要素(間に何かが挟まっている場合)まで正確に除外するのは難しいです。
そのため、確実に「pre内のcodeにはスタイルを当てたくない」場合は、all: unsetを使う方法が有効です。
all: unsetは要素に適用されているすべてのスタイルをリセットするcssプロパティです。

code {
  /* まずすべてのcodeにスタイルを適用 */
  background-color: #f0f0f0;
  padding: 0.2em 0.4em;
  border-radius: 4px;
}
pre code {
  all: unset; /* preの中にあるすべてのcodeはリセットする */
  background-color: #000;
}
  • 一旦code全部にスタイルを当てる
  • そのあと、pre配下のcodeをリセットする

ただし、「親から継承されるはずのプロパティ」などもリセットされる強力なプロパティなので、使い所にはかなり注意が必要です。

まとめ

以上、特定の親要素を持つ子要素だけcssを当てない方法について考察してきました。デモページをご用意しましたので、ぜひ参考にしてください。
目的にあった手段をうまく選んでいきましょう。