I have a page, foo.razor, that represents a Form:
<EditForm Model=SomeModel>
<InputText @bind-Value=SomeModel.Property1 />
<InputText @bind-Value=SomeModel.Property2 />
<p>a paragraph</p>
</EditForm>
And next to it I have an isolated CSS, foo.razor.css, for example:
input{
display: block;
}
p{
color: lime;
}
The style for the paragraph applies correctly but the style for the input element does not. I can't understand why.
Plus, I know that blazor generates random ids for the elements after compilation. In the browser I can see that the paragraph has such an id but again the input and form elements do not.
I tried adding a class name for the component and use the deep selector, like this:
::deep .classname {
.......
}
But this does not work either.
The tricky thing about CSS isolation is that it works with plain HTML nodes only, it does not work for components. Under the covers, the elements from your current component that defines isolated CSS get a custom (kind of random) attribute, and CSS rules cascade through that to get scoped to your current component. The framework does not know what is inside other components, through, so it cannot add that attribute to them - their rendering is their own. So, these scoped CSS rules cannot target components.
How to work around that - wrap your components in an HTML element from your current component and write your rules to target elements inside that container too by replacing that element with ::deep.
Here's an example:
input,
::deep input{
display: block;
border: 2px solid red;
}
p,
::deep p {
color: lime;
border: 2px solid red;
}
And here's how to modify the form
<EditForm Model=SomeModel>
<div> @* This div right here is the "magic" *@
<InputText @bind-Value=SomeModel.Property1 />
<InputText @bind-Value=SomeModel.Property2 />
<p>a paragraph</p>
</div>
</EditForm>
@code{
TestModel SomeModel { get; set; } = new TestModel();
public class TestModel
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
}
The answer by @rdmptn is correct, but I'll add my two cents anyway. I struggled for a long time to understand and get ::deep working and I'll just share my journey.
Imagine we'd like to override the color of a <p> tag in a child component:
MyComponent.razor.css
::deep p { color: red !important; }
MyComponent.razor:
<span>My component!</span>
<OtherComponent/>
OtherComponent.razor:
<p>Other component!</p>
Now, the generated HTML for MyComponent.razor looks something like:
<span random1="">My component!</span>
<p random2="">Other component!</p>
Every .razor component has a random, unique component-id that gets assigned to all its plain HTML tags, i.e. <div>, <p>, <span>, <img>, etc. Note how the <span> element from MyComponent gets tagged with random1. And similarly, the <p> element gets tagged with random2 because that element was defined inside OtherComponent.
But the generated CSS from MyComponent.razor.css looks something like:
[random1] p { color: red !important; }
That is, the ::deep CSS selector becomes something like "all elements of type <p> under elements with the tag random1". And from the generated HTML above it's clear this will not match the <p> from OtherComponent. This is why wrapping the content in a <div> works:
MyComponent.razor (updated)
<span>My Component!</span>
<div>
<OtherComponent/>
</div>
And the new generated HTML:
<span random1="">My Component!</span>
<div random1="">
<p random2="">Other component!</p>
</div>
And now the ::deep CSS selector will match the <p> tag in OtherComponent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With