ASP.NET Core CSS 分離

# はじめに
ASP.NET Core 6 (.NET 6)から、CSS 分離が利用できるようになっています。CSS 分離を使うことで、ページ単位での CSS の定義ができます。

# ページ単位での CSS ファイルの作成
対象ページと同一のディレクトリに、ファイル名 + .css の形式で、css ファイルを作成します。作成後、Visual Studio のソリューション エクスプローラーからは、ページの入れ子項目として表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/abb05e62-1236-c178-f03e-1708ac0459fd.png)

# CSS ファイルへのスタイルの定義
作成した CSS ファイルにスタイルを定義します。
例として、ファイル Index.cshtml.css を作成し、h1 要素のフォントを白、背景色を赤に設定しています。
```css:Index.cshtml.css
h1 {
    color: white;
    background-color: red;
}
```
比較のために別のページとして、ファイル Privacy.cshtml.css を作成し、h1 要素のフォントを灰色、背景色を青に設定します。
```css:Privacy.cshtml.css
h1 {
    color: gray;
    background-color: blue;
}
```

# CSS ファイルへのリンクの設定

"<アセンブリ名>.styles.css" の書式で、CSS ファイルが生成されるので、ページへ CSS ファイルへのリンクを設定します。ページ毎に設定しても良いですが、_Layout.cshtml 等のレイアウト ページに設定しておくと、一度で済みます。

```html:_Layout.cshtml
<link rel="stylesheet" href="~/<アセンブリ名>.styles.css" asp-append-version="true" />
```

# 実行結果の確認
Index.cshtml を見ると、Index.cshtml.css ファイルで定義したスタイルが適用されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/d91db1a7-06b0-cffc-771d-6eccedb4979a.png)
同様に、 Privacy.cshtml.css ファイルで定義したスタイルが適用されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/2fb19c40-62e4-d342-950b-d2d6f78de06b.png)

実行後の HTML ソースをみると、CSS で指定した h1 要素に、"b-<10 文字の文字列>" の書式で、属性が付与されていることが分かります。
```html:Index
<h1 b-x1wxtdv58m class="display-4">Welcome</h1>
```
ページ毎に異なる属性値が設定されています。
```html:Privacy
 <h1 b-w0r7xftrir>Privacy Policy</h1>
```

実行後の CSS ファイルをみると、ページ毎に作成した CSS ファイル内のスタイル定義が統合されて、要素の指定が、属性セレクターを使用するように変更されています。ページ毎の h1 要素に付与された属性に一致します。
```css:<アセンブリ名>.styles.css
h1[b-x1wxtdv58m] {
    color: white;
    background-color: red;
}

h1[b-w0r7xftrir] {
    color: gray;
    background-color: blue;
}
```
CSS 属性セレクターについては、以下を参照。

https://www.w3schools.com/cssref/sel_attribute.php

# 注意点
Razor ファイルの実行時コンパイルを有効にすると、CSS 分離が~~機能しなくなることがあります。~~ 機能しなくなります。
実行時コンパイルについては、以下を参照。

https://qiita.com/hiromasa-masuda/items/796866b8a8ca512a135c

.NET 6 + Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 6.0.16 の環境では、CSS 分離が機能しませんでした。Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 5.0.17 にダウングレードしたところ機能するようになりました。
以下に、この問題についての話があります。

https://stackoverflow.com/questions/70257443/adding-addrazorruntimecompilation-to-the-container-in-net-core-6-will-break-t

しかしながら、この方法では、CSS 分離は機能するようになりますが、実行時コンパイルが機能しません。
RuntimeCompilation をダウングレードすることで、実行時コンパイルが機能しなくなったことで、CSS 分離が機能するようになったように見えます。

実行時コンパイルを有効にすることで、CSS 分離が機能しなくなる問題は、以下にもあがっていました。

https://github.com/dotnet/aspnetcore/issues/33446

https://github.com/dotnet/aspnetcore/issues/38889

https://github.com/dotnet/aspnetcore/issues/42411

.NET 7 では、確認していませんが、現状では、この問題は改善されていないようです。
そこで、実行時コンパイルに替わる方法として、ホットリロードを使用します。
Visual Studio でコードを実行し、ホット リロードを実行すると、コードの変更が反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/f7d940c1-4359-f806-e885-8b6d33a496c9.png)
[ファイルの保存時のホット リロード] を選択しておくと、コードを変更時に、変更が反映されるので便利です。


# まとめ
各ページのスタイルを一つの CSS ファイルで管理すると、各ページで同一の要素のスタイルの管理が煩雑になります。ファイルを分割すると、リンクの設定忘れもあります。ASP.NET Core の CSS 分離を活用し、レイアウト ページに CSS ファイルの参照を設定しておくことで、ページ毎の CSS スタイルをページ単位で容易に管理できるようになります。