138 lines
4.6 KiB
Markdown
138 lines
4.6 KiB
Markdown
---
|
|
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?
|