ASP.NET Core Blazor の CSS の分離

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

作成者: Dave Brock

この記事では、CSS の分離を使用して Razor コンポーネントに CSS のスコープを設定する方法について説明します。これにより、CSS が簡素化され、他のコンポーネントやライブラリとの競合を回避できます。

CSS スタイルを個々のページ、ビュー、コンポーネントに分離して、次のものを減らすか回避します。

  • 維持が困難なグローバル スタイルへの依存関係。
  • 入れ子になったコンテンツでのスタイルの競合。

CSS の分離を有効にする

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

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

Example.razor:

@page "/example"

<h1>Scoped CSS Example</h1>

Example.razor.css:

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

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

Note

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

CSS の分離のバンドル

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

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

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

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

バンドルされたファイル内で、各コンポーネントはスコープ識別子に関連付けられます。 スタイル付きコンポーネントごとに、HTML 属性に b-{STRING} の形式が追加されます。ここで、{STRING} プレースホルダーは、フレームワークによって生成される 10 文字の文字列です。 この識別子はアプリごとに一意です。 レンダリングされる Counter コンポーネントでは、Blazor により、スコープ識別子が h1 要素に追加されます。

<h1 b-3xxtam6d07>

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

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

ビルド時、規則 obj/{CONFIGURATION}/{TARGET FRAMEWORK}/scopedcss/projectbundle/{ASSEMBLY NAME}.bundle.scp.css でプロジェクト バンドルが作成されます。ここで、プレースホルダーは次のようになります。

  • {CONFIGURATION}: アプリのビルド構成 (DebugRelease など)。
  • {TARGET FRAMEWORK}: ターゲット フレームワーク (net6.0 など)
  • {ASSEMBLY NAME}: アプリのアセンブリ名 (例: BlazorSample)。

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

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

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

Parent.razor:

@page "/parent"

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

    <Child />
</div>

Child.razor:

<h1>Child Component</h1>

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

Parent.razor.css:

::deep h1 { 
    color: red;
}

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

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

Parent.razor:

<div>
    <h1>Parent</h1>

    <Child />
</div>

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

Parent.razor:

<h1>Parent</h1>

<Child />

::deep 擬似要素は、スコープ属性が規則に適用される場所に影響を与えます。 スコープ付き CSS ファイルで CSS 規則を定義すると、そのスコープは既定で右端の要素に適用されます。 たとえば、div > adiv > a[b-{STRING}] に変換されます。この {STRING} プレースホルダーは、フレームワークによって生成される 10 文字の文字列 (例: b-3xxtam6d07) です。 代わりに、規則を別のセレクターに適用したい場合は、::deep 擬似要素を使うと実行できます。 たとえば、div ::deep > adiv[b-{STRING}] > a に変換されます (例: div[b-3xxtam6d07] > a)。

任意の HTML 要素に ::deep 擬似要素をアタッチする機能を使うと、レンダリングされた HTML タグの構造を特定できる場合に、他のコンポーネントによってレンダリングされた要素に影響を与えるスコープ付き CSS スタイルを作成できます。 別のコンポーネント内にハイパーリンク タグ (<a>) をレンダリングするコンポーネントの場合は、そのコンポーネントを div (または他の任意の要素) でラップし、規則 ::deep > a を使って、親コンポーネントのレンダリング時にのみそのコンポーネントに適用されるスタイルを作成するようにしてください。

重要

スコープ付き CSS は "HTML 要素" にのみ適用され、Razor コンポーネントやタグ ヘルパーには適用されません (<input asp-for="..." /> のようにタグ ヘルパーが適用された要素など)。

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

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

AspNetCore.SassCompiler などの多くのサードパーティ製 NuGet パッケージは、CSS の分離が発生する前に、ビルド プロセスの開始時に SASS または SCSS ファイルをコンパイルできます。

CSS の分離の構成

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

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

既定では、スコープ識別子には、b-{STRING} の形式が使用されます。ここで、{STRING} プレースホルダーは、フレームワークによって生成される 10 文字の文字列です。 スコープ識別子の形式をカスタマイズするには、プロジェクト ファイルを目的のパターンに更新します。

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

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

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

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

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

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

既定では、スコープ識別子には、b-{STRING} の形式が使用されます。ここで、{STRING} プレースホルダーは、フレームワークによって生成される 10 文字の文字列です。 スコープ識別子の形式をカスタマイズするには、プロジェクト ファイルを目的のパターンに更新します。

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

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

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

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

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

<ItemGroup>
  <None Update="Pages/*.razor.css" CssScope="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>

CSS の分離を無効にする

アプリのプロジェクト ファイルで <ScopedCssEnabled> プロパティを false に設定することでプロジェクトの CSS 分離を無効にします。

<ScopedCssEnabled>false</ScopedCssEnabled>

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

NuGet パッケージまたは Razor クラス ライブラリ (RCL) のコンポーネントの分離スタイルは自動的にバンドルされます。

  • アプリでは CSS インポートを使用し、RCL のバンドルされたスタイルを参照します。 ClassLib という名前のクラス ライブラリと、BlazorSample.styles.css スタイルシートのある Blazor アプリについては、アプリのスタイルシートの一番上に RCL のスタイルシートがインポートされます。

    @import '_content/ClassLib/ClassLib.bundle.scp.css';
    
  • RCL のバンドルされたスタイルは、スタイルを使用するアプリの静的な Web アセットとして公開されません。

RCL の詳細については、次の記事を参照してください。

その他のリソース