# Phase 5: Blazor Hybrid Host + Theme + Localization
**Status:** ⬜ Not Started
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** frontend
**Implementer:** Opus + frontend-design skill
## Objective
Create the WPF + BlazorWebView host that loads `Marathon.UI` (Razor Class Library),
establish the design system / theme using MudBlazor, set up bilingual (RU/EN)
localization end-to-end, and wire up DI to compose Application + Infrastructure layers.
## Tasks
- [ ] In `src/Marathon.Hosts.WpfBlazor/Marathon.Hosts.WpfBlazor.csproj`:
- Set `true`, `false`
- SDK: `Microsoft.NET.Sdk.Razor` (so Razor + WPF interop works)
- Add packages:
- `Microsoft.AspNetCore.Components.WebView.Wpf`
- `MudBlazor`
- `Microsoft.Extensions.Hosting`
- `Serilog.Extensions.Hosting`
- `Serilog.Sinks.File`
- `Serilog.Sinks.Console`
- [ ] In `src/Marathon.UI/Marathon.UI.csproj`:
- SDK: `Microsoft.NET.Sdk.Razor`
- `net8.0` with WebView for Razor Components
- Add `MudBlazor` (so components in this RCL can use MudBlazor)
- [ ] Create `Marathon.UI/_Imports.razor` with namespace and component imports
(Microsoft.AspNetCore.Components.*, MudBlazor, project namespaces).
- [ ] Create `Marathon.UI/wwwroot/index.html` (Blazor host HTML for the WebView).
- [ ] Create `Marathon.UI/MainLayout.razor` with MudBlazor `MudLayout` + `MudAppBar` +
`MudDrawer` navigation. Include locale switcher (RU/EN) in the AppBar.
- [ ] Create `Marathon.UI/Pages/Home.razor` placeholder dashboard.
- [ ] Create `Marathon.UI/Pages/Settings.razor` — bound to all `appsettings.json`
options (ScrapingOptions, WorkerOptions, StorageOptions, AnomalyOptions,
LocalizationOptions). Live save via `IOptionsMonitor` + writing back to
`appsettings.Local.json`.
- [ ] Establish theme tokens in `Marathon.UI/Theme/MarathonTheme.cs` — distinctive
palette per frontend-design guidance, NOT generic AI-default. Include:
- Primary, secondary, accent
- Surface tones for light + dark mode
- Typography stack (RU-friendly font for Cyrillic — e.g., Inter or Manrope which
have full Cyrillic coverage)
- Spacing scale, radius scale, shadow scale as CSS variables in a `app.css`
- [ ] Wire MudBlazor theme via `MudThemeProvider` in `MainLayout.razor`.
- [ ] Localization:
- Add `Microsoft.Extensions.Localization` to `Marathon.UI`
- Create `Marathon.UI/Resources/SharedResource.cs` (marker class for `IStringLocalizer`)
- Add `Marathon.UI/Resources/SharedResource.ru.resx` and `SharedResource.en.resx`
with all UI strings used in this phase + placeholders for later phases
- Configure supported cultures in host: `ru-RU`, `en-US`
- Locale switcher persists choice to `appsettings.Local.json` and reloads UI
- [ ] In `src/Marathon.Hosts.WpfBlazor/MainWindow.xaml`:
- Single `BlazorWebView` filling the window
- `HostPage="wwwroot/index.html"`
- `RootComponents` add ``
- [ ] In `src/Marathon.Hosts.WpfBlazor/App.xaml.cs`:
- Build `IHost` via `Host.CreateApplicationBuilder()`
- Call `services.AddMarathonInfrastructure(config)`
- Call `services.AddMarathonApplication(config)`
- Call `services.AddWpfBlazorWebView()`
- Add MudBlazor: `services.AddMudServices()`
- Configure Serilog (rolling file at `./logs/marathon-.log`, console)
- Start the host on `OnStartup`, stop on `OnExit`
- [ ] Add `appsettings.json` to `Marathon.Hosts.WpfBlazor/` (move from Phase 3 if
placed there) with all sections. Add `appsettings.Development.json` template.
- [ ] Tests in `Marathon.UI.Tests` (using bUnit):
- Test: `MainLayout` renders without errors
- Test: locale switcher changes culture
- Test: theme tokens are applied (CSS variables present in DOM)
## Files to Modify/Create
- `src/Marathon.UI/_Imports.razor`
- `src/Marathon.UI/MainLayout.razor`
- `src/Marathon.UI/Pages/Home.razor`, `Pages/Settings.razor`
- `src/Marathon.UI/Theme/MarathonTheme.cs`, `Theme/app.css`
- `src/Marathon.UI/wwwroot/index.html`
- `src/Marathon.UI/Resources/SharedResource.{cs,ru.resx,en.resx}`
- `src/Marathon.UI/Components/LocaleSwitcher.razor`
- `src/Marathon.Hosts.WpfBlazor/App.xaml`, `App.xaml.cs`
- `src/Marathon.Hosts.WpfBlazor/MainWindow.xaml`, `MainWindow.xaml.cs`
- `src/Marathon.Hosts.WpfBlazor/appsettings.json`, `appsettings.Development.json`
- `src/Marathon.Hosts.WpfBlazor/Properties/AssemblyInfo.cs`
- `tests/Marathon.UI.Tests/MainLayoutTests.cs`, `LocaleSwitcherTests.cs`
## Acceptance Criteria
- Host project compiles (Big Bang smoke check).
- `Marathon.UI` is a clean RCL — usable from any host (verifies portability).
- Theme is distinct, not generic — implementer should follow `frontend-design` skill
guidance for typography, color, motion, spatial composition.
- Locale switcher works (toggles between RU and EN strings on the same page).
- Settings page surfaces every configurable parameter from `appsettings.json`.
## Notes
- This phase is parallelizable with Phases 2 and 3 (only depends on Phase 1 Domain,
but the orchestrator can run all three after Phase 1 completes).
- The frontend-design skill content is provided to the agent in `FRONTEND_DESIGN_SKILL`
context block. Follow it precisely.
- Use Cyrillic-friendly fonts (Inter, Manrope, IBM Plex Sans, JetBrains Mono).
- For BlazorWebView in WPF, the project SDK MUST be `Microsoft.NET.Sdk.Razor` and
the OutputType set to `WinExe` with WPF enabled.
## Review Checklist
- [ ] Compiles
- [ ] `Marathon.UI` references no host-specific code (BlazorWebView, WPF)
- [ ] Theme not generic — distinctive palette + typography
- [ ] All `appsettings.json` keys reachable via the Settings page
- [ ] RU + EN both renderable (placeholder strings ok for later phases)
- [ ] Accessibility: keyboard navigation in nav drawer, focus indicators
## Handoff to Next Phase