Google Chromeの実験的な機能として、CSSのネストが実装されたらしいということで、仕様を読みながら実際にその挙動を確認してみようと思います。
本記事で紹介する「CSS Nesting Module」の仕様は現時点(2022/11/25)でFirst Public Working Draft(2022/8/31公開)であり、今後仕様が変更される可能性があります。
実験的な Web プラットフォームの機能
本記事の内容を確認するためにはchrome://flags/
にアクセスして、Experimental Web Platform features
をEnabled
にする必要があります。
現状の対応ブラウザ:https://caniuse.com/css-nesting
CSS Nesting Module
CSS Nesting ModuleはネイティブCSSでネスト(入れ子)を導入するための仕様です。
CSSでネストを使うためには、今までSassやLess、StylusなどのCSSプリプロセッサを導入する必要がありました。CSS Nesting Moduleがブラウザに実装されると以下のようなコードをネイティブのCSSで実現できます。
.parent {
color: red;
& > .child {
color: bluel
}
}
/* 以下のコードと同等に解釈される
.parent {
color: red;
}
.parent > .child {
color: blue
}
*/
挙動を試してみる
以下の挙動はFirst Public Working DraftだけではなくEditor’s Draftの内容も参考にしています(個人的に試した限りだと「Editor’s Draft」の内容も反映されているようでした)。
参考:https://bugs.chromium.org/p/chromium/issues/detail?id=1095675
基本的なネスト
例えば次のようなコードがあるとします。
<div class="parent">
parent
<div class="child">child</div>
</div>
.parent {
color: red;
}
.parent .child {
color: blue
}
このCSSをネストで表現しようとすると、次のように書くことができます。Sassユーザーには見慣れた形ですね。
.parent {
.child {
color: red;
}
}
もしくは&
を使って次のように書くこともできます。
.parent {
& .child {
color: red;
}
}
ChromeのExperimental Web Platform features
をEnable
にして確認すると、次の画像のようにスタイルが適用されることを確認できました。
要素型セレクター
要素型セレクターをネストで指定するときには&
をつける必要があります。もしくは:is()
使っても問題ありません。
要素型セレクターとは簡単にいうとh1
やp
などの要素をそのまま指定したセレクターのことです。
参考:要素型セレクター - CSS: カスケーディングスタイルシート | MDN
<ul>
<li>item - 01</li>
<li>item - 02</li>
<li>item - 03</li>
</ul>
ul {
& li {
color: red;
}
/* もしくは以下でもいい */
:is(li) {
color: red;
}
}
/* 以下のコードと同等
ul li {
color: red
}
*/
/* 以下はNG
ul {
li {
color: red
}
}
*/
複数のネスト
複数ネストも可能です。
<div class="hoge">
hoge
<div class="fuga">
fuga
<div class="piyo">piyo</div>
</div>
</div>
.hoge {
color: red;
.fuga {
color: blue;
.piyo {
color: green;
}
}
}
/* 以下と同等
.hoge {
color: red;
}
.hoge .fuga {
color: blue;
}
.hoge .fuga .piyo {
color: green;
}
*/
隣接セレクタ
隣接セレクタは次のように書くことができます。
<ul class="list">
<li class="item">item - 01</li>
<li class="item">item - 02</li>
<li class="item">item - 03</li>
</ul>
.item {
+ .item {
color: blue;
}
}
/* 以下と同等
.item + .item {
color: blue;
}
*/
擬似クラス
次のように書くことで擬似クラスのまとめることができます(例として:nth-child
を使用していますが、:hover
や:focus
などでも同様です)。
<ul>
<li>item - 01</li>
<li>item - 02</li>
<li>item - 03</li>
</ul>
ul {
& li {
color: red;
&:nth-child(2) {
color: blue;
}
&:nth-child(3) {
color: green;
}
}
}
/* 以下と同等
ul li {
color: red;
}
ul li:nth-child(2) {
color: blue;
}
ul li:nth-child(3) {
color: green;
}
*/
擬似要素
擬似要素も擬似クラスと同様にまとめることができます。
<div class="hoge">hoge</div>
.hoge {
&::before {
content: "before - ";
}
&::after {
content: "- after";
}
}
/* 以下と同等
.hoge::before {
content: "before - ";
}
.hoge::after {
content: "- after";
}
*/
1つの要素に複数のセレクタ
1つの要素に複数のセレクタを適用したいときは&
を使って次のように書くことができます。
<div class="fuga">fuga</div>
<div class="fuga hoge">fuga and hoge</div>
.fuga {
color: red;
&.hoge {
color: blue
}
}
/* 以下と同等
.fuga {
color: red;
}
.fuga.hoge {
color: blue;
}
*/
&
を連続で複数使う
あまり使うことはないかもしれませんが、&
を連続で使うこともできます。詳細度を上げるテクニックとしては有効ですが、決して乱用するものではありません。
参考:セレクターの複製による詳細度の向上
<div class="fuga">fuga</div>
.fuga {
color: red;
&& {
color: blue;
}
}
/* 以下と同等
.fuga {
color: red;
}
.fuga.fuga {
color: blue;
}
*/
:not
と&
を使う
:not
と&
を使うことで、次のようなコードも可能です。
<div class="hoge">hoge</div>
<div class="fuga">fuga</div>
.hoge {
color: red;
:not(&) {
color: blue;
}
}
/* 以下と同等
.hoge {
color: red;
}
:not(.hoge) {
color: blue;
}
*/
Sassの@at-root
の代替
次のようなコードでSassにおける@at-root
的な使い方も可能です。
<div class="hoge">hoge</div>
<div class="fuga">
<div class="hoge">fuga > hoge</div>
</div>
.hoge {
color: red;
.fuga & {
color: blue;
}
}
/* 以下と同等
.hoge {
color: red;
}
.fuga .hoge {
color: blue;
}
*/
メディアクエリー
次のように書くことで@media
のネストも可能です。
<div class="hoge">hoge</div>
.hoge {
color: red;
@media (min-width: 900px) {
color: blue;
}
}
/* 以下と同等
.hoge {
color: red;
}
@media (min-width: 900px) {
.hoge {
color: blue;
}
}
*/
また@media
の中に@media
をネストすることもできます。
.hoge {
color: red;
@media (min-width: 900px) {
color: blue;
@media (max-width: 1200px) {
color: green;
}
}
}
/* 以下と同等
.hoge {
color: red;
}
@media (min-width: 900px) {
.hoge {
color: blue;
}
}
@media (min-width: 900px) and (max-width: 1200px) {
.hoge {
color: green;
}
}
*/
詳細度に関して
通常、ネストを使用すると詳細度を高めることにつながります。
つまり次のコードでは.fuga
にcolor:blue
が適用されることになります。
<div class="hoge">
<div class="fuga">fuga</div>
</div>
.hoge {
.fuga {
color: blue;
}
}
.fuga {
color: red;
}
/* 以下と同等になるので.fugaは`color:blue`が適用される
.hoge .fuga {
color: blue;
}
.fuga {
color: red;
}
*/
:where()
の擬似クラス関数を使うことで、これを回避することができます。
.hoge {
:where(&) .fuga {
color: blue;
}
}
.fuga {
color: red;
}
/* 以下と同等になるので.fugaは`color:red`が適用される
:where(.hoge) .fuga {
color: blue;
}
.fuga {
color: red;
}
*/
:where()
を使うと詳細度を0に保つことができます。詳しくは以下をご参照ください。
:where() - CSS: カスケーディングスタイルシート | MDN
ネストと優先順位
.hoge {
& {
color: red;
}
}
.hoge {
color: blue;
}
上記のコードを書いたときに&{}
は詳細度を高めないので、後ろに書いたcolor:blue
が優先されます。
感覚的にもこれは問題ないように思えますね。
しかし、次ようなコードを書いたときには注意が必要です。
<div class="hoge">hoge</div>
.hoge {
color: red;
& {
color: blue;
}
color: green;
}
/* 以下と同等 `&{}`が優先される
.hoge {
color: red;
color: green;
color: blue;
}
*/
通常の宣言とネストされた宣言を混合して書くと、ネストされた宣言が優先されることに注意しましょう。またこの時、詳細度は変わりません。
セレクタの一部を&
で繋げる
BEMで見かける記法ですねですね。block__element--modifier
のelement
やmodifier
を&
で繋げていく記法になります。これはできませんのでご注意ください。
<div class="block">
block
<div class="block__element">block__element</div>
</div>
.block {
color: red;
/* 以下はSassと同等の挙動ではない */
&__element {
color: blue;
}
}
/* Sassでは以下となる
.block {
color: red;
}
.block__element {
color: blue;
}
*/
/* CSS Nesting Moduleでは以下のように解釈されるらしい
.block {
color: red;
}
__element.block {
color: blue;
}
*/
おわりに
CSS Nesting Moduleの仕様は現時点ではFirst Public Working Draftであり、今後仕様が変更される可能性は十分にあります。Editor’s Draftを読んでみても、まだまだ不安定な箇所があり、この段階で一喜一憂するものではないでしょう。
とはいえ、CSS Nesting Mdduleが導入されることでコードがクリーンになったり、楽になる部分もあると思いますので、各々のブラウザでサポートされるのが待ち遠しいですね。
また、PostCSSのプラグインを使うことで、現時点でもCSS Nesting Moduleの機能を導入することが可能です。興味がある方は以下のリンクをご参照ください。
PostCSS Nesting