Skip to content

Commit da0f030

Browse files
authored
New article for Blazor CSS isolation (#19956)
1 parent 453b2e9 commit da0f030

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
title: ASP.NET Core Blazor CSS isolation
3+
author: daveabrock
4+
description: Learn how CSS isolation allows you to scope CSS to your components, which can simplify your CSS and avoid collisions with other components or libraries.
5+
monikerRange: '>= aspnetcore-5.0'
6+
ms.author: riande
7+
ms.custom: mvc
8+
ms.date: 10/20/2020
9+
no-loc: ["ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
10+
uid: blazor/components/css-isolation
11+
---
12+
# ASP.NET Core Blazor CSS isolation
13+
14+
By [Dave Brock](https://twitter.com/daveabrock)
15+
16+
CSS isolation simplifies an app's CSS footprint by preventing dependencies on global styles and helps to avoid styling conflicts among components and libraries.
17+
18+
## Enable CSS isolation
19+
20+
To define component-specific styles, create a `razor.css` file matching the name of the `.razor` file for the component. This `razor.css` file is a *scoped CSS file*.
21+
22+
For a `MyComponent` component that has a `MyComponent.razor` file, create a file alongside the component called `MyComponent.razor.css`. The `MyComponent` value in the `razor.css` filename is **not** case-sensitive.
23+
24+
For example to add CSS isolation to the `Counter` component in the default Blazor project template, add a new file named `Counter.razor.css` alongside the `Counter.razor` file, then add the following CSS:
25+
26+
```css
27+
h1 {
28+
color: brown;
29+
font-family: Tahoma, Geneva, Verdana, sans-serif;
30+
}
31+
```
32+
33+
The styles defined in `Counter.razor.css` are only applied to the rendered output of the `Counter` component. Any `h1` CSS declarations defined elsewhere in the app don't conflict with `Counter` styles.
34+
35+
> [!NOTE]
36+
> In order to guarantee style isolation when bundling occurs, `@import` Razor blocks aren't supported with scoped CSS files.
37+
38+
## CSS isolation bundling
39+
40+
CSS isolation occurs at build time. During this process, Blazor rewrites CSS selectors to match markup rendered by the component. These rewritten CSS styles are bundled and produced as a static asset at `{PROJECT NAME}.styles.css`, where the placeholder `{PROJECT NAME}` is the referenced package or product name.
41+
42+
These static files are referenced from the root path of the app by default. In the app, reference the bundled file by inspecting the reference inside the `<head>` tag of the generated HTML:
43+
44+
```html
45+
<link href="MyProjectName.styles.css" rel="stylesheet">
46+
```
47+
48+
Within the bundled file, each component is associated with a scope identifier. For each styled component, an HTML attribute is appended with the format `b-<10-character-string>`. The identifier is unique and different for each app. In the rendered `Counter` component, Blazor appends a scope identifier to the `h1` element:
49+
50+
```html
51+
<h1 b-3xxtam6d07>
52+
```
53+
54+
The `MyProjectName.styles.css` file uses the scope identifier to group a style declaration with its component. The following example provides the style for the preceding `<h1>` element:
55+
56+
```css
57+
/* /Pages/Counter.razor.rz.scp.css */
58+
h1[b-3xxtam6d07] {
59+
color: brown;
60+
}
61+
```
62+
63+
At build time, a project bundle is created with the convention `{STATIC WEB ASSETS BASE PATH}/MyProject.lib.scp.css`, where the placeholder `{STATIC WEB ASSETS BASE PATH}` is the static web assets base path.
64+
65+
If other projects are utilized, such as NuGet packages or [Razor class libraries](xref:blazor/components/class-libraries), the bundled file:
66+
67+
* References the styles using CSS imports.
68+
* Isn't published as a static web asset of the app that consumes the styles.
69+
70+
## Child component support
71+
72+
By default, CSS isolation only applies to the component you associate with the format `{COMPONENT NAME}.razor.css`, where the placeholder `{COMPONENT NAME}` is usually the component name. To apply changes to a child component, use the `::deep` combinator to any descendant elements in the parent component's `razor.css` file. The `::deep` combinator selects elements that are *descendants* of an element's generated scope identifier.
73+
74+
The following example shows a parent component called `Parent` with a child component called `Child`.
75+
76+
`Parent.razor`:
77+
78+
```razor
79+
@page "/parent"
80+
81+
<div>
82+
<h1>Parent component</h1>
83+
84+
<Child />
85+
</div>
86+
```
87+
88+
`Child.razor`:
89+
90+
```razor
91+
<h1>Child Component</h1>
92+
```
93+
94+
Update the `h1` declaration in `Parent.razor.css` with the `::deep` combinator to signify the `h1` style declaration must apply to the parent component and its children:
95+
96+
```css
97+
::deep h1 {
98+
color: red;
99+
}
100+
```
101+
102+
The `h1` style now applies to the `Parent` and `Child` components without the need to create a separate scoped CSS file for the child component.
103+
104+
> [!NOTE]
105+
> The `::deep` combinator only works with descendant elements. The following HTML structure applies the `h1` styles to components as expected:
106+
>
107+
> ```razor
108+
> <div>
109+
> <h1>Parent</h1>
110+
>
111+
> <Child />
112+
> </div>
113+
> ```
114+
>
115+
> In this scenario, ASP.NET Core applies the parent component's scope identifier to the `div` element, so the browser knows to inherit styles from the parent component.
116+
>
117+
> However, excluding the `div` element removes the descendant relationship, and the style is **not** applied to the child component:
118+
>
119+
> ```razor
120+
> <h1>Parent</h1>
121+
>
122+
> <Child />
123+
> ```
124+
125+
## CSS preprocessor support
126+
127+
CSS preprocessors are useful for improving CSS development by utilizing features such as variables, nesting, modules, mixins, and inheritance. While CSS isolation doesn't natively support CSS preprocessors such as Sass or Less, integrating CSS preprocessors is seamless as long as preprocessor compilation occurs before Blazor rewrites the CSS selectors during the build process. Using Visual Studio for example, configure existing preprocessor compilation as a **Before Build** task in the Visual Studio Task Runner Explorer.
128+
129+
Many third-party NuGet packages, such as [Delegate.SassBuilder](https://www.nuget.org/packages/Delegate.SassBuilder), can compile SASS/SCSS files at the beginning of the build process before CSS isolation occurs, and no additional additional configuration is required.
130+
131+
## CSS isolation configuration
132+
133+
CSS isolation is designed to work out-of-the-box but provides configuration for some advanced scenarios, such as when there are dependencies on existing tools or workflows.
134+
135+
### Customize scope identifier format
136+
137+
By default, scope identifiers use the format `b-<10-character-string>`. To customize the scope identifier format, update the project file to a desired pattern:
138+
139+
```xml
140+
<ItemGroup>
141+
<None Update="MyComponent.razor.css" CssScope="my-custom-scope-identifier" />
142+
</ItemGroup>
143+
```
144+
145+
In the preceding example, the CSS generated for `MyComponent.Razor.css` changes its scope identifier from `b-<10-character-string>` to `my-custom-scope-identifier`.
146+
147+
### Change base path for static web assets
148+
149+
The `scoped.styles.css` file is generated at the root of the app. In the project file, use the `StaticWebAssetBasePath` property to change the default path. The following example places the `scoped.styles.css` file, and the rest of the app's assets, at the `_content` path:
150+
151+
```xml
152+
<PropertyGroup>
153+
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
154+
</PropertyGroup>
155+
```
156+
157+
### Disable automatic bundling
158+
159+
To opt out of how Blazor publishes and loads scoped files at runtime, use the `DisableScopedCssBundling` property. When using this property, it means other tools or processes are responsible for taking the isolated CSS files from the `obj` directory and publishing and loading them at runtime:
160+
161+
```xml
162+
<PropertyGroup>
163+
<DisableScopedCssBundling>true</DisableScopedCssBundling>
164+
</PropertyGroup>
165+
```

aspnetcore/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@
385385
uid: blazor/components/virtualization
386386
- name: Templated components
387387
uid: blazor/components/templated-components
388+
- name: CSS isolation
389+
uid: blazor/components/css-isolation
388390
- name: Integrate components
389391
uid: blazor/components/integrate-components-into-razor-pages-and-mvc-apps
390392
- name: Component libraries

0 commit comments

Comments
 (0)