Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: DesignTokens and RenderModeServer in .NET 8 #706

Closed
SPWizard01 opened this issue Sep 7, 2023 · 9 comments
Closed

docs: DesignTokens and RenderModeServer in .NET 8 #706

SPWizard01 opened this issue Sep 7, 2023 · 9 comments
Labels
area:blazor A blazor-specific issue status:blocked Any issue blocked by another

Comments

@SPWizard01
Copy link

SPWizard01 commented Sep 7, 2023

🙋 Documentation Request

Hi,

In the documentation you specifically point that you recommend to use code approach to specify the design tokens to implement custom theming.

https://www.fluentui-blazor.net/DesignTokens#using-the-fluentdesignsystemprovider

The usual place where you would do this is in MainLayout(i.e. change default font, primary color etc), however the layout cannot be set to RenderModeServer.
So unless you would specifically use WebAssembly, you cannot get OnAfterRenderAsync to execute, and therefore you cannot get Ref to an element where you would apply the theming.

I was not able to find a way to specify BodyFont and i.e. FillColor at the same time inside a MainLayout without using WebAssembly or FluentDesignSystemProvider.

Maybe I am missing something?

💁 Possible Solution

The only solution that i found was to create an SSR wrapper and use that wrapper in every page as top element, something like that:

SSRThemeProvider.cs:

 public class SSRThemeProvider
 {
     private readonly BodyFont bodyFont;
     private readonly FillColor fillColor;


     public SSRThemeProvider(BodyFont bodyFontInj, FillColor fillColorInj)
     {
         bodyFont = bodyFontInj;
         fillColor = fillColorInj;
     }

     public async Task SetupTheming(ElementReference reference)
     {
         await bodyFont.SetValueFor(reference, "Fantasy");
         await fillColor.SetValueFor(reference, "#DEADBF".ToSwatch());
     }

 }

CustomThemeWrapper.razor:

@using Microsoft.Fast.Components.FluentUI;
<div @ref="themeHolder">
    <FluentLayout>
        <FluentHeader>
            My App
            <FluentSpacer />
            <CascadingAuthenticationState>
                <AuthorizeView>
                    Hello, @context.User.Identity?.Name!
                </AuthorizeView>
            </CascadingAuthenticationState>
        </FluentHeader>
        <FluentStack Orientation="Orientation.Horizontal">
            <NavMenu />
            <FluentBodyContent>
                @ChildContent
            </FluentBodyContent>
        </FluentStack>
    </FluentLayout>
</div>
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private ElementReference themeHolder;
    [Inject]
    public SSRThemeProvider ThemeProvider { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await ThemeProvider.SetupTheming(themeHolder);
            StateHasChanged();
        }
    }
}

and then inside a page:

@using Microsoft.Fast.Components.FluentUI;
@page "/ValidationManagement"
@attribute [RenderModeServer]

<CustomThemeWrapper>
    <FluentStack Orientation="Orientation.Vertical">
        ....
    </FluentStack>
</CustomThemeWrapper>

the problem with this approach is that the page would flicker 3 times (initial prerender, FluenUI load, theming setup), and the fact you have to wrap your component every time :)

Is there any better way to do the theming using RenderModeServer?

@SPWizard01 SPWizard01 changed the title DesignTokens and RenderModeServer in .NET 8 docs: DesignTokens and RenderModeServer in .NET 8 Sep 7, 2023
@vnbaaij
Copy link
Collaborator

vnbaaij commented Sep 7, 2023

I have not found a better/different/other way yet. Was running into the same problem with the @ref being outside of the scope of the point where you can influence it.
Waiting for .NET 8 RC1/RC2 to see what will change and adopt to that at a later point.

@vnbaaij vnbaaij added the area:blazor A blazor-specific issue label Sep 7, 2023
@vnbaaij
Copy link
Collaborator

vnbaaij commented Sep 8, 2023

This is the issue that tracks this on the aspnetcore repo: dotnet/aspnetcore#50433

@vnbaaij vnbaaij added the status:blocked Any issue blocked by another label Sep 11, 2023
@SPWizard01
Copy link
Author

Now that RC2 is released I can see that they would apply SSR on the Router, hence making Layout also SSR, however the issue still persists that it would flicker (now 2 times instead of 3 :) ).
Is there any possibility to make the Theming setup during Program.cs load? i.e. https://www.fluentui-blazor.net/CodeSetup

builder.Services.AddFluentUIComponents(options =>
{
    //DesignTokens setup delegate? or FluentUI Theme class?
     options.DefaultTheme = ....
});

Just thinking out loud. but that way this could also work without SSR.

Don't know whether its better or worse, but for me it would make sense, and then for each individual component you could setup additional theming within component?
Something like mergeStyles or mergeThemes in React FluentUI v8?

It kind of begs it would work similar to these:
https://developer.microsoft.com/en-us/fluentui#/controls/web/themeprovider
https://github.com/microsoft/fluentui/blob/master/packages/merge-styles/README.md

@vnbaaij
Copy link
Collaborator

vnbaaij commented Oct 30, 2023

We have a solution in the demo site now that prevents flicker in most cases. We added a theme.js file to the Shared project and load that in index.html.
Also in index.html we add a bit of inline style:

<style>
        :root {
            --neutral-fill-layer-rest: #ffffff;
            color: #000000;
        }

        .loading-progress-text {
            color: #000000;
        }

        @media(prefers-color-scheme: dark) {
            :root {
                --neutral-fill-layer-rest: #464646;
                color: #ffffff;
            }

            .loading-progress-text {
                color: #ffffff;
            }
        }

        body,
        #container {
            font: 14px "Segoe UI Variable", "Segoe UI", sans-serif;
            background-color: var(--neutral-fill-layer-rest);
        }
</style>

So now when the site starts, through CSS it sets the background and font color based on your browser settings.

Then, through theme.js we use those same browser settings to set the design tokens before Blazor kicks in and can do something with that.

Only flicker we can't prevent is if you have set browser preference to dark and then set the site to use light them (and vice versa). You won' see that on first load because it always uses system setting, but you will see itonce you have made a change through te demo settings panel and reload the site (then the cookie is used to read preference instead of using browser value.

@SPWizard01
Copy link
Author

SPWizard01 commented Oct 30, 2023

Yeah, I can see that, however does it not defeat the purpose of using this library in the first place?

I mean, don't get me wrong, its nice, I love it. But the fact that using a .NET Library I would rather write a .NET code with minimal JS :) the theme.js does contain quite bit of code, and you have to know what to write, to be able write it.
Furthermore, this implies that i need to write a custom JS/CSS for every customized component instead of defining it within code (the way you would normally do it today in JS libs)

Design Tokens are nice, but the next step would be having ability to define the "Default Theme" during app startup (so it would then generate CSS/JS internally?) instead of having to add those bits of code yourself.

I don't think I am the only one with this problem here, also this kinda implies that Demo site cannot achieve custom design functionality of the framework in question without using separate JS/CSS files :)

Just my two cents :)

@vnbaaij
Copy link
Collaborator

vnbaaij commented Oct 30, 2023

This is one of those things where you need a JS solution, I'm afraid. With the Design Token implementation we have in the library, we need JS interop and that is only available after the page has been rendered. To prevent the flickering, that is going to be too late.
You are totally right about the amount of code in the theme.js file. It does a bit more than just setting the token to a default value. We are thinking about providing something like it out of the box so that you would just need to add a script tag to the file (with a /_content/...path)

Design Tokens are nice, but the next step would be having ability to define the "Default Theme" during app startup (so it would then generate CSS/JS internally?) instead of having to add those bits of code yourself.

We are trying to do something like that: #808

@SPWizard01
Copy link
Author

SPWizard01 commented Oct 30, 2023

We are trying to do something like that: #808

This is something I will be expecting, looks promising.

As for the very late part, why not SectionOutlet for header?

i.e.
image
image
image

essentially something you required to be added inside the header anyways (the css) but just wrapped as component that executes some generative code based on services provided in app startup?

Again, maybe I am completely off here :)

@vnbaaij
Copy link
Collaborator

vnbaaij commented Oct 30, 2023

No, you might be on to something there. Interesting concept!

@vnbaaij
Copy link
Collaborator

vnbaaij commented Nov 1, 2023

Closing this as there is no immediate action to be taken. We are looking at it in the mentioned PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:blazor A blazor-specific issue status:blocked Any issue blocked by another
Projects
None yet
Development

No branches or pull requests

2 participants