ASP.NET Core Blazor の CSS の分離

作成者: Dave Brock

CSS の分離により、グローバル スタイルへの依存を防ぐことで、アプリの CSS 占有領域を簡素化し、コンポーネントおよびライブラリ間のスタイルの競合を回避するのに役立ちます。

CSS の分離を有効にする

コンポーネント固有のスタイルを定義するには、同じフォルダー内のコンポーネントの .razor ファイルの名前と一致する .razor.css ファイルを作成します。 .razor.css ファイルは、"スコープ付き CSS ファイル" です。

Example.razor ファイル内の Example コンポーネントの場合、コンポーネントと共に Example.razor.css という名前のファイルを作成します。 Example.razor.css ファイルは、Example コンポーネント (Example.razor) と同じフォルダー内に存在する必要があります。 ファイルの "Example" ベース名では、大文字と小文字が区別 されません

Pages/Example.razor:

@page "/example"

<h1>Scoped CSS Example</h1>

Pages/Example.razor.css:

h1 { 
    color: brown;
    font-family: Tahoma, Geneva, Verdana, sans-serif;
}

Example.razor.css で定義されるスタイルは、Example コンポーネントのレンダリングされる出力にのみ適用されます。 CSS の分離は、一致する Razor ファイル内の HTML 要素に適用されます。 アプリ内の他の場所で定義されるどの h1 CSS 宣言も、Example コンポーネントのスタイルと競合しません。

注意

バンドルが発生したときにスタイルの分離を保証するために、Razor コード ブロックでの CSS のインポートはサポートされていません。

CSS の分離のバンドル

CSS の分離は、ビルド時に発生します。 Blazor により、コンポーネントによってレンダリングされるマークアップと一致するように CSS セレクターが書き換えられます。 これらの書き換えられた CSS スタイルは、バンドルされ、静的アセットとして生成されます。 このスタイルシートは、wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の <head> タグ内で参照されます。 次の <link> 要素は、既定では、Blazor プロジェクト テンプレートから作成されたアプリに追加されます。ここで、プレースホルダー {ASSEMBLY NAME} はプロジェクトのアセンブリ名です。

<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">

次の例は、ホストされている Blazor WebAssembly Client アプリからのものです。 アプリのアセンブリ名は BlazorSample.Client です。<link> は、プロジェクトがホステッド オプションを使用して作成される際に (.NET CLI を使用する場合は -ho|--hosted オプション、Visual Studio を使用する場合は [ASP.NET Core Hosted](ASP.NET Core ホステッド) チェックボックス)、Blazor WebAssembly プロジェクト テンプレートによって追加されます。

<link href="BlazorSample.Client.styles.css" rel="stylesheet">

バンドルされたファイル内で、各コンポーネントはスコープ識別子に関連付けられます。 スタイルが設定されるコンポーネントごとに、HTML 属性が形式 b-<10-character-string> を使用して追加されます。 識別子は一意であり、アプリごとに異なります。 レンダリングされる Counter コンポーネントでは、Blazor により、スコープ識別子が h1 要素に追加されます。

<h1 b-3xxtam6d07>

{ASSEMBLY NAME}.styles.css ファイルは、スコープ識別子を使用してスタイル宣言をそのコンポーネントと共にグループ化します。 次の例では、前述の <h1> 要素のスタイルを示します。

/* /Pages/Counter.razor.rz.scp.css */
h1[b-3xxtam6d07] {
    color: brown;
}

ビルド時、規則 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css でプロジェクト バンドルが作成されます。ここで、プレースホルダー {STATIC WEB ASSETS BASE PATH} は静的な Web アセットのベース パスです。

NuGet パッケージや Razor クラス ライブラリなどの他のプロジェクトを利用する場合、バンドル ファイルは次のようになります。

  • CSS インポートを使用してスタイルを参照する。
  • スタイルを使用するアプリの静的な Web アセットとして公開されない。

子コンポーネントのサポート

既定で、CSS の分離は形式 {COMPONENT NAME}.razor.css に関連付けられたコンポーネントにのみ適用されます。ここで、プレースホルダー {COMPONENT NAME} は、通常、コンポーネント名です。 子コンポーネントに変更を適用するには、親コンポーネントの .razor.css ファイル内の子孫要素に ::deep 連結子を使用します。 ::deep 連結子により、要素の生成されたスコープ識別子の "子孫" である要素が選択されます。

次の例は、Child という名前の子コンポーネントを持つ Parent という名前の親コンポーネントを示します。

Pages/Parent.razor:

@page "/parent"

<div>
    <h1>Parent component</h1>

    <Child />
</div>

Shared/Child.razor:

<h1>Child Component</h1>

::deep 連結子を使用して Parent.razor.css 内の h1 宣言を更新し、h1 スタイル宣言を親コンポーネントとその子に適用する必要があることを示します。

Pages/Parent.razor.css:

::deep h1 { 
    color: red;
}

これで、子コンポーネント用の個別のスコープ付き CSS ファイルを作成する必要なく、h1 スタイルは、ParentChild の各コンポーネントに適用されます。

::deep 連結子は、子孫要素でのみ機能します。 次のマークアップでは、想定どおりに h1 スタイルがコンポーネントに適用されます。 親コンポーネントのスコープ識別子が div 要素に適用されるため、ブラウザーでは、スタイルを親コンポーネントから継承することが認識されます。

Pages/Parent.razor:

<div>
    <h1>Parent</h1>

    <Child />
</div>

ただし、div 要素を除外すると、子孫関係が削除されます。 次の例では、スタイルは子コンポーネントに適用 されません

Pages/Parent.razor:

<h1>Parent</h1>

<Child />

CSS プリプロセッサのサポート

CSS プリプロセッサは、変数、入れ子、モジュール、mixin、継承などの機能を利用することで CSS 開発を改善するのに役立ちます。 CSS の分離は、SASS や LESS などの CSS プリプロセッサをネイティブにサポートしていませんが、ビルド プロセス中に Blazor により CSS セレクターが書き換えられる前にプリプロセッサのコンパイルが行われる限り、CSS プリプロセッサの統合はシームレスです。 たとえば、Visual Studio を使用して、Visual Studio タスク ランナー エクスプローラーで既存のプリプロセッサ コンパイルを ビルド前 タスクとして構成します。

Delegate.SassBuilder などの多くのサードパーティ製 NuGet パッケージは、CSS の分離が発生する前に、ビルド プロセスの開始時に SASS または SCSS ファイルをコンパイルできます。追加の構成は必要ありません。

CSS の分離の構成

CSS の分離は、すぐに使用できるように設計されていますが、既存のツールやワークフローへの依存関係がある場合など、一部の高度なシナリオ用の構成も用意されています。

スコープ識別子の形式をカスタマイズする

既定では、スコープ識別子に形式 b-<10-character-string> が使用されます。 スコープ識別子の形式をカスタマイズするには、プロジェクト ファイルを目的のパターンに更新します。

<ItemGroup>
  <None Update="Pages/Example.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>

前の例では、Example.razor.css 用に生成された CSS により、そのスコープ識別子は b-<10-character-string> から my-custom-scope-identifier に変更されます。

スコープ識別子を使用して、スコープ付き CSS ファイルでの継承を実現します。 次のプロジェクト ファイルの例では、BaseComponent.razor.css ファイルに、コンポーネント間の共通スタイルが含まれています。 DerivedComponent.razor.css ファイルでは、これらのスタイルが継承されます。

<ItemGroup>
  <None Update="Pages/BaseComponent.razor.css" CssScope="my-custom-scope-identifier" />
  <None Update="Pages/DerivedComponent.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>

ワイルドカード (*) 演算子を使用して、複数のファイル間でスコープ識別子を共有します。

<ItemGroup>
  <None Update="Pages/*.razor.css" CssScope="my-custom-scope-identifier" />
</ItemGroup>

静的な Web アセットのベース パスを変更する

scoped.styles.css ファイルは、アプリのルートで生成されます。 プロジェクト ファイルでは、StaticWebAssetBasePath プロパティを使用して既定のパスを変更します。 次の例では、scoped.styles.css ファイルとアプリの残りのアセットを _content パスに配置します。

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

自動バンドルを無効にする

Blazor でスコープ付きファイルを公開し、それを実行時に読み込む方法をオプトアウトするには、DisableScopedCssBundling プロパティを使用します。 このプロパティを使用する場合、obj ディレクトリからの CSS ファイルの分離と、それらの公開および実行時の読み込みを他のツールまたはプロセスが担当することを意味します。

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

Razor クラス ライブラリ (RCL) のサポート

Razor クラス ライブラリ (RCL) により分離スタイルが提供される場合、<link> タグの href 属性は {STATIC WEB ASSET BASE PATH}/{ASSEMBLY NAME}.bundle.scp.css を指します。ここで、プレースホルダーは次のとおりです。

  • {STATIC WEB ASSET BASE PATH}: 静的な Web 資産のベース パス。
  • {ASSEMBLY NAME}: クラス ライブラリのアセンブリ名。

次に例を示します。

  • 静的な Web 資産のベース パスは _content/ClassLib です。
  • クラス ライブラリのアセンブリ名は ClassLib です。

wwwroot/index.html (Blazor WebAssembly) または Pages_Host.cshtml (Blazor Server):

<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">

RCL および Razor クラス ライブラリの詳細については、次の記事を参照してください。