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

Dark/Light mode does not update neutral and foreground colors for Blazor projects using JavaScript #4231

Closed
apsthisdev opened this issue Jan 7, 2021 · 5 comments
Assignees
Labels
bug A bug closed:not-actionable There is no action to be taken in response to this issue. community:request Issues specifically reported by a member of the community.

Comments

@apsthisdev
Copy link

apsthisdev commented Jan 7, 2021

Describe the bug; what happened?

See sample code in Github: https://github.com/ameyasubhedar/FastBlazorTheme

I have a Razor component library that bundles the styling and JavaScript for multiple Blazor WebAssembly and Blazor Server projects. I am trying to achieve same look and feel for all of the Blazor projects, so the razor component libraries contains the css and JS to initialize the fast design system provider and update baseLayerLuminance to achieve dark (0.23), light (1) and black (0) modes .

// Initialize the fast color palette with dark theme by default.
// JsInterop.InitDesignSystemProviderAsync()
export function initDesignSystemProvider() {

    //  Get the fluent-design-system-designSystemProvider to configure
    const designSystemProvider = document.querySelector("fluent-design-system-provider");

    //  Default parameters
    designSystemProvider.density = 0;
    designSystemProvider.designUnit = 4;
    designSystemProvider.baseHeightMultiplier = 10;
    designSystemProvider.baseHorizontalSpacingMultiplier = 3;
    designSystemProvider.cornerRadius = 3;
    designSystemProvider.outlineWidth = 1;
    designSystemProvider.focusOutlineWidth = 2;
    designSystemProvider.disabledOpacity = 0.3;

    // Tells the fluent-design-system-provider to create the layer CSS custom property
    designSystemProvider.registerCSSCustomProperty(neutralLayerCardContainerBehavior);

    // Tells the fluent-design-system-provider to use the above as the CSS background for the region
    designSystemProvider.style.setProperty("background-color", `var(--${neutralLayerCardContainerBehavior.name})`);
}

// Set the layer luminance and update the fast color palette.
// JsInterop.SetColorLayerLuminanceAsync(layerLuminance)
export function setColorLayerLuminance(layerLuminance) {
    var designSystemProvider = document.querySelector("fluent-design-system-provider");

    // Update the baseLayerLuminance to reflect the selected style.
    designSystemProvider.baseLayerLuminance = parseFloat(layerLuminance).toFixed(2);

    // Update accent color
    updateAccentColor(designSystemProvider, layerLuminance);

    // Update the background color based on evaluated color recipe.
    updateBackgroundColor(designSystemProvider);

    // Update neutral color
    updateNeutralColor(designSystemProvider);
}

// Toggle between the dark, light and black modes.
//  JsInterop.ToggleColorLayerLuminanceAsync();
export function toggleColorLayerLuminance() {
    var layerLuminance = getLayerLuminance();
    if (layerLuminance === "1") {

        // Light to Dark
        setColorLayerLuminance("0.23");
    }
    else if (layerLuminance === "0.23") {

        // Dark to Black
        setColorLayerLuminance("0");
    }
    else {

        // Black to Light
        setColorLayerLuminance("1");
    }
}

// get the layer luminance
export function getLayerLuminance() {
    const designProvider = document.querySelector("fluent-design-system-provider");
    return designProvider.baseLayerLuminance.toString();
}


// Update Accent color on design provider
function updateAccentColor(designSystemProvider) {
    var accentBaseColor = "#F33378";
    designSystemProvider.accentBaseColor = accentBaseColor;
    const accentPalette = createColorPalette(parseColorString(accentBaseColor));
    designSystemProvider.accentPalette = accentPalette;
}

// Update neutral color on design provider
function updateNeutralColor(designSystemProvider, layerLuminance) {
    var neutralColor = "#808080";
    const neutralPalette = createColorPalette(parseColorString(neutralColor));
    designSystemProvider.neutralPalette = neutralPalette;
}

// Update background color on design provider
function updateBackgroundColor(designSystemProvider) {
    designSystemProvider.backgroundColor = (neutralLayerCardContainerBehavior.value)(designSystemProvider.designSystem);
}

Razor component expose these as JSInterop scoped service, pretty standard for asp.net core balzor project.

In my App.razor pages I initialize the design system

protected override async Task OnAfterRenderAsync(bool firstRender)
 {
     await base.OnAfterRenderAsync(firstRender);

     await JsInterop.InitDesignSystemProviderAsync();
     await JsInterop.SetColorLayerLuminanceAsync(Web.Shared.Styling.LayerLuminance.Light);
 }

Everything works nicely

image

I change the code manually with dark layer JsInterop.SetColorLayerLuminanceAsync(Web.Shared.Styling.LayerLuminance.Dark); below and restart the blazor project and that also works nicely.

 protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        await JsInterop.InitDesignSystemProviderAsync();
        await JsInterop.SetColorLayerLuminanceAsync(Web.Shared.Styling.LayerLuminance.Dark);
    }

image

But now if I change the dark theme at runtime and use toggleColorLayerLuminance JavaScript function defined above in the razor event handler the code updates the luminance layer and background color correctly but neutral and accent colors are not updated.

    private async Task DarkModeClickEventHandlerAsync()
    {
        await JsInterop.ToggleColorLayerLuminanceAsync();
    }

image

See the text foreground color, this is the issue. Same issues happens with fluent-card component, its background color is not updated.

What am I doing wrong in the JavaScript code ? Any help is appreciated.

@apsthisdev apsthisdev added the status:triage New Issue - needs triage label Jan 7, 2021
@nicholasrice nicholasrice added bug A bug status:needs-investigation Needs additional investigation labels Jan 8, 2021
@nicholasrice
Copy link
Contributor

Thanks @ameyasubhedar. I'm not 100% sure what the cause of this is but it appears there is an issue with the base layer luminance system. If you're blocked in the short term you can set the background color directly (instead of layer luminance) as show here: https://stackblitz.com/edit/js-zmgrjb

@apsthisdev
Copy link
Author

@nicholasrice, thanks. I referenced the FAST website code to build the code for Blazor and JavaScript. The fast website has a custom component fast-frame that uses baseLayerLuminance to update the background. How does it work there ? Can you also check this source and let me know if I am missing something.

https://github.com/microsoft/fast/tree/906d9c2f42172c7e0c9041e0d6fb07609c83cfbd/sites/fast-website/src/app/components/fast-frame

image

@EisenbergEffect EisenbergEffect added community:request Issues specifically reported by a member of the community. and removed status:triage New Issue - needs triage labels Jan 11, 2021
@nicholasrice
Copy link
Contributor

Okay I tracked this down.

For historical reasons, the color recipes themselves implement design-system memoization where return values are memoized by object reference. We're tracking an issue to change that here: #3833.

What is happening in your example is the product of neutralLayerCardContainer is getting memoized on the first invocation, and keeps returning the same value invocation, even when baseLayerLuminance changes because the object reference provided to neutralLayerCardContainerBehavior.value() never changes:

function updateBackgroundColor(designSystemProvider) {
    designSystemProvider.backgroundColor = (neutralLayerCardContainerBehavior.value)(designSystemProvider.designSystem);
}

The DesignSystemProvider helps get around this though. You can ask it to evaluate a neutralLayerCardContainerBehavior with it's own design system context with the DesignSystemProvider.evaluate() method:

designSystemProvider.backgroundColor = designSystemProvider.evaluate(neutralLayerCardContainerBehavior).

Internally, the DesignSystemProvider circumvents this memoization and will force re-evaluation of the color recipe.

I have a working example pulled from your code here: https://stackblitz.com/edit/js-zmgrjb - give that a shot and let me know if that solves the issue for you.

@apsthisdev
Copy link
Author

@nicholasrice Thanks for the fix. Yes it does work now. One quick question is there any order dependency for the designSystemProvider force evaluate. For example I am first setting the layer luminance, then the accent color template followed with neutral color pallet and finally update the background color as show below. Hope this order is Ok ?

But overall its working now, thanks for a quick fix.

// Initialize the fast color palette.
export function setColorLayerLuminance(layerLuminance) {
    var designSystemProvider = document.querySelector("fluent-design-system-provider");

    // Update the baseLayerLuminance to reflect the selected style.
    designSystemProvider.baseLayerLuminance = parseFloat(layerLuminance).toFixed(2);

    // Update accent color
    updateAccentColor(designSystemProvider, layerLuminance);

    // Update neutral color
    updateNeutralColor(designSystemProvider);

    // Update the background color based on evaluated color recipe.
    updateBackgroundColor(designSystemProvider);
}

@nicholasrice
Copy link
Contributor

Great! Sorry for the confusion, that is an area we're hoping to address soon.

The order doesn't matter, it will all get reconciled by the provider :)

@nicholasrice nicholasrice added the closed:not-actionable There is no action to be taken in response to this issue. label Jan 21, 2021
@EisenbergEffect EisenbergEffect removed the status:needs-investigation Needs additional investigation label Jan 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A bug closed:not-actionable There is no action to be taken in response to this issue. community:request Issues specifically reported by a member of the community.
Projects
None yet
Development

No branches or pull requests

3 participants