question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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:

image

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

🔦 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:closed
  • Created 2 years ago
  • Comments:13 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
EisenbergEffectcommented, Nov 9, 2021

@rajsite Thanks for putting this together. We’re talking with Blazor engineers and will work towards a solution. This is obviously less than ideal.

1reaction
rajsitecommented, Oct 28, 2022

@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:

/**
 * Directive to provide Angular integration for the button.
 */
@Directive({
    selector: 'nimble-button'
})
export class NimbleButtonDirective {
    public get appearance(): ButtonAppearance {
        return this.elementRef.nativeElement.appearance;
    }

    @Input() public set appearance(value: ButtonAppearance | ButtonAppearanceAttribute) {
        this.renderer.setProperty(this.elementRef.nativeElement, 'appearance', value);
    }

    public get disabled(): boolean {
        return this.elementRef.nativeElement.disabled;
    }

    @Input() public set disabled(value: BooleanValueOrAttribute) {
        this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', toBooleanProperty(value));
    }

    // contentHidden property intentionally maps to the content-hidden attribute
    // eslint-disable-next-line @angular-eslint/no-input-rename
    @Input('content-hidden') public set contentHidden(value: BooleanValueOrAttribute) {
        this.renderer.setProperty(this.elementRef.nativeElement, 'contentHidden', toBooleanProperty(value));
    }

   // ...

    public constructor(private readonly renderer: Renderer2, private readonly elementRef: ElementRef<Button>) {}
}

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)?

Read more comments on GitHub >

github_iconTop Results From Across the Web

FluentUI Razor web components do not work with Blazor ...
One way to workaround this issue is to use the ::deep selector within the consuming component and make the style scoped to the...
Read more >
Net 6 Blazor Server-Side CSS Isolation not working
NET 5 Blazor application and I had to add webBuilder.UseStaticWebAssets() on the Program.cs file, but on .Net 6 we don't have that file...
Read more >
ASP.NET Core Blazor CSS isolation
Learn how CSS isolation scopes CSS to Razor components, which can simplify CSS and avoid collisions with other components or libraries.
Read more >
BlazorFluentUI.CoreComponents 6.0.2 on NuGet
These components and interfaces must be removed as they no longer function within this library. Switch to using CSS isolation with your razor...
Read more >
How to Isolate CSS in Razor Components - YouTube
CSS Isolation is a simple but powerful concept that you will use in nearly every SPA application that you produce. In this video...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found