Skip to content

Commit e49d9f7

Browse files
authored
Prevent FOUC in RadzenTheme (#2294)
* ThemeService.SetTheme uses JavaScript to change the theme CSS files to prevent FOUC during Blazor initial hydration. * Update the getting started instructions. * Initialize the theme in ThemeService from RadzenTheme.
1 parent 6202bd7 commit e49d9f7

File tree

8 files changed

+80
-32
lines changed

8 files changed

+80
-32
lines changed

Radzen.Blazor/RadzenTheme.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
{
55
<link rel="preload" href="@IconFontHref" as="font" type="font/woff2" crossorigin="anonymous" />
66
}
7-
<link rel="stylesheet" href="@Href" />
7+
<link id="radzen-theme-link" rel="stylesheet" href="@Href" />
88
@if (wcag)
99
{
10-
<link rel="stylesheet" href="@WcagHref" />
10+
<link id="radzen-wcag-theme-link" rel="stylesheet" href="@WcagHref" />
1111
}

Radzen.Blazor/RadzenTheme.razor.cs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,14 @@ public partial class RadzenTheme : IDisposable
3636

3737
private bool wcag;
3838

39-
private static readonly string? Version = typeof(RadzenTheme).Assembly.GetName().Version?.ToString();
39+
private string Href => ThemeService.Href;
4040

41-
private string Href => $"{Path}/{theme}-base.css?v={Version}";
41+
private string WcagHref => ThemeService.WcagHref;
4242

43-
private string WcagHref => $"{Path}/{theme}-wcag.css?v={Version}";
44-
45-
private string Path => Embedded ? $"_content/Radzen.Blazor/css" : "css";
46-
47-
private string IconFontPath => Embedded ? $"_content/Radzen.Blazor/fonts" : "fonts";
43+
private string IconFontPath => ThemeService.Embedded ? $"_content/Radzen.Blazor/fonts" : "fonts";
4844

4945
private string IconFontHref => $"{IconFontPath}/MaterialSymbolsOutlined.woff2";
5046

51-
private bool Embedded => theme switch
52-
{
53-
"material" => true,
54-
"material-dark" => true,
55-
"standard" => true,
56-
"standard-dark" => true,
57-
"humanistic" => true,
58-
"humanistic-dark" => true,
59-
"software" => true,
60-
"software-dark" => true,
61-
"default" => true,
62-
"dark" => true,
63-
_ => false
64-
};
65-
6647
private PersistingComponentStateSubscription? persistingSubscription;
6748

6849
/// <inheritdoc />

Radzen.Blazor/ThemeService.cs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using Microsoft.AspNetCore.Components;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.JSInterop;
14
using System;
25
using System.Collections;
36
using System.Collections.Generic;
@@ -366,12 +369,31 @@ public static class Themes
366369
/// <summary>
367370
/// Service for theme registration and management.
368371
/// </summary>
369-
public class ThemeService
372+
public class ThemeService(IJSRuntime jsRuntime, IServiceProvider serviceProvider)
370373
{
374+
375+
private string theme;
371376
/// <summary>
372377
/// Gets the current theme.
373378
/// </summary>
374-
public string Theme { get; private set; }
379+
public string Theme
380+
{
381+
get
382+
{
383+
if (theme == null)
384+
{
385+
var persistentComponentState = serviceProvider.GetService<PersistentComponentState>();
386+
387+
if (persistentComponentState?.TryTakeFromJson(nameof(Theme), out string persistedTheme) == true)
388+
{
389+
theme = persistedTheme;
390+
}
391+
}
392+
return theme;
393+
}
394+
395+
private set => theme = value;
396+
}
375397

376398
/// <summary>
377399
/// Specify if the theme colors should meet WCAG contrast requirements.
@@ -430,8 +452,38 @@ public void SetTheme(ThemeOptions options)
430452
if (requiresChange && options.TriggerChange)
431453
{
432454
ThemeChanged?.Invoke();
455+
456+
try
457+
{
458+
jsRuntime.InvokeVoid("Radzen.setTheme", Href, WcagHref);
459+
}
460+
catch (Exception)
461+
{
462+
}
433463
}
434464
}
465+
private static readonly string Version = typeof(ThemeService).Assembly.GetName().Version?.ToString();
466+
467+
internal string Href => $"{Path}/{Theme}-base.css?v={Version}";
468+
469+
internal string WcagHref => $"{Path}/{Theme}-wcag.css?v={Version}";
470+
471+
private string Path => Embedded ? $"_content/Radzen.Blazor/css" : "css";
472+
473+
internal bool Embedded => Theme switch
474+
{
475+
"material" => true,
476+
"material-dark" => true,
477+
"standard" => true,
478+
"standard-dark" => true,
479+
"humanistic" => true,
480+
"humanistic-dark" => true,
481+
"software" => true,
482+
"software-dark" => true,
483+
"default" => true,
484+
"dark" => true,
485+
_ => false
486+
};
435487

436488
/// <summary>
437489
/// Enables or disables WCAG contrast requirements.

Radzen.Blazor/wwwroot/Radzen.Blazor.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,5 +2592,16 @@ window.Radzen = {
25922592
unregisterScrollListener: function (element) {
25932593
document.removeEventListener('scroll', element.scrollHandler, true);
25942594
window.removeEventListener('resize', element.scrollHandler, true);
2595+
},
2596+
setTheme: function (href, wcagHref) {
2597+
const theme = document.getElementById('radzen-theme-link');
2598+
if (theme && theme.href != href) {
2599+
theme.href = href;
2600+
}
2601+
2602+
const wcagTheme = document.getElementById('radzen-wcag-theme-link');
2603+
if (wcagTheme && wcagTheme.href != wcagHref) {
2604+
wcagTheme.href = wcagHref;
2605+
}
25952606
}
25962607
};

RadzenBlazorDemos.Host/App.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<base href="/" />
2222
<link href="css/site.css" rel="stylesheet" />
2323
<HeadOutlet @rendermode="InteractiveWebAssembly" />
24-
<RadzenTheme Theme="material3" @rendermode="InteractiveWebAssembly" />
24+
<RadzenTheme Theme="material3" />
2525
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/vs/editor/editor.main.min.css">
2626
</head>
2727
<body class="rz-scrollbars">

RadzenBlazorDemos.Server/App.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<base href="/" />
2222
<link href="css/site.css" rel="stylesheet" />
2323
<HeadOutlet @rendermode="InteractiveServer" />
24-
<RadzenTheme Theme="material3" @rendermode="InteractiveServer" />
24+
<RadzenTheme Theme="material3" />
2525
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/vs/editor/editor.main.min.css">
2626
</head>
2727
<body class="rz-scrollbars">

RadzenBlazorDemos/Pages/GetStarted.razor

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,9 @@ These instructions assume the new application layout which uses rendering modes
3838
Open the <code>App.razor</code> file of your application. Add this code within the <code>&lt;head&gt;</code> element:
3939
</RadzenText>
4040
<pre class="rz-p-4">
41-
<code>&lt;RadzenTheme Theme="material" @@rendermode="InteractiveAuto" /&gt;
41+
<code>&lt;RadzenTheme Theme="material" /&gt;
4242
</code>
4343
</pre>
44-
<RadzenAlert AlertStyle="AlertStyle.Info" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false">
45-
Set the <code>@@rendermode</code> attribute to the render mode of your application e.g. <code>InteractiveServer</code>, <code>InteractiveWebAssembly</code> or <code>InteractiveAuto</code>.
46-
</RadzenAlert>
4744
@IncludeJavaScript(("App.razor", CurrentVersion))
4845
@UseComponent(CurrentVersion)
4946
<RadzenText Anchor="@($"get-started/{CurrentVersion}#additional")" TextStyle="TextStyle.H5" TagName="TagName.H2" class="rz-mt-12 rz-mb-4">6. Use Dialog, Notification, ContextMenu and Tooltip components</RadzenText>

RadzenBlazorDemos/Pages/ThemeServicePage.razor

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ The Radzen.Blazor library provides a built-in service that persists the current
3232

3333
[Inject]
3434
private ThemeService ThemeService { get; set; }
35+
36+
[Inject]
37+
private IOptions&lt;CookieThemeServiceOptions&gt; Options { get; set; }
3538

3639
protected override void OnInitialized()
3740
{
@@ -45,6 +48,10 @@ The Radzen.Blazor library provides a built-in service that persists the current
4548
{
4649
ThemeService.SetTheme(theme, false);
4750
}
51+
else
52+
{
53+
HttpContext.Response.Cookies.Append("MyApplicationTheme", "material" , new CookieOptions { Secure = Options.Value.IsSecure, Expires = DateTimeOffset.Now.Add(Options.Value.Duration) });
54+
}
4855
}
4956
}
5057
}

0 commit comments

Comments
 (0)