CSS ネスティング(入れ子構文)

作成日:2024年月1日

CSS Nesting の概要

2023年12月の時点で、主なモダンブラウザでは、 CSS でも Sass のようにセレクターを入れ子にする構文が使えるようになっています(Sass と全く同じではありませんが、入れ子で記述できます)。

以下は MDN CSS 入れ子からの抜粋です。

CSS 入れ子 (CSS nesting) モジュールは、セレクターを入れ子にする構文を定義し、親ルールのセレクターに対して子ルールのセレクターを相対的に指定することで、あるスタイルルールを別のスタイルルールの中に入れ子にする機能を提供します

CSS 入れ子は、 Sass のような CSS プリプロセッサーとは異なり、 CSS プリプロセッサーによって事前にコンパイルされるのではなく、ブラウザーで解釈されます。

例えば、以下のような HTML がある場合、

<section>
  <h3>H3 タイトル</h3>
  ・・・
</section>

従来は以下のように記述していた CSS を

section {
  margin: 3rem 0;
}

section h3 {
  color: #53845b;
}

以下のようにセレクタをネストして記述できるようになりました(入れ子の構文が使えます)。

section {
  margin: 3rem 0;

  h3 {
    color: #53845b;
  }
}

リリース時のバージョンでは、要素タグ(要素名のセレクター)をネストするには、&(入れ子セレクター)を記述する必要がありましたが、現在は要素タグの入れ子でも & の記述は必要(必須)ではありません。

以下のように &(入れ子セレクター)を記述した場合は、上記と同じことになります。

section {
  margin: 3rem 0;

  & h3 {
    color: #53845b;
  }
}

以下は定義リストのスタイルに入れ子を使用した例です。

dl.foo {
  margin: 2rem 0;

  dt {
    color: #53845b;
  }

  dd {
    margin-top: .5rem;
  }
}

上記は以下と同じことです。

dl.foo {
  margin: 2rem 0;
}

dl.foo dt {
  color: #53845b;
}

dl.foo dd {
  margin-top: .5rem;
}

特定の場面(複合セレクターなどを使用してセレクターを互いに結合する場合など)では、入れ子セレクター &(Nesting Selector)が必要になることがあります。

サポート状況

サポート状況の詳細は以下で確認できます。

https://caniuse.com/css-nesting

Baseline2023 NEWLY AVAILABLE と表示されていて、MDN では「2023年12月以降、この機能は最新のデバイスとブラウザーのバージョンで動作します。 この機能は、古いデバイスやブラウザでは動作しない可能性があります。」となっています。

古いブラウザへの対応

古いブラウザの対応が必要な場合は、以下のようなプラグインがあります。

PostCSS Nesting

入れ子セレクター(Nesting Selector)

入れ子を &(入れ子セレクター)なしで記述した場合、自動的にセレクターの間に空白を追加した子孫セレクタとして解釈されます(空白は子孫結合子)。

.foo {
  color: #999;
  .bar {
    background-color: pink;
  }
}

上記の場合、.foo と .bar が空白で連結されて以下のように子孫セレクタとして解釈されます。

.foo {
  color: #999;
}

.foo .bar {
  background-color: pink;
}

上記は、& を使って以下のように記述したのと同じことです( & と .bar の間に空白)。

.foo {
  color: #999;
  & .bar {
    background-color: pink;
  }
}

& と .bar の間に空白がない場合は、異なって解釈されます。

.foo {
  color: #999;
  /* & と .bar の間に空白がない */
  &.bar {
    background-color: pink;
  }
}

上記は以下と同じことになります。

.foo {
  color: #999;
}

.foo.bar {
  background-color: pink;
}

擬似クラスや複合セレクターでは & を直前に使用

例えば、以下のような HTML で .foo の文字色とホバー時の文字色を設定する場合、

<p><a href="#" class="foo">Foo</a></p>

以下のように & を使わずに記述すると、

.foo {
  color: green;

  :hover {
    color: pink;
  }
}

以下と同じことになり、.foo の子孫要素のホバー時の文字色の設定になり、リンク(.foo)にホバーしても色は変わりません。

この場合、例えば .foo に子要素(例えば span 要素)を追加して、その要素にホバーすれば、その要素の文字色がピンクになります(.foo *:hover と解釈されるため)。

.foo {
  color: green;
}

/* .foo と :hover の間に空白が入る */
.foo :hover {
  color: pink;
}

以下のように & を使って記述すれば、.foo:hover と解釈され、リンク(.foo)にホバーすると文字色が変わります。

.foo {
  color: green;

  &:hover {
    color: pink;
  }
}

上記は以下と同じことになります。

.foo {
  color: green;
}

.foo:hover {
  color: pink;
}

例えば、以下のような HTML で

<p class="bar">
  sample text with <a href="#">Link</a>
</p>

以下のように入れ子にすると、4行目の & はその親要素の .bar を表し、7と8行目の & はその親要素の & > a、つまり .bar > a を表すので

.bar {
  color: green;

  & > a {
    color: blue;
    text-decoration: none;
    &:hover,
    &:focus {
      color: tomato;
      background-color: #eee;
    }
  }
}

以下と同じことになります。

.bar {
  color: green;
}

.bar > a {
  color: blue;
  text-decoration: none;
}

.bar > a:hover,
.bar > a:focus {
  color: tomato;
  background-color: #eee;
}

入れ子セレクターの追加

入れ子セレクターは任意のセレクタの後に追加することもできます。

以下のような HTML がある場合、

<div class="foo">
  <p class="bar">text</p>
</div>

<div class="baz">
  <div class="foo">
    <p class="bar">text</p>
  </div>
</div>

以下の CSS は

.foo {
  border: 1px solid #999;

  .bar {
    background-color: pink;
  }

  .baz & .bar {
    background-color: lime;
  }
}

以下と同じことになります。

.foo {
  border: 1px solid #999;
}

.foo .bar {
  background-color: pink;
}

.baz .foo .bar {
  background-color: lime;
}

以下のように入れ子セレクターを後に追加することで、ルールのコンテキストを逆にすることもできます。

.foo {
  color: slateblue;

  .baz & {
    color: tomato;
  }
}

上記は以下と同じことになります。

.foo {
  color: slateblue;
}

.baz .foo  {
  color: tomato;
}

以下のような HTML がある場合、

<div class="foo">
  <h2>Foo</h2>
</div>

<div class="foo bar">
  <h2>Foo Bar</h2>
</div>

以下の & h2.foo h2)にネストした .bar &.bar.foo h2 と解釈されます。

.foo {
  display: inline-block;
  border: 1px solid black;
  & h2 {
    /* .foo h2 と同等 */
    color: blue;
    .bar & {
      /* .bar.foo h2 と同等 */
      color: red;
    }
  }
}

この場合、.bar &.barクラスを持つ要素内にある .foo h2を指します(.bar の子孫ではない)。

上記は以下と同じことになります。

.foo {
  display: inline-block;
  border: 1px solid black;
}

.foo h2 {
  color: blue;
}

.bar.foo h2 {
  color: red;
}

また、入れ子セレクターは複数配置することができます。

入れ子セレクターは擬似要素を表わせない

入れ子セレクターは擬似要素を表すことはできません。

<p class="foo">Text</p>

以下の CSS で入れ子セレクター & は、.foo にのみマッチします(疑似要素にはマッチしません)。

.foo, .foo::before, .foo::after {
  color: red;
  &:hover { color: blue; }
}

.foo::before, .foo::after {
  content:' ★ ';
}

上記は以下と同じことです。

.foo, .foo::before, .foo::after {
  color: red;
}

.foo:hover {
  color: blue;
}

.foo::before, .foo::after {
  content:' ★ ';
}

メディアクエリ

メディアクエリもネストすることができます。

.foo {
  color: blue;
  @media (min-width: 740px) {
    color: green;
  }
}

上記は以下と同じことです。

.foo {
  color: blue;
}

@media (min-width: 740px) {
  .foo {
    color: green;
  }
}

MDN: CSS 入れ子アットルール

詳細度

複数の親要素がある(セレクターリストの)場合、入れ子セレクターの詳細度はその中で最大の詳細度を使用して計算されます。以下の HTML で、

<div class="foo">
  <p>Text</p>
</div>

以下の CSS を記述すると、上記の Text の色は blue になります。これは、親要素のリストに詳細度の高い id セレクター(#bar)があるためです。

#bar, div {
  & p {
    color: blue;
  }
}

.foo p {
  color: red;
}

入れ子セレクターの詳細度は、:is() 関数を使用した場合の詳細度の計算方法と同じになっています。

上記の場合、:is(#bar, div) p { color: blue; }の方が.foo p { color: red;}より詳細度が高くなり、color: blue が適用されます。

:where() を使って入れ子セレクターの詳細度を 0 にすることもできます。

以下の場合、.foo p の方が詳細度が高くなり、color: red が適用されます。

#bar, div {
  /* :where() で詳細度を 0 に抑制 */
  :where(&) p {
    color: blue;
  }
}

.foo p {
  color: red;
}

複数の親要素に対して :where() を使用しても詳細度を 0 にすることができるので、この例の場合、上記と同じ結果になります(.foo p の方が詳細度が高くなり、color: red が適用されます)。

:where(#bar, div) {
  & p {
    color: blue;
  }
}

.foo p {
  color: red;
}

MDN: CSS 入れ子と詳細度

関連ページ:CSS 疑似クラス :is() と :where()