---
name: blazor-theme-patterns
description: Best practices for implementing Light/Dark themes in Blazor (MudBlazor) applications, ensuring synchronization between C# and CSS.
---

# Blazor Theme Patterns

This skill documents the patterns and best practices for implementing robust Light/Dark theming in Blazor applications, specifically when using **MudBlazor** alongside **Custom CSS**.

## 1. The Hybrid Theming Problem

Blazor apps often have two sources of truth for styling:
1.  **MudBlazor Theme (`MudTheme` in C#)**: Controls MudBlazor components (AppBar, Drawer, Buttons).
2.  **CSS Variables (`app.css`)**: Controls native HTML elements (`body`, `h1`, custom divs) and global layout.

**Risk**: If these two are not synchronized, you get "partial themes" (e.g., Dark Header but Light Body).

## 2. Solution: synchronization Pattern

Always synchronize the Blazor theme state with the DOM via JavaScript Interop.

### Step 1: Define CSS Variables with Data Attribute
In your `app.css`, define variables using the `[data-theme="dark"]` selector.

```css
:root {
    /* Light Mode Defaults */
    --bg-primary: #ffffff;
    --text-primary: #18181b;
}

[data-theme="dark"], :root[data-theme="dark"] {
    /* Dark Mode Overrides */
    --bg-primary: #09090b;
    --text-primary: #fafafa;
}

/* Enforce Body Background */
body {
    background-color: var(--bg-primary) !important;
    color: var(--text-primary) !important;
    transition: background-color 0.3s ease;
}
```

### Step 2: Add JS Helper
In `index.html` (before `MudBlazor.min.js`), add a lightweight helper to switch attributes.

```html
<script>
    window.setTheme = (theme) => {
        document.documentElement.setAttribute('data-theme', theme);
    }
</script>
```

### Step 3: call from Blazor Layout
In `MainLayout.razor`, inject `IJSRuntime` and call the helper when toggling the theme.

```razor
@inject IJSRuntime JSRuntime

<MudThemeProvider @bind-IsDarkMode="_isDarkMode" Theme="_theme" />

@code {
    private bool _isDarkMode;
    private MudTheme _theme = new();

    private async Task ToggleDarkMode()
    {
        _isDarkMode = !_isDarkMode;
        // Sync with CSS
        await JSRuntime.InvokeVoidAsync("setTheme", _isDarkMode ? "dark" : "light");
    }
}
```

## 3. Common Pitfalls & Solutions

### A. "Invisible" Text in Dark Mode (White on White)
**Issue**: MudBlazor's `Primary` color might be White in Dark Mode (e.g., `#fafafa`). If the `PrimaryContrastText` is not set, it might default to White, making text invisible on a Primary Filled Button.
**Fix**: Explicitly set `PrimaryContrastText` in your `PaletteDark`.

```csharp
PaletteDark = new PaletteDark()
{
    Primary = "#fafafa",
    PrimaryContrastText = "#18181b", // Force Black text on White Primary button
    // ...
}
```

**CSS Hardening**:
If C# palette fails, force it locally in CSS:
```css
[data-theme="dark"] .mud-button-filled-primary {
    color: #18181b !important;
}
```

### B. AppBar Color Mismatch
**Issue**: `MudAppBar` defaults to `Color.Primary` (often black/dark). In Light Mode, if you want a white AppBar, `Color.Primary` might conflict.
**Fix**: Use `Color="Color.Default"` and let the Theme Palette handle the background color (`AppbarBackground`).

```razor
<MudAppBar Color="Color.Default" Elevation="0" ... />
```

### C. Development Workflow (Hot Reload)
**Recommendation**: Use `dotnet watch` during development to instantly see CSS and Razor changes without restarting.

```bash
dotnet watch --project src/WebClientBase.Client
```

**Note**:
*   **CSS Changes**: Applied instantly.
*   **Razor Markup**: Applied instantly.
*   **C# Logic (`@code`)**: usually applied instantly, but complex logic changes (like modifying `OnInitialized`) may require a manual restart (Ctrl+R).
*   **Full Rebuild**: Required if you add constant fields or change method signatures that Hot Reload cannot handle.

## 4. Verification Checklist

When implementing themes, verify these points using the Browser Tool across multiple devices:

### A. Core Theme Checks
1.  **Body Background**: Does it change hex codes when toggled?
2.  **Primary Button Text**: Is it readable (Contrast Ratio) in *both* Light and Dark modes?
3.  **Headings**: Do standard `h1`-`h6` tags invert color correctly?

### B. Responsive Checks (Crucial)
1.  **Desktop (> 960px)**: Check standard navigation bar and layout.
2.  **Tablet (768px - 960px)**: Check if layout adapts (e.g. grid changes).
3.  **Mobile (< 768px)**:
    -   **Hamburger Menu**: Is the icon visible in both themes?
    -   **Drawer/Sidebar**: Does the drawer background match the theme (especially in Dark Mode)?
    -   **Mobile Navigation**: are links readable?
