ASP.NET Core や Blazor では、コンポーネントごとにスタイルを分離する「CSS 分離(CSS Isolation)」が標準で有効になっています。便利な一方で、「既存のグローバル CSS 設計と合わない」「ビルドで余計なファイルが増える」といった理由から、あえて無効化したいケースも少なくありません。この記事では、プロジェクト全体で CSS 分離をオフにする公式な方法と、一部だけ仕組みを発動させない実務的テクニック、そして無効化後にハマりがちなポイントを、サンプルコード付きで詳しく解説します。
ASP.NET Core の CSS 分離(CSS Isolation)とは何か
まずは前提として、ASP.NET Core/Blazor の CSS 分離がどのように動いているかをざっくり押さえておきます。仕組みを知っておくと「どこを触れば無効化できるか」がイメージしやすくなります。
CSS 分離の基本動作
CSS 分離は、次のような命名規則に従った CSS ファイルを検出すると自動的に動作します。
*.razor.css… Blazor コンポーネント(Razor コンポーネント)用*.cshtml.css… Razor Pages / MVC のビュー用
たとえば Counter.razor コンポーネントがある場合、同じフォルダーに Counter.razor.css を置くと、その CSS は Counter コンポーネント専用の「スコープ付き CSS」として扱われます。
ビルド時には次のような処理が行われます。
Counter.razor.cssの内容が中間ファイル(obj/配下の.scopedcssなど)にコンパイルされる- HTML 側には
b-xxxxxのようなスコープ用属性が自動的に付与される - 最終的に、アプリケーションごとのスタイルシート(例:
MyApp.styles.css)にマージされて出力される
この結果、そのコンポーネントにだけ適用されるセレクタが生成されるため、CSS クラス名の衝突やグローバルな副作用を抑えやすくなります。
通常の CSS と CSS 分離の違い
| 項目 | 通常の CSS(例: wwwroot/css/site.css) | CSS 分離(例: Counter.razor.css) |
|---|---|---|
| ファイル配置 | wwwroot 以下など静的ファイルとして配置 | コンポーネントと同じフォルダーに配置 |
| 読み込み方法 | <link> タグで手動読み込み | ビルド時に自動で結合され、ホストページに 1 枚の CSS として読み込まれる |
| スコープ | セレクタがマッチする要素すべて(グローバル) | 該当コンポーネントの要素に付与されるスコープ属性に限定 |
| 名前衝突 | クラス名が他ページと衝突しやすい | スコープ付きセレクタになるので衝突しにくい |
| デバッグ | 単純だが、どこで上書きされたか追いにくいことも | 生成後の CSS を確認する一手間が必要 |
この仕組み自体は便利なのですが、設計方針によっては「いったん全体で無効にしたい」「一部だけ通常 CSS に戻したい」というニーズも出てきます。
CSS 分離を無効化したい典型的なケース
実務でよく聞く「CSS 分離を切りたい理由」をいくつか挙げておきます。
- 既存アプリを ASP.NET Core に載せ替えただけで、CSS はこれまでどおりグローバル運用したい
- Tailwind CSS や Bootstrap などのユーティリティ系/フレームワーク系 CSS をメインに使っており、コンポーネント単位の分離は不要
- ビルド済みの
*.styles.cssが Source Map と合わせて大量に出力され、CI のログや成果物が見づらい - コンポーネント数が多く、
.razor.cssファイルが乱立して管理しきれない - スタイルガイドやデザインシステムを自前で運用しており、「全部グローバル CSS で統一する」と決めている
このような状況では、プロジェクト単位で CSS 分離そのものを無効化するか、一部コンポーネントだけ分離を使わないようにするほうが、長期的な保守コストを下げられます。
方法A: プロジェクト全体で CSS 分離を無効化する(推奨)
最もシンプルで安全なのが、*.csproj の設定で CSS 分離そのものをオフにする方法です。ASP.NET Core の公式ドキュメントでも案内されているやり方で、バージョンが進んでも通用しやすいアプローチです。
ScopedCssEnabled プロパティを false にする
対象プロジェクトの .csproj ファイルを開き、任意の <PropertyGroup> 内に次の 1 行を追加します。
<PropertyGroup>
<ScopedCssEnabled>false</ScopedCssEnabled>
</PropertyGroup>
すでに他の設定がある場合は、その <PropertyGroup> に追記して構いません。重要なのは、同じプロジェクトファイル内で ScopedCssEnabled が複数定義されないようにすることです。
この設定を行うと、
*.razor.css/*.cshtml.cssからの「スコープ付き CSS」の生成処理が無効化されるMyApp.styles.cssなど、CSS 分離を前提としたビルド成果物が出力されなくなるobj/配下の.scopedcss中間ファイルも作られなくなる
つまり、プロジェクト全体で「そもそも CSS 分離をしない」状態になります。
テンプレート別の挙動と注意点
ASP.NET Core のテンプレートによっては、あらかじめ CSS 分離を前提にした <link> タグが含まれていることがあります。
| プロジェクト種別 | CSS 分離関連の初期設定 | 無効化後に見直すファイル |
|---|---|---|
| Blazor WebAssembly | wwwroot/index.html 内で MyApp.styles.css を読み込む <link> がある場合あり | wwwroot/index.html |
| Blazor Server | Pages/_Host.cshtml 内に同様の <link> がある場合あり | Pages/_Host.cshtml |
| Razor Pages / MVC | Views/Shared/_Layout.cshtml に *.styles.css 読み込みがあるテンプレートも存在 | Views/Shared/_Layout.cshtml |
| Razor Class Library (RCL) | アプリ側の *.styles.css から @import される構成になる | ライブラリ利用側のホストページ |
ScopedCssEnabled=false にすると MyApp.styles.css 自体が生成されなくなるため、対応する <link> タグを削除しないと 404 エラーになります(詳細は後述の「既定リンクの整理」で解説)。
ビルド構成ごとに切り替える例
開発中は CSS 分離を試しつつ、本番では無効化したい、という場合はビルド構成ごとに切り替えることも可能です。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<ScopedCssEnabled>false</ScopedCssEnabled>
</PropertyGroup>
このように条件付きで設定しておけば、Debug ビルドでは CSS 分離あり、Release ビルドでは CSS 分離なしという運用もできます。CI で Release ビルドのみをデプロイしている場合などに有効です。
無効化後は一度クリーンビルドする
ScopedCssEnabled を切り替えたあと、一度「クリーン」→「再ビルド」することを強くおすすめします。
- 以前のビルドで生成された
obj/配下の.scopedcssファイルなどが残っていると、IDE の補完やビルド出力が紛らわしくなる - 古い
*.styles.cssがbin/配下に残っていると、設定変更が反映されているのか判断しづらい
CI 環境でも、CSS 分離の有効/無効を切り替えたコミットでは、キャッシュをクリアしてからビルドし直しておくと安心です。
方法B: 命名規則を避けて CSS 分離を「発動させない」
プロジェクト全体では CSS 分離を使いたいが、「このコンポーネントだけ」「このビューだけ」は通常の CSS として扱いたい、というケースでは、命名規則をわざと外すという手があります。
CSS 分離は、すでに見たとおり *.razor.css / *.cshtml.css という拡張子に反応して動作します。逆に言えば、その名前さえ避ければ、仕組み自体は動きません。
分離用ファイルを通常の .css にリネームする
すでに CSS 分離を使っているコンポーネントを、あえて通常 CSS に戻したいときの手順例です。
Counter.razor.cssをCounter.cssにリネームするwwwroot/css/components/など、静的ファイル用のフォルダーに移動する- ホストページやレイアウトから
<link>で読み込む
たとえば _Layout.cshtml であれば、次のような記述になります。
<link rel="stylesheet" href="~/css/components/Counter.css" />
これで Counter コンポーネント用の CSS も、通常のグローバル CSS と同じ扱いになります。
新規コンポーネントでは最初から分離 CSS を作らない
チームとして「CSS 分離は原則使わない」という方針なら、コンポーネント追加時のテンプレートやレシピを整備しておくと事故が減ります。
- コンポーネント作成時に
.razor.cssを自動生成しない(IDE のスニペットを修正) - スタイルは
wwwroot/css/pagesやwwwroot/css/components配下にまとめる - 「新しいページを作るときは、この CSS ファイルに追記する」というルールを README などで共有する
こうしておけば、.razor.css / .cshtml.css がそもそも存在しないため、CSS 分離の仕組みは一切動きません。プロジェクトによっては、こちらの運用だけで十分な場合もあります。
命名規則回避 vs プロジェクト全体無効化の比較
| 観点 | 方法A: ScopedCssEnabled=false | 方法B: 命名規則を避ける |
|---|---|---|
| 設定の明示性 | プロジェクトファイル 1 か所で一括制御できる | ファイル名ルールに依存するため、ぱっと見で意図が分かりにくい |
| 細かい制御 | プロジェクト単位でオン/オフのみ | コンポーネント単位で分離を使う/使わないを選べる |
| 既存コードへの影響 | 既存の分離 CSS がすべて通常 CSS として扱われるようになる | リネームしたファイルだけが通常 CSS になる |
| チーム開発での分かりやすさ | プロジェクトルートで「CSS 分離は無効」と一目で分かる | 命名ルールを周知徹底しないと混乱しやすい |
プロジェクト全体で CSS 分離を使わない方針なら方法A、部分的に使い分けたいなら方法B、というのが実務上の落としどころです。
方法C: 既定の *.styles.css のリンクを整理する
方法Aで ScopedCssEnabled=false にした場合、CSS 分離で生成される *.styles.css ファイルが出力されなくなります。にもかかわらず、レイアウトやホストページ側に <link> タグが残っていると、アプリ起動時に 404 エラーを出してしまいます。
代表的なテンプレートを例に、確認すべき箇所を整理しておきます。
Blazor WebAssembly の例
wwwroot/index.html を開き、次のような記述がないか確認します。
<link href="MyApp.styles.css" rel="stylesheet" />
ここで MyApp は通常、プロジェクト名(アセンブリ名)です。CSS 分離を無効にした場合、このファイルは生成されなくなるため、この <link> を丸ごと削除して構いません。
Blazor Server の例
Blazor Server では、ホストページは Pages/_Host.cshtml です。こちらにも同様の記述がある場合があります。
<link href="MyApp.styles.css" rel="stylesheet" />
この場合も同様に、CSS 分離を使わないなら 削除またはコメントアウトします。
Razor Pages / MVC の例
Razor Pages や MVC のテンプレートでは、Views/Shared/_Layout.cshtml に CSS 分離の .styles.css を読み込む <link> が入っている場合があります。
<link rel="stylesheet" href="~/MyApp.styles.css" />
ここも同様に削除すれば OK です。かわりに、site.css などグローバル CSS を読み込む <link> がきちんと残っているかどうかも合わせて確認しておきましょう。
CSS 分離を無効化したあとのスタイル設計のポイント
CSS 分離をオフにすると、すべての CSS がグローバルに作用するようになります。これは昔ながらの Web 開発の感覚に近い一方で、気を抜くとすぐに「どこを変えたらどこが壊れるか分からない」状態になりがちです。
無効化後のスタイル設計で意識しておきたいポイントを整理します。
セレクタのスコープと詳細度を意識する
- 汎用クラス(例:
.btn,.text-danger)は最上位の共通 CSS にまとめる - ページ固有のスタイルは、ページごとのラッパー要素(例:
.page-dashboard)の下にぶら下げる #idや不要に長い子孫セレクタは避け、上書きしやすいセレクタにする
CSS 分離がないぶん、DOM 構造とクラス設計で「どこまで影響させるか」をきちんと区切ることが重要です。
CSS ファイルの分割戦略を決める
単に 1 枚の site.css にすべて書き込んでいくと、数万行クラスの巨大ファイルになりがちです。たとえば次のような分割戦略をとると、運用しやすくなります。
| ファイル名 | 役割 | 読み込み順の例 |
|---|---|---|
base.css | リセット CSS/タイポグラフィ/共通レイアウト | 1番目(すべてのベース) |
components.css | ボタン、フォーム、カードなど再利用コンポーネント | 2番目 |
layout.css | ヘッダー/サイドバー/フッターなどアプリ共通レイアウト | 3番目 |
pages/*.css | ページ固有のスタイル(必要なときのみ読み込み) | 4番目以降(必要なページでのみ追加) |
ビルドパイプラインでこれらを 1 枚にまとめるかどうかは、アプリ規模やデプロイ構成に合わせて決めると良いでしょう。
コンポーネント設計と CSS 設計を紐付ける
CSS 分離がない環境でも、コンポーネントごとにクラス名のプレフィックスを付けることで、それに近い効果を得られます。
- コンポーネント名:
OrderCard→ CSS クラス:.oc-card,.oc-header,.oc-footerなど - ページ名:
Dashboard→ ルート要素に.page-dashboardクラスを付け、その下でのみ.chart-areaなどを定義
こうすることで「このクラスはどのコンポーネントのものか」がひと目で分かり、チーム内の CSS 汚染も抑えやすくなります。
CSS 分離を「完全にやめる」のではなく部分的に使う、という選択肢
ここまで「無効化する」話を中心にしてきましたが、実務では 一部コンポーネントだけ CSS 分離を活かすという折衷案も有力です。
- アプリ全体のベーススタイルやレイアウトはグローバル CSS で運用
- 一方で「外部配布を前提とした UI ライブラリ」「別プロジェクトからも利用される共通コンポーネント」は CSS 分離で厳密にスコープする
特に、Razor Class Library(RCL)として公開するコンポーネントでは、CSS 分離を使っておくことで、ライブラリ利用者のスタイルと衝突しにくくなります。
このように、
- アプリ固有の UI → グローバル CSS で素早く実装
- 再利用前提の UI → CSS 分離で厳密にスコープ
と用途を分けると、設計の筋が通りやすくなります。
CSS 分離まわりのその他の設定と無効化パターン
「CSS 分離そのものは使いたいが、一部の挙動だけ変えたい」という場合に役立つ、関連プロパティも簡単に触れておきます。
自動バンドルだけを無効化する: DisableScopedCssBundling
Blazor では、デフォルトで「各コンポーネントごとに生成された scoped CSS を、ひとつのファイルにまとめて読み込む」仕組みが用意されています。これを無効にしたいときは、プロジェクトファイルに次のように設定します。
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
この場合、「CSS 分離」自体は有効のままですが、生成された scoped CSS ファイルをどうバンドルして公開するかは開発者が責任を持って行うことになります。Webpack や Vite といった他のツールチェーンと組み合わせたい場合には有効な選択肢です。
静的 Web アセットのベースパスを変える: StaticWebAssetBasePath
生成される scoped CSS の配置パスを変えたい場合は、StaticWebAssetBasePath プロパティでベースパスを指定できます。これにより、_content/... など、任意の基準パス配下にアセットをまとめられます。
CSS 分離を完全に無効にするわけではありませんが、「どこに何が出力されているか」を明確にしたい場合に覚えておくと便利です。
よくある質問とトラブルシューティング
Q. ScopedCssEnabled=false にしたのに、以前のスタイルが効いているように見えます
A. 多くの場合、ブラウザーキャッシュか、古いビルド成果物が原因です。
- ブラウザーのスーパーリロード(キャッシュ無視)を試す
- プロジェクトで「クリーン」→「再ビルド」を実行する
bin/やobj/を一度削除してからビルドし直す
それでも解消しない場合は、開発ツールで「実際にどの CSS ファイルがロードされているか」を確認し、想定外の .styles.css などが残っていないかをチェックしましょう。
Q. 一部のコンポーネントだけ CSS 分離に戻したくなりました
A. もしプロジェクト全体で ScopedCssEnabled=false にしている場合、そのプロジェクト内で CSS 分離を使うことはできません。そのコンポーネントだけ別のクラスライブラリ(RCL)に切り出し、そちらでは CSS 分離を有効にする、という構成が現実的です。
まだ全体無効化まではしていない場合は、方法Bで紹介したように、.razor.css / .cshtml.css の命名ルールを復活させるだけで、そのコンポーネント単位の CSS 分離を再度利用できます。
Q. 既存の .razor.css を通常 CSS に移すとき、どこまで書き換える必要がありますか?
A. CSS 分離では、ビルド時にスコープ用の属性が自動付与されるため、生成後の CSS ではセレクタが書き換えられています。しかし、元の .razor.css ファイルの中身自体は、普通の CSS セレクタです。
そのため、
.razor.css内のコードをそのまま通常の.cssにコピペしても構文上は問題ない- ただし、他ページとクラス名が衝突する可能性があるため、必要に応じてプレフィックスを付けるなどリファクタリングしたほうが安全
コンポーネントが他とほとんど連携しない小さな UI であれば、そのまま移行しても大きな問題にならない場合もありますが、中長期的にはクラス名の整理をおすすめします。
チーム開発でのルール作りのヒント
CSS 分離を無効化する・しないにかかわらず、チームで開発するなら「スタイル周りのルール」を決めておくと、後から入ってきたメンバーも迷いません。ここでは、CSS 分離を無効化する前提でのルール例を挙げます。
- プロジェクトの README に「
ScopedCssEnabled=falseを設定済みである」ことを明記する - 新規コンポーネント作成時の手順(どの CSS ファイルを編集するか)をテンプレート化する
- クラス名のルール(例: BEM 風、プレフィックス付きなど)を決めて共有する
- PR レビュー時に「グローバルに影響しそうな CSS が増えていないか」をチェック項目に入れる
特に、Razor コンポーネント単位で見ると HTML だけでは影響範囲が分かりにくいため、「どの CSS によってスタイルが当たっているのか」をコメントやドキュメントで補足しておくと安心です。
まとめ: 方針ごとに最適な無効化パターンを選ぶ
ASP.NET Core の CSS 分離は強力な機能ですが、アプリの性質やチームの文化によっては「使わないほうがシンプル」というケースも十分にあります。最後に、この記事で紹介した選択肢を簡単にまとめます。
- プロジェクト全体で CSS 分離をやめたい
→.csprojに<ScopedCssEnabled>false</ScopedCssEnabled>を設定する(方法A)。
あわせて*.styles.cssを読む<link>を削除し、クリーンビルドを行う。 - 一部コンポーネントだけ通常 CSS に戻したい
→ 対象の.razor.css/.cshtml.cssを通常の.cssにリネームし、wwwroot/cssなどに移動してレイアウトから<link>読み込みする(方法B)。 - CSS 分離は使うが、バンドル方法だけ変えたい
→<DisableScopedCssBundling>true</DisableScopedCssBundling>を設定し、生成された scoped CSS を独自のビルドパイプラインで処理する。
いずれの方法を選ぶにしても、「なぜこのプロジェクトではこの設定にしているのか」を README やアーキテクチャドキュメントに一言残しておくと、未来の自分やチームメンバーが迷わずにすみます。CSS 分離を理解したうえで、プロジェクトにとって最適なレベルのスコープと運用方法を選んでいきましょう。

コメント