TLDR; If you are using scoped CSS with RCL, and want to style some 3rd-party components' RenderFragment, then wrap the <3rdPartyComponent> under a <div> and use ::deep pseudo in your scoped CSS, before the selectors.
If you are:
- ...using .Net9 (or 8, or 7, etc.)
- ...having a Razor Class Library (RCL) within your project to implement reusable components (because why not?!)
- ...also having a scoped CSS to be used with your component
Then, we are in the same boat! The thing is, the rules in the scoped CSS file didn't work for me, when I tried to apply it to the components that based on another 3rd-party library I imported into mine. It's scratching head time!
For the context
I was having a Blazor (SSR) projects where the structure is like:
ProjectSolution
|-- UILibraryProject
| |-- Components (folder/namespace)
| |-- Component1.razor
| |-- Component1.razor.css <-- This is it
|-- MainAppProject
|-- MainAppComponent
And given that the MainAppComponent was using the <Component1>.
Happy case
If your Component1 is completly written from scratch with the standard HTML tags, then, it would work out of the box for you. There's nothing left to discuss, just enjoy Blazor and sip some coffee :).
According to the documentation, the scoped CSS in the RCL would be imported to the app's stylesheet. We'd just simply need to have a <link> to href to the app's stylesheet and we are good to go.
For instance, in the App.razor file of the MainAppProject
<link href="@Assets["MainAppProject.styles.css"]" rel="stylesheet"/>
Scratching head case
First, here was my Component1.razor
@using Microsoft.AspNetCore.Components.Forms
<MudDialog>
<DialogContent>
<EditForm Model="_model" OnValidSubmit="Confirm">
<DataAnnotationsValidator/>
<MudFocusTrap DefaultFocus="DefaultFocus.FirstChild">
<BitOtpInput Length="6" @bind-Value="_model.OtpValue" Class="otp-input" AutoFocus="true"/>
<MudContainer Class="pt-2 px-4">
<ValidationMessage For="() => _model.OtpValue"/>
</MudContainer>
<MudContainer Class="flex flex-row-reverse px-0 pt-4 pb-4">
<MudButton Disabled="@(!IsOtpComplete)" Color="@ConfirmColor" OnClick="Confirm"
ButtonType="ButtonType.Submit">@ConfirmText</MudButton>
<MudButton OnClick="Cancel">@CancelText</MudButton>
</MudContainer>
</MudFocusTrap>
</EditForm>
</DialogContent>
</MudDialog>
There are 2 things to keep in mind here:
- I was using third party libraries like MudBlazor, Bit.BlazorUI, etc. For the reason why -> why not?
- I wanted to apply custom CSS in the scoped file to the children of the
<BitOtpInput>. And more precisely, to its<input>elements.
And...this Component1.razor.css did not work:
.otp-input {
gap: 1rem;
margin-inline: 1rem;
}
.otp-input input {
border-radius: 0;
border-width: 0 0 1px 0;
border-color: lightseagreen;
background: none;
font-size: large;
height: 48px;
}
The problem: When I checked the HTML built by Blazor, one thing I could see, obviously, the css scoped id of the RenderFragment of all of the components there, were different from the one applied to my CSS file! :(
Then, I encountered this issue. Thanks GOD!
The workaround: I hope that one day, this would be supported as the issue stated. But for now, what helped me stop scratching was this workaround.
So, basically, I had to wrap the <EditForm> under another <div>, like:
<div>
<EditForm>
...
</EditForm>
</div>
And in the scoped CSS file, I added the pseudo ::deep before my selectors (as stated in the document). And voila!