GithubHelp home page GithubHelp logo

Comments (13)

EisenbergEffect avatar EisenbergEffect commented on May 17, 2024 2

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

from fluentui-blazor.

rajsite avatar rajsite commented on May 17, 2024 1

@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 dotnet/razor#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 dotnet/razor#7606 was de-prioritized and doesn't seem to be making much progress)?

from fluentui-blazor.

javiercn avatar javiercn commented on May 17, 2024

The solution here will likely involve dotnet/razor#7606 since CSS isolation works at the file/component level. We can't allow you to pierce into the scope of a component unless that component explicitly allows it, which we don't really have a mechanism for.

One way to workaround this issue is to use the ::deep selector within the consuming component and make the style scoped to the callsite, for example, if your component is wrapped within a div do something like div::deep fluent-textfield on your scoped css file.

from fluentui-blazor.

rajsite avatar rajsite commented on May 17, 2024
Original workaround using wrapped ::deep hidden, See the top level issue description for the most recent workaround not requiring a wrapper > The solution here will likely involve [dotnet/razor#7606](https://github.com/dotnet/razor/issues/7606) since CSS isolation works at the file/component level. We can't allow you to pierce into the scope of a component unless that component explicitly allows it, which we don't really have a mechanism for. > > One way to workaround this issue is to use the `::deep` selector within the consuming component and make the style scoped to the callsite, for example, if your component is wrapped within a `div` do something like `div::deep fluent-textfield` on your scoped css file.

So we use Blazor's ::deep combinator which is a Blazor-specific feature that gets compiled out. The fundamental bit of the workaround is that we won't rely on the class set on the FAST Razor component and instead rely on an immediate wrapper and the ::deep selector to target the FAST web component child of the FAST Razor component.

So instead of the following (which does not work as expected):

Counter.razor.css:

.button-container { border: 1px solid red; }
.my-razor-button-scoped-css { margin: 16px; }

Counter.razor:

<div class="button-container">
    <FluentButton class="my-razor-button-scoped-css" @onclick="IncrementCount">Click me - scoped css</FluentButton>
</div>

The following workaround using ::deep and a wrapper element would accomplish a similar behavior:

Counter.razor.css:

.button-container { border: 1px solid red; }
.my-razor-button-scoped-css { display: contents; } /* Note: Set the wrapper to display contents so the child handles sizing behavior. We know this will target the immediate parent of the generated fluent-button. */
.my-razor-button-scoped-css ::deep fluent-button { margin: 16px; } /* Note: This selector is targeting the underlying FAST web component generated by the FAST Razor component. */

Counter.razor:

<div class="button-container">
    <div class="my-razor-button-scoped-css"> <!-- Note: This wrapper div was added and is used to help build the ::deep selector targeting the generated fluent-button FAST web component. -->
        <FluentButton @onclick="IncrementCount">Click me - scoped css</FluentButton>
    </div>
</div>

This seems workable. I'm on the fence if this is much better than some of the other workarounds like global CSS since it's a lot of munging in the DOM hierarchy.

If we had to assume one of these workarounds would be used long-term (maybe until .NET 7 or later @javiercn ?) then it might be better to have the encapsulation this option provides despite the maintenance burden of putting wrappers over every FAST Razor component usage.

I'll put a link in the workarounds options above.

from fluentui-blazor.

atmgrifter00 avatar atmgrifter00 commented on May 17, 2024

I may be mistaken, but I believe we missed the one configuration that will actually solve this perceived issue, which is taking the original CSS and simply changing it to:

.button-container { border: 1px solid red; }
.my-web-component-button-scoped-css { margin: 16px; }
::deep .my-razor-button-scoped-css { margin: 16px; }

I've verified that this yields the expected behavior with no changes to the .razor file (thus, no wrappers are necessary). I think this behavior jives with how we should expect Blazor isolated CSS to work (since the class we're applying to the Blazor component is actually applied to an element inside what is essentially a child component), and I believe we can close this issue out. I think it would be good to let @rajsite comment further before doing so however.

from fluentui-blazor.

EisenbergEffect avatar EisenbergEffect commented on May 17, 2024

Unfortunately, ::deep was removed from the standards track several years ago and is only implemented in Chromium-based engines for backwards compat. It is deprecated.

from fluentui-blazor.

EisenbergEffect avatar EisenbergEffect commented on May 17, 2024

@rajsite Is a fix for this issue planned for the next Blazor release?

from fluentui-blazor.

javiercn avatar javiercn commented on May 17, 2024

@EisenbergEffect Blazor has a ::deep pseudo-selector in its implementation of scoped CSS, which is that this is talking about I think.

from fluentui-blazor.

atmgrifter00 avatar atmgrifter00 commented on May 17, 2024

@javiercn @EisenbergEffect, correct. As far as I know, the ::deep pseudo-selector is a necessary, well-supported construct for Blazor apps, otherwise the official docs are in dire need of updating.

from fluentui-blazor.

EisenbergEffect avatar EisenbergEffect commented on May 17, 2024

Oh. Interesting. That is confusing since W3C had a selector with the same name in early versions of web components, which was intentionally removed...

from fluentui-blazor.

atmgrifter00 avatar atmgrifter00 commented on May 17, 2024

Yes...it's all horribly confusing, particularly since we've now been conditioned to not use that pseudo-selector in Angular apps, which was once their recommendation for crossing component boundaries.

from fluentui-blazor.

EisenbergEffect avatar EisenbergEffect commented on May 17, 2024

Well, if this fixes the issues, that's great. I'd like to keep this open until we maybe add some documentation around this scenario. Anyone here interested in adding a small section to the readme?

from fluentui-blazor.

rajsite avatar rajsite commented on May 17, 2024

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 dotnet/razor-tooling#7606 was de-prioritized and doesn't seem to be making much progress)?

I guess that concept is tagHelpers which aren't supported in Blazor.

from fluentui-blazor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.