feat(mobile-ux): implement theme toggle, client-side persistence, and light mode style overrides (#71)
Resolves #72 ## Description This PR implements the theme toggle mechanism, client-side persistence, and comprehensive light mode style overrides for the mobile reader layout in **NexusReader**. ### Key Changes 1. **ThemeService & State Management**: - Enhanced `ThemeService` with `InitializeAsync` and `ToggleTheme` using JS Interop. - Synchronized theme state changes via `OnThemeChanged` event. 2. **Local Storage Persistence & FOUC Prevention**: - Added client-side script in `App.razor` and MAUI `index.html` to instantly apply `.theme-light` from `localStorage` before prerendering/rendering to prevent a Flash of Unthemed Content (FOUC). - Created `theme.js` containing JS helper methods (`themeInterop.isLightMode`, `themeInterop.setLightMode`) to interface with `localStorage` and `document.documentElement` class list. 3. **UI Theme Toggle**: - Added a minimalist glassmorphic theme toggle button in the header of the `ReaderCanvas.razor` with dynamic transitions and icon morphing between sun and moon SVG icons. 4. **Light Mode Stylesheets Overrides**: - Added detailed light mode scoped styles (`.theme-light`) for the `ReaderCanvas` and the bottom-sheet `GlobalIntelligence` AI chat panel, utilizing earthy gray text (`#2d2a26`) on warm paper-like backgrounds (`rgba(244, 241, 234, 0.95)` / `#faf8f5`) to maintain high-quality aesthetic consistency. ### Verification - Solution built successfully without errors (`dotnet build NexusReader.slnx --no-restore`). - Scoped CSS isolation checked and validated for both Web and MAUI contexts. --------- Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Reviewed-on: #71 Co-authored-by: Antigravity <antigravity@google.com> Co-committed-by: Antigravity <antigravity@google.com>
This commit was merged in pull request #71.
This commit is contained in:
@@ -4,5 +4,6 @@ public interface IThemeService
|
||||
{
|
||||
bool IsLightMode { get; }
|
||||
event Func<Task>? OnThemeChanged;
|
||||
Task InitializeAsync();
|
||||
Task ToggleTheme();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace NexusReader.UI.Shared.Services;
|
||||
|
||||
public sealed class ThemeService : IThemeService
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
public bool IsLightMode { get; private set; } = false;
|
||||
public event Func<Task>? OnThemeChanged;
|
||||
|
||||
public ThemeService(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLightMode = await _jsRuntime.InvokeAsync<bool>("themeInterop.isLightMode");
|
||||
if (OnThemeChanged != null) await OnThemeChanged();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fail silently during prerendering or if JS is not available yet
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ToggleTheme()
|
||||
{
|
||||
IsLightMode = !IsLightMode;
|
||||
try
|
||||
{
|
||||
await _jsRuntime.InvokeVoidAsync("themeInterop.setLightMode", IsLightMode);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fail silently
|
||||
}
|
||||
if (OnThemeChanged != null) await OnThemeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user