fix: FluentUI Razor web components do not work with Blazor CSS isolation
See original GitHub issue🐛 Bug Report
Because the terminology in the docs is a bit hard to keep track of, I’ll use the following terms:
- FAST Razor components: Refers to the Razor components exported in this repo wrapping the Fluent web components.
- FAST web components: Refers to using the Fluent web components directly.
When using the FAST Razor components you cannot style them like you would expect with Blazor CSS isolation.
This seems to be related to https://github.com/dotnet/razor-tooling/issues/7606
TL;DR: I expect the following FAST web component usage and FAST Razor component usage to render the same but the Razor component renders without the margin:
Example.razor.css:
.awesome-margin { margin: 16px; }
Example.razor:
<!-- ✔ Using the FAST web component works and we see the margin, but I'd like to use the typing given by FAST Razor components -->
<fluent-button class="awesome-margin">FAST Web component</fluent-button>
<!-- ❌ Swapping and using the FAST Razor component does not work as the margin is missing 😢 -->
<FluentButton class="awesome-margin">FAST Razor component</FluentButton>
💻 Repro or Code Sample
Using the following Counter.razor.css file:
.button-container { display:flex; flex-direction: column; border: 1px solid red; width: 500px;}
.my-web-component-button-scoped-css { margin: 16px; }
.my-razor-button-scoped-css { margin: 16px; }
::deep .my-razor-button-deep { margin: 16px; }
For the following Counter.razor file:
<div class="button-container">
<!-- ✔ Using the FAST web component with CSS isolation works, but I'd like to use the typing given by FAST Razor components -->
<fluent-button class="my-web-component-button-scoped-css" @onclick="IncrementCount">Click me - web component scoped css</fluent-button>
<!-- ❌ Swapping and using the FAST Razor component with CSS isolation does not work 😢 -->
<FluentButton class="my-razor-button-scoped-css" @onclick="IncrementCount">Click me - scoped css</FluentButton>
<!-- The following are different workarounds -->
<!-- ⚠ Using the FAST Razor component by moving the css into a style tag "works" but is cheating. Those do not participate in CSS isolation and are effectively global styles -->
<style>
.my-razor-button-local-css {
margin: 16px;
}
</style>
<FluentButton class="my-razor-button-local-css" @onclick="IncrementCount">Click me - local css</FluentButton>
<!-- ⚠ Using a FAST Razor component with inline styles works but does not let us use CSS files as expected -->
<FluentButton style="margin: 16px;" @onclick="IncrementCount">Click me - inline css</FluentButton>
<!-- ✔⚠ Using a FAST Razor component with deep style selector has the fewest tradeoffs -->
<FluentButton class="my-razor-button-deep" @onclick="IncrementCount">Click me - deep css</FluentButton>
</div>
Gives the following output:
Notice how the FAST Razor component with the label “Click me - scoped css” does not receive the margin that was set in the corresponding CSS file.
Example repo: https://github.com/rajsite/fast-blazor-issue-template/tree/fast-blazor-scoped-css-margin
🤔 Expected Behavior
I expect to be able to target the FAST Razor components with classes from CSS files like I would the corresponding Fast web component. In the screenshot above I would expect all the buttons to have the same margin.
😯 Current Behavior
See screenshot above.
💁 Possible Solution
Wait for https://github.com/dotnet/razor-tooling/issues/7606 to be resolved?
Some workarounds:
- Use the Blazor-specific
::deep
selector as shown in the last example above. The Blazor docs explicitly describe this as Child component support. This allows you to place component styles in a stylesheet participating in Blazor CSS isolation. Probably the best workaround so far. - Modify CSS that does not participate in Blazor CSS isolation:
- Add
<style>
tags in the Razor HTML - Add styles to
wwwroot/css
- Use inline
style=""
attributes
- Add
🔦 Context
Trying to switch from using the FAST web components to using the FAST Razor components.
🌍 Your Environment
$ dotnet --version
5.0.400
<script type="module" src="https://unpkg.com/@fluentui/web-components@1.6.3/dist/web-components.min.js"></script>
<PackageReference Include="Microsoft.Fast.Components.FluentUI" Version="0.4.0" />
Issue Analytics
- State:
- Created 2 years ago
- Comments:13 (5 by maintainers)
Top GitHub Comments
@rajsite Thanks for putting this together. We’re talking with Blazor engineers and will work towards a solution. This is obviously less than ideal.
@atmgrifter00 That does look like a cleaner way to use the
::deep
workaround (ie not requiring a wrapping element) and is described in the Blazor docs as Child component support. Good find!I updated my top-level issue description and my example branch to use that workaround.
@EisenbergEffect @javiercn it still very much feels like a workaround but it is probably the workaround worth documenting. 👍 (as a community member using FAST, I’m not at Microsoft / representative of the Blazor team)
Hopefully there is some pattern that blazor / asp.net can adopt to make this use-case more first class. There is issue https://github.com/dotnet/razor-tooling/issues/7606, however another idea could be inspired from Angular.
In Angular the corresponding concept is having components vs directives. A component is a class with a template associated with it and a directive is a class without a template associated with it but instead with a selector associated with it. For our Angular fast web component wrappers we have defined Angular directives that match to the web components and can do Angulary behaviors (Angular Form participation, defining property mappings, etc).
Notice in the following example that the directive has a selector that it matches but does not instantiate a template:
I could imagine a similar concept like that in Blazor / ASP.net that could apply for web components. @javiercn Does something like that already exist to support the built-in elements, ie div, span, etc that could be extended for web components (particularly as https://github.com/dotnet/razor-tooling/issues/7606 was de-prioritized and doesn’t seem to be making much progress)?