CSSのネスト(入れ子)の書き方とつまずきやすい点
CSS のネスト(入れ子)を使うと、.card の中の .title のスタイルを、.card { .title { … } } のように親のルールの中に書けます。Sass などのプリプロセッサーを通さなくても、ブラウザがそのまま読み取ります。ただし、見た目は Sass とほぼ同じでも、& の扱いや詳細度の計算が違うところがあり、そこで思ったとおりにスタイルが効かないことがあります。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。検証用のページを Chromium で開き、要素ごとに getComputedStyle() で実際に効いている値を調べました。あわせて、ブラウザが CSS をどう読み取ったかを、document.styleSheets のルールの一覧(CSSOM)で確かめています。Firefox と Safari は試していません。
基本の書き方
親のルールの中に、セレクターとスタイルをもう一組書きます。
.card {
padding: 8px;
.title { color: green; }
}
これは .card .title と書いたのと同じ意味になります。検証環境で試したところ、.card の中にある .title は緑になり、.card の外にある .title は色が変わりませんでした。
ブラウザが読み取ったルールを CSSOM で見ると、入れ子にした .title は、.card のルールの子として & .title という形で入っていました。
CSSStyleRule ".card" { padding: 8px; }
CSSStyleRule "& .title" { color: rgb(0, 128, 0); }
& は、外側のルールのセレクター(ここでは .card)を表す記号です。MDN の & 入れ子セレクターのページにあるとおり、& を書かずに入れ子にすると、ブラウザは親のセレクターとの間に空白を入れて読みます。空白は「中にある要素」という意味なので、.title は .card の子孫の要素だけに当たります。
MDN の「CSS 入れ子の使用」によると、+ などの結合子も、そのまま入れ子の先頭に書けます。h2 { + p { … } } は h2 + p と同じ意味です。
:hover の前に & を付け忘れると、ボタン自身には効かない
いちばん間違えやすいのが、疑似クラスの前の & です。
.btn {
:hover { background: #d85878; }
}
こう書くと、空白が入って .btn :hover という意味になります。つまり、「.btn の中にある要素のうち、マウスが重なっているもの」です。ボタン自身にマウスを重ねても、色は変わりません。ボタン自身の状態を指すには、& を空白なしで付けます。
.btn {
&:hover { background: #d85878; }
}
検証環境で、2つの書き方のボタンにそれぞれマウスを重ねたところ、色が変わったのは &:hover と書いたほうだけでした。

CSSOM で見ても、& のないほうは & :hover と、間に空白の入ったセレクターとして読み取られていました。
同じことが、クラスを2つ重ねる書き方にも言えます。.tab { .active { … } } は .tab の中にある .active に、.tab { &.active { … } } は class="tab active" の要素自身に当たります。検証でも、前者のスタイルは中の要素にだけ、後者のスタイルはタブ自身にだけ効きました。
Sass のように & の後ろにクラス名の続きを書けない
Sass では、&__title と書くと、親のクラス名に文字をつなげて .block__title というクラスを作れます。BEM という命名の決まりでよく使う書き方です。CSS のネストでは、これができません。
.block {
&__title { color: #d85878; }
}
検証環境では、このルールはまるごと無視されました。CSSOM を見ると、.block のルールの中に、入れ子のルールが1つも残っていません。

MDN の「CSS 入れ子の使用」には、CSS の入れ子では、結合子を使わない場合、入れ子のセレクターは要素型セレクターとして扱われると書かれています。&__title の __title は、__title という名前の要素を指すことになります。そして、要素型セレクターは複合セレクターの先頭に書く決まりなので、& の後ろにあると、そのセレクターとルールのブロック全体が無効になります。
CSS のネストで BEM のクラス名を使うときは、入れ子にせず .block__title と書くか、入れ子の中で & .block__title のようにクラス名を省略せずに書きます。検証環境でも、& .block__title の形で書いたルールは、親の要素の中の見出しに効きました。
要素名と & を組み合わせるときは、要素名を前に書く
同じ理由で、§ion のように & の後ろに要素名を書くことはできません。要素名と & をつなげるときは、section& と書きます。
.post {
section& { border-top: 3px solid blue; }
}
検証環境では、section& は <section class="post"> に効き、§ion と書いたルールは無視されました。
一方、入れ子のルールを要素名から書き始めること(.post { p { … } })は、検証環境で問題なく効きました。MDN のブラウザ互換性データによると、Chrome は 112 から 119 まで、Safari は 16.5 から 17.1 まで、要素名で始まる入れ子のルールに対応していませんでした。MDN の「CSS 入れ子の使用」でも、& を付ける理由の1つに後方互換性が挙げられています。こうした古いブラウザで表示される可能性があるなら、& p のように & から書き始めておきます。
詳細度は :is() と同じ計算になる
CSS のネストでは、& の詳細度を :is() と同じように計算します。そのため、親のセレクターに詳細度の違うものがカンマで並んでいるときは、入れ子にせずにセレクターを1つずつ書いた場合と、詳細度が変わります。
MDN の「CSS 入れ子と詳細度」と同じ例で確かめました。
#a, b {
& i { color: blue; }
}
.foo i { color: red; }
<b class="foo"><i>青になる</i></b>
<i> が入っているのは <b> なので、ID の #a は使われていません。それでも、& の詳細度は、並んだセレクターの中でいちばん高い #a のものになります。そのため & i が .foo i より強くなり、検証環境でも文字は青になりました。
比べるために、同じ形のルールを入れ子にせず #c i, u i と書き、.bar i と競わせたところ、こちらは赤になりました。u i は ID を含まないので、.bar i のほうが強いからです。セレクターを1つずつ書いていた CSS を入れ子に書き換えると、こうしてスタイルの勝ち負けが変わることがあります。入れ子の中のスタイルを、後から書いたクラスのセレクターで上書きしたいこともあります。そのときは、ID と要素名のように詳細度の違うセレクターを、別々のルールに分けて書きます。
& は疑似要素を表せない
MDN の & 入れ子セレクターのページにあるとおり、& は疑似要素を表せません。.note::before { .important & { … } } と書いても、::before には当たりません。検証でも、.important の中にある .note::before の色は変わりませんでした。
疑似要素は、& の後ろに付けて書きます。.note { .important &::before { … } } と書いたところ、.important の中にある .note::before の色が変わりました。
@media も入れ子にできる
@media や @supports、@container などは、ルールの中に入れ子にできます(MDN の「CSS 入れ子アットルール」)。中には、セレクターを書かずにプロパティを直接書けます。
.side {
width: 300px;
@media (width < 600px) { width: 100px; }
}
検証環境で表示領域の幅を変えたところ、幅 800px では 300px、幅 500px では 100px になりました。
入れ子のルールの後に書いたプロパティは、書いた順に効く
プロパティを、入れ子のルールより後ろに書くこともできます。
.order {
color: red;
@media screen { color: blue; }
color: green;
}
検証環境では、文字は緑になりました。上から順に読んで、最後に書いた color: green が効いています。CSSOM で見ると、後ろの color: green は、.order 自身のスタイルではなく、CSSNestedDeclarations という別のルールとして、@media の後ろに入っていました。
CSSStyleRule ".order" { color: rgb(255, 0, 0); }
CSSMediaRule "screen"
CSSNestedDeclarations { color: rgb(0, 0, 255); }
CSSNestedDeclarations { color: rgb(0, 128, 0); }
CSS Nesting の仕様には、以前は入れ子のルールの後ろにあるプロパティも、ルールの先頭に移して扱っていたと書かれています。MDN の「CSS 入れ子の使用」にも、CSSNestedDeclarations に対応していないブラウザでは、入れ子のルールを間違った順序で解釈する場合があると書かれています。ブラウザ互換性データでは、対応したのは Chrome 130、Firefox 132、Safari 18.2 です。どのブラウザでも同じ結果にしたいなら、プロパティは入れ子のルールより前にまとめて書きます。
無効なセレクターがあっても、ほかのルールは残る
入れ子のルールのセレクターが無効なとき、無視されるのはそのルールだけです。
.parent {
& %invalid { color: red; }
& .valid { color: green; }
}
検証環境では、% を含むルールは CSSOM から消えていましたが、その後ろの & .valid は残り、.valid の文字は緑になりました。&__title や §ion のルールが消えたのも、同じ仕組みです。入れ子のスタイルが効かないときは、document.styleSheets の cssRules をたどって、そのルールが残っているかを確かめると、書き方の間違いに気づけます。





