In this lesson, you will learn how to implement user-selectable themes by leveraging CSS variables coupled with Blazor’s state management.
The Strategy: CSS Variable Injection
At an advanced level, you don't want to swap entire stylesheets, as this triggers expensive browser re-renders and potential FOUC (Flash of Unstyled Content). Instead, we map our design tokens to a set of global CSS variables defined in the :root pseudo-class. By dynamically updating the style attribute on a top-level container (like the body or a wrapper div), we trigger an immediate, high-performance visual update across the entire application.
Implementation Pattern
First, define your design tokens in your global CSS file:
:root {
--bg-color: #ffffff;
--text-color: #000000;
}
[data-theme='dark'] {
--bg-color: #1a1a1a;
--text-color: #f0f0f0;
}
Next, create a ThemeService that manages the state. You can expose an Action or EventCallback that components subscribe to, or simply use a scoped state container that the MainLayout observes:
public class ThemeService {
public string CurrentTheme { get; private set; } = "light";
public event Action OnThemeChanged;
public void SetTheme(string theme) {
CurrentTheme = theme;
OnThemeChanged?.Invoke();
}
}
In your MainLayout.razor, wrap your content in a container and bind the state:
<div data-theme="@ThemeService.CurrentTheme" class="app-container">
@Body
</div>
@code {
protected override void OnInitialized() => ThemeService.OnThemeChanged += StateHasChanged;
}
Why this approach?
By keeping the logic in data-theme attributes rather than inline styles, you maintain a clean separation of concerns. The CSS engine handles the heavy lifting of recalculating the styles once the attribute changes, while Blazor remains lightweight by only managing the application state. This avoids the overhead of complex state serialization and ensures that your styling remains responsive, predictable, and decoupled from your component logic.
Key Takeaway: Dynamic theming is most efficient when you use C# state to toggle CSS data-attributes, allowing the browser to recalculate global variable values natively.