From site.css to component styles
October 7, 2020 · 4 minute read · Tags: blazor | css | javascript
Developers are human beings…
If there’s one thing we know about human beings it’s that they’re hardwired to take the shortest route from A to B…
Which probably explains why you have a single stylesheet in your app where ALL the CSS styles go.
One stylesheet to rule them all
You’re working on your app and realise you need to tweak the CSS…
… so you go to the one place where all the styles live.
Once there you realise you need a new style but where should you put it?
I mean… you’re already in this file which has lots of other CSS styles, and creating a new one would mean naming it, and linking to it from your main HTML page.
That all sounds like work and it’s nearly lunchtime… maybe, just this once, you could add one teeny-tiny little style to the bottom of this file?
Go round this hamster wheel a few times; site.css
gets bigger, new styles are chucked on the end and tweaking your styles takes longer, and longer (as does figuring out why anything looks the way it does).
.NET 5 offers a solution - CSS Isolation
Enter .NET 5 and CSS Isolation.
With CSS Isolation you can define styles for your components right there, next to the component.
All you need to do is:
- Create a new
<component>.razor.css
file - Define your component’s styles in that file
- Get on with the rest of your day (or you know, go for lunch)
Say you want to tweak the styles for the Counter
component which ships with the Blazor project templates.
With .NET 5 you can create a new Counter.razor.css
file.
In there, define your styles as normal at which point Blazor will automatically wire everything up so Counter
uses these styles.
Counter.razor.css
h1 {
font-size: 1.6em;
color: red;
font-weight: bold;
font-style: italic;
}
The big win is that these styles only apply to the specific component. So you can happily set styles for generic elements like h1
, confident that it will only affect this component’s h1
elements and not the other h1
elements in your application.
Index.razor
<h1>Regular H1 (outside of Counter component)</h1>
<Counter />
Go ::deep
By default these styles will only apply to the specific component, not child components.
For example:
Counter.razor
<div>
<CustomHeading>Counter</CustomHeading>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>
If the Counter
component includes another component (CustomHeading
in this example), and that component has an h1
tag:
<h1 class="heading">@ChildContent</h1>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
}
The style we defined in Counter.razor.css
won’t apply to the h1
in CustomHeading
.
However, you can make it apply using the ::deep
combinator.
Counter.razor.css
::deep h1 {
font-size: 1.6em;
color: red;
font-weight: bold;
font-style: italic;
}
Now any child components with h1
elements will take on this style as well.
How it works
When you use this feature in .NET 5 Blazor automatically gives your elements unique names/identifiers and rewrites your CSS rules to use these identifiers (at build time).
It also compiles all the various CSS styles into a file called {project_name}.styles.css
.
So your HTML will end up looking something like this (when you inspect the source):
<div b-dmd9kyn336="">
<h1 class="heading">Counter</h1>
<!-- other markup -->
</div>
And the corresponding file can be seen in {project_name}.styles.css
.
/* /Pages/Counter.razor.rz.scp.css */
[b-dmd9kyn336] h1 {
font-size: 1.6em;
color: red;
font-weight: bold;
font-style: italic;
}
If we omit the ::deep
combinator from our example we get slightly different CSS in {project_name}.styles.css
.
/* /Pages/Counter.razor.rz.scp.css */
h1[b-dmd9kyn336] {
font-size: 1.6em;
color: red;
font-weight: bold;
font-style: italic;
}
This shows the clear difference between using or not using ::deep
.
In summary
Once you start thinking in terms of components it follows you’ll want fine-grained control of each component’s appearance using CSS.
CSS Isolation in .NET 5 makes this much easier and keeps the CSS close to the markup it affects (avoiding the problem of everything getting thrown into one big stylesheet, making maintenance much more difficult).
All posts in the
To .NET 5 and beyond series.
- Update the HTML head from your Blazor components
- From site.css to component styles
- Prerendering your Blazor WASM application with .NET 5 (part 1)
- Prerendering your Blazor WASM application with .NET 5 (part 2 - solving the missing HttpClient problem)
- Render Blazor WASM components in your existing MVC/Razor Pages applications
- Does .NET 6 fix Blazor Prerendering?