@inject IJSRuntime JS @* Dark-mode toggle. Pure JS-interop + localStorage — no DI service and no server state. The actual theme switch happens in the browser via window.sbTheme (wwwroot/js/theme.js); this button just drives toggle() and reflects the result. The page-load default (no-flash) is owned by the inline pre-hydration script in App.razor, not here. *@ @code { /// Mirrors the browser's current theme so the glyph + aria-pressed stay in sync. private bool _isDark; protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; // Reflect the persisted theme once the circuit is interactive. Prerender // has no JS runtime, so guard the call; a disconnect/blip just leaves the // button on its default (light) glyph until the next render. try { var mode = await JS.InvokeAsync("sbTheme.get"); var isDark = mode == "dark"; if (isDark != _isDark) { _isDark = isDark; StateHasChanged(); } } catch (JSException) { } catch (JSDisconnectedException) { } catch (InvalidOperationException) { } } private async Task ToggleAsync() { try { var mode = await JS.InvokeAsync("sbTheme.toggle"); _isDark = mode == "dark"; } catch (JSException) { } catch (JSDisconnectedException) { } catch (InvalidOperationException) { } } }