SCSSのNesting（ネスト）はやめるべき！？詳細度を低く保つための解決策:where()の利用について

今ではSCSSだけでなくCSSでもネスト（入れ子）構造で記述できるようになりました。
私はこれまでネストで書くことは冗長さを防ぐし、良いものだと疑問を持たずに書いていました。

もはや常識になりつつある記述方法ですが、先日ある質問を受けたことを機に改めて「ネストで書くこと」について考えてみました。

## 先日受けた質問とは？
私：「ネストさせて、＆を使ってほしいです！」
質問者：「ネストは詳細度が高くなるので並列に書きたいです！また、`&`は検索性が悪くなるのでできれば使いたくないです！」

私の理想
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  &-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}
```

質問者の理想
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
}

.hoge-list {
  background-color: #fff;
  border: 1px solid #ccc;
}
```

## この記事について
私の理想と質問者の理想を合わせると下記になります。
- 非冗長に書きたい
- ネストさせたい
- 詳細度は低く保ちたい
- 検索性は担保したい

本記事は、Nestingについてまとめた上で解決策を模索する内容となっています。

:::note 
サイト設計やリプレイス時の参考にしていただければ嬉しいです！
初学者の方でも読んでいただけるように書いているので「解決方法だけ知りたい！」という方は「[【解決策】`:where()`を使う](https://qiita.com/tanimoto-hikari/items/6b58d69f51f86604e634#%E8%A7%A3%E6%B1%BA%E7%AD%96where%E3%82%92%E4%BD%BF%E3%81%86)」をご覧ください！
:::


## Nesting（ネスト）ってなに？
ネストはSCSSで下記のように記法することです。

```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  .hoge-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}
```

:::note
詳しく触れませんが、CSSもPostCSSの`postcss-nested`を利用することでネストして記述できます！
:::

## Nesting（ネスト）のメリットとデメリット
ネストのメリットとデメリットを改めて考えてみました。

:::note
ネストで書いていて良かった！逆に困ったことがあればぜひ教えてほしいのでコメントください！
:::

### Nesting（ネスト）のメリット
主要なメリットは下記の点だと考えています！
- 冗長になるのを防ぐ
- 構造を伝える＆縛る
- 縦に長くなるのを防ぐ

#### ネストのメリット①：冗長になることを防ぐ
ネストのメリットで1番に思い浮かんだのは冗長さを防ぐという点です。
cssで下記のような記述があったとします。
```css
.hoge {
  box-sizing: border-box;
  padding: 12px;
}

.hoge .hoge-list {
  background-color: #fff;
  border: 1px solid #ccc;
}
```
scssでは下記のように記述できます。
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  .hoge-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}

/*
  &を使うことによってclass名も再利用できます
  トランスパイルされた後の状態は上記と変わるので注意が必要
*/

.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  &-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}
```

#### ネストのメリット②：構造を伝える＆縛る
ネストさせておけばSCSSからどんな構造か推測できます。
下記のようなコードであれば、hogeの中にlistがあるんだ…ってのがわかります。
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  .hoge-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}
```

```html
<!-- scssから想像するHTML構造 -->
<div class="hoge">
  <ul class="hoge-list">
  </ul>
</div>
```

また、`hoge-list`は最も外側のclassが付与されたタグ内でしか使えないので構造的な縛りを与えることでます。

```html
<!-- hoge-listのスタイルはあたらない -->
<ul class="hoge-list">
</ul>

<!-- .hogeで囲まれている場合にのみスタイルがあたる -->
<div class="hoge">
  <ul class="hoge-list">
  </ul>
</div>
```
あえて縛り与えることで想定外の箇所で利用されることを防ぎclass利用のルールとして機能させられます。

#### ネストのメリット③：縦に長くなるのを防ぐ
下記のようにネストさせないと単純に縦に積むように記述するため縦に長くなりやすいです。
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
}

.hoge-list {
  background-color: #fff;
  border: 1px solid #ccc;
}
```

## Nesting（ネスト）のデメリット
主要なデメリットは下記の点だと考えています！
- 詳細度が高くなる
- 「＆」が重なる、ネストが深くなると読みづらくなる

### ネストのデメリット①：詳細度が高くなる
`&`を使うわずにネストして記述するとセレクタの詳細度が上がってしまいます…。

```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
    
  .hoge-list {
     background-color: #fff;
     border: 1px solid #ccc;
  }
}

// トランスパイル後
.hoge {
  box-sizing: border-box;
  padding: 12px;
}

.hoge .hoge-list {
  background-color: #fff;
  border: 1px solid #ccc;
}
```
`.hoge .hoge-list`の詳細度は0.2.0となり`.hoge-list`で定義した時よりも詳細度が高くなります。

詳細度の計算はツールを使うのが便利です！
セレクターを貼り付けると詳細度を表示してくれるツールです。

https://specificity.keegan.st/

ツールを使わずとも計算できるようになりたいという方は下記の記事を参考にしてください！

https://developer.mozilla.org/ja/docs/Web/CSS/Specificity

### ネストのデメリット②：「＆」が重なる、ネストが深くなると読みづらくなる
例えば下記のような状態をいいます。
```scss
.hoge {
  box-sizing: border-box;
  padding: 12px;
  
  & > * {
    & > {
      &::after {
      }
      
      .list {
        color: red;
        
        &-item {
          padding: 8px;
          
          & > .list-item-anchor {
            color: red;
          }
        }
      }
    }
  }
}
```
`&`とネストの使い方を誤ると冗長的で読みづらいコードが誕生します…。
プロパティが記述されるとさらに見通しの悪いコードになります…

:::note info
運用でネストが深くならないようにするのは限界があるのでLintを利用して制限をつけるのがオススメです！
:::

## 【解決策】`:where()`を使う
解決策として考えたのが`:where()`を利用して詳細度を下げる方法です！
```scss
:where(.hoge) {
  box-sizing: border-box;
  padding: 12px;
  border: 1px solid #ccc;
  margin-block-end: 10px;
    
  .hoge-list {
    background-color: #fff;
    border: 1px solid #ccc;
  }
}
```
`.hoge-list`をラップしている親classを`:where()`で囲むことで子要素の詳細度を下げられます！
本当に下がるのか？をもう少し簡単なコードで検証してみました！

```scss
.container {
  background: #999;
  color: #222;

  a {
    color: red;
  }
}

:where(.container) {
  background: #999;
  color: #222;

  a {
    color: blue;
  }
}

```
上記のコードが通常の詳細度であれば`.container`で囲まれたanchorタグの文字色は青色になるはずです。
`:where()`で`.container`を囲むことで詳細度を下げることでanchorタグの色は赤色になります。

### お試し用のコード
HTMLとトランスパイル後のCSSは下記です。
よろしければエディターやブラウザ等でお試しください！
```html
<style>
<!-- トランスパイル後のCSS -->
.container {
  background: #999;
  color: #222;
}
.container a {
  color: red;
}

:where(.container) {
  background: #999;
  color: #222;
}
:where(.container) a {
  color: blue;
}
</style>
<div class="container">
  <a href="">hoge1</a>
</div>
```

### `:where()`ってなに？
`:where()`で囲んだセレクターは詳細度を0にできます。
なので例に出したCSSの場合だと後に定義した`.container`を`:where()`でラップすることによって詳細度を先に定義した`.container a`とよりも低くなり、赤色のスタイルがanchorタグに当たるということです。

https://developer.mozilla.org/en-US/docs/Web/CSS/:where

:::note warn
`:is()`という似たような性質を持った擬似クラスがありますが、`:is()`は詳細度が0になるのではなく引数で指定したセレクタの中で最も高いものが選ばれる仕様なので詳細度を下げる。という用途で利用するのは少し不向きかもしれません…。
:::

### `:where()`のブラウザ互換性
サービスの状況によって変わると思うので新しいブラウザだけに対応する場合は問題なさそうです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/0280d47a-ec36-8784-b4aa-356db02f3ffa.png)

## 【まとめ】SCSS記法のNesting（ネスト）はやめるべき！？
そもそも、React等のライブラリを利用していれば考えなくても良いのでは？と言われそうですが、運用歴が長いサイトでは明日からライブラリ、フレームワークを導入します！
というのは無理なのでどうあるべきかを考えて、常に見直し続けることが大切だと今回の出来事で思いました。

もし、他の考え方やもっと良い解決策をお持ちの方はぜひコメントいただきたいです！
最後まで読んでいただきありがとうございました！

## 参考記事

https://qiita.com/xrxoxcxox/items/16002a866aa7ba8fb346

https://sass-lang.com/documentation/style-rules#nesting