Blazor CSS Isolation
Overview
CSS Isolation is a Blazor feature that allows CSS styles to be associated specifically with a component, thereby avoiding style conflicts with other components or libraries. This approach improves the maintainability and modularity of Blazor applications.
How to enable CSS Isolation
To define styles specific to a component, simply create a .razor.css
file with the same name as the component’s .razor
file, in the same folder.
For example, for a component named MyComponent
in a file named MyComponent.razor
, create a file named MyComponent.razor.css
next to it:
MyComponent.razor :
<div class="container">
<h1>My Title</h1>
<p class="description">Description of my component</p>
</div>
MyComponent.razor.css :
.container {
background-color: #f0f0f0;
padding: 20px;
border-radius: 8px;
}
h1 {
color: #333;
font-size: 2rem;
margin-bottom: 1rem;
}
.description {
color: #666;
font-style: italic;
}
How it works internally?
During compilation, Blazor automatically rewrites CSS selectors to match the markup rendered by the component. The framework generates a unique scope identifier in the format b-{STRING}
(where {STRING}
is a 10-character string generated automatically) and adds it as an HTML attribute to the component’s elements.
generated HTML :
<div class="container" b-3xxtam6d07>
<h1 b-3xxtam6d07>Mon Titre</h1>
<p class="description" b-3xxtam6d07>...</p>
</div>
generated CSS :
.container[b-3xxtam6d07] {
background-color: #f0f0f0;
padding: 20px;
border-radius: 8px;
}
h1[b-3xxtam6d07] {
color: #333;
font-size: 2rem;
margin-bottom: 1rem;
}
.description[b-3xxtam6d07] {
color: #666;
font-style: italic;
}
Advantages of CSS Isolation
✅ Style encapsulation
- Styles are automatically limited to the component, avoiding conflicts with other parts of the application
- No risk of styles “leaking” to other components
✅ Improved maintainability
- Clear organization: each component has its own styles
- Facilitates code refactoring and maintenance
- Reduced dependencies on global styles
✅ Optimized performance
- Automatic bundling of styles at compile time
- Generation of a single CSS file for the entire application
- No overhead at runtime
✅ Ease of use
- No special configuration required
- Standard CSS syntax
- Compatible with CSS preprocessors (Sass, Less, etc.)
Disadvantages and limitations
❌ Complexity with child components
- Styles do not automatically apply to child components
- Requires the use of the
::deep
pseudo-element to target descendants
❌ Limitation on Razor components
- ⚠️ The generated identifier is not applied to the Blazor components you use, only to HTML elements
❌ Restricted HTML structure
- Sometimes necessary to add additional HTML elements to use
::deep
correctly - May impact HTML semantics
❌ More complex debugging
- Automatically generated class names can complicate debugging
- Requires an understanding of the identifier generation mechanism
Styling child components with ::deep
One of the major challenges of CSS Isolation is styling child components. By default, isolated styles only apply to the parent component.
The problem
Parent.razor :
@page "/parent"
<h1>Parent Component</h1>
<MyChildComponent />
Parent.razor.css :
/* This style will NOT apply to the child component. */
h1 {
color: blue;
}
MyChildComponent.razor :
<h1>MyChildComponent</h1>
The solution with ::deep
To apply styles to child components, use the pseudo-element ::deep
:
Parent.razor.css :
/* This style will apply to the h1 of the parent AND the children. */
::deep h1 {
color: blue;
}
Crucial point: Need for an HTML container element
Problem: The generated identifier is not applied to the Razor components themselves, but only to the HTML elements they contain. To use ::deep
correctly, it is often necessary to add a container HTML element.
Incorrect example (will not work):
<!-- Parent.razor -->
<h1>Parent</h1>
<MyChildComponent />
Correct example (will work):
<!-- Parent.razor -->
<div>
<h1>Parent</h1>
<MyChildComponent />
</div>
Parent.razor.css :
/* Now ::deep can work correctly */
::deep h1 {
color: red;
}
The container div
receives the scope identifier, allowing ::deep
to work on descendant elements.
Practical examples
Card component with CSS isolation
Card.razor :
<div class="card">
<div class="card-header">
<h3>@Title</h3>
</div>
<div class="card-content">
@ChildContent
</div>
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public RenderFragment? ChildContent { get; set; }
}
Card.razor.css :
.card {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 1rem;
}
.card-header {
background: #f8f9fa;
padding: 1rem;
border-bottom: 1px solid #ddd;
border-radius: 8px 8px 0 0;
}
.card-header h3 {
margin: 0;
color: #333;
}
.card-content {
padding: 1rem;
}
Page using the Card component
MyPage.razor :
@page "/my-page"
<div class="page-container">
<Card Title="First card">
<p>Content of the first card</p>
</Card>
<Card Title="Second card">
<p>Content of the second card</p>
</Card>
</div>
MyPage.razor.css :
.page-container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
/* Styling cards from the parent */
::deep .card {
transition: transform 0.2s ease;
}
::deep .card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
Advanced configuration
Customize the scope identifier
You can customize the scope identifier format in the project file:
<ItemGroup>
<None Update="Components/MyComponent.razor.css" CssScope="my-custom-id" />
</ItemGroup>
Disable CSS Isolation
To completely disable CSS Isolation in a project:
<PropertyGroup>
<ScopedCssEnabled>false</ScopedCssEnabled>
</PropertyGroup>
Best practices
- Organize your styles: Keep
.razor.css
files next to their respective components - Use explicit class names: Even if styles are isolated, clear names improve readability
- Minimize the use of ::deep: Prefer component composition over modifying child styles
- Add HTML containers: When you need to use
::deep
, make sure you have an appropriate HTML container element - Test compatibility: Check that your styles work correctly with different browsers
Conclusion
Blazor CSS Isolation is a powerful tool for keeping styles organized and avoiding conflicts. Although it has some limitations, particularly with child components and the need to sometimes add container HTML elements, its advantages in terms of maintainability and encapsulation make it a wise choice for most Blazor applications.
Understanding how it works internally, particularly the fact that the generated ID only applies to HTML elements and not to Razor components themselves, is essential for using this feature effectively.