Theme Switcher

Theme Switcher

An all-in-one popover panel that lets users pick a base color scheme and a primary accent color at runtime. Enable ShowStyles to expose style variant, radius, font, and menu customisation via a second tab. Changes are persisted across sessions via localStorage.

Demo

Click the circle button to open the theme panel. Colors tab is always shown. Enable ShowStyles to reveal the Styles & Layout tab.

Default (colors only)
ShowStyles=true (tabbed)

DarkModeToggle

A companion component bundled in the same namespace. It uses the same ThemeService and is already embedded inside ThemeSwitcher's popover header, but can also be placed standalone anywhere in your layout.

Dark mode: off

@using NeoUI.Blazor

<DarkModeToggle />

Usage

Minimal usage — drop it anywhere in your layout.

@using NeoUI.Blazor

<ThemeSwitcher />

Typical placement is in a top-bar or sidebar footer alongside DarkModeToggle:

<header class="flex items-center gap-2">
    <ThemeSwitcher />
    <DarkModeToggle />
</header>

Customisation

Use TriggerClass and PopoverContentClass to tailor the trigger button and panel without subclassing.

Fixed Positioning

By default Strategy is Fixed so the popover correctly escapes any CSS stacking context (e.g. sidebars with overflow: hidden or transform). Switch to Absolute if you embed the switcher in regular document flow where fixed positioning causes layout shifts.

@using NeoUI.Blazor   

<ThemeSwitcher Strategy="PositioningStrategy.Absolute" />

API Reference

ThemeSwitcher component parameters and their types.

Prop Type Default Description
ShowStyles bool false When true, adds a "Styles & Layout" tab exposing style variant, radius, font, and menu options alongside the default Colors tab.
TriggerClass string? null Additional CSS classes merged onto the trigger Button.
PopoverContentClass string? null Additional CSS classes merged onto the PopoverContent panel.
Align PopoverAlign End Popover alignment relative to trigger.
Strategy PositioningStrategy Fixed Use Fixed inside transformed/overflow-hidden containers; Absolute otherwise.
ZIndex int ZIndexLevels.PopoverContent Z-index for the popover panel.

ThemeService

Inject ThemeService directly if you need to read or change the theme programmatically without rendering the switcher UI.

Prop Type Default Description
CurrentBaseColor BaseColor Currently active base color scheme.
CurrentPrimaryColor PrimaryColor Currently active primary accent color.
CurrentStyleVariant StyleVariant Currently active visual style variant.
CurrentRadiusPreset RadiusPreset Currently active border radius preset.
CurrentFontPreset FontPreset Currently active font preset.
IsDarkMode bool Whether dark mode is currently active.
OnThemeChanged event Action Fired whenever the theme changes.
InitializeAsync() Task Reads persisted preferences from localStorage.
SetBaseColorAsync(BaseColor) Task Changes and persists the base color scheme.
SetPrimaryColorAsync(PrimaryColor) Task Changes and persists the primary accent color.
SetStyleVariantAsync(StyleVariant) Task Changes and persists the visual style variant.
SetRadiusPresetAsync(RadiusPreset) Task Changes and persists the border radius preset.
SetFontPresetAsync(FontPreset) Task Changes and persists the font preset.
SetThemeAsync(bool) Task Toggles dark mode and persists the preference.
ApplyPresetAsync(ThemePreset) Task Applies a named preset across all theme dimensions.

App.razor Setup

Add the following to the of App.razor (Blazor Web) or index.html (Blazor WASM):

1 — Core stylesheet

The main NeoUI stylesheet provides all component styles and CSS variables.

<link href="_content/NeoUI.Blazor/components.css" rel="stylesheet" />

2 — Base color stylesheets

Each base color scheme is a separate CSS file. In production include only the ones you actually offer to users — if your app only uses Zinc, ship only zinc.css.

<!-- Base color themes (include only those you need) -->
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/zinc.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/slate.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/gray.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/neutral.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/stone.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/mist.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/mauve.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/taupe.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/base/olive.css" />

3 — Primary color stylesheets

Same rule applies — only include the accent colors your app exposes. Omitting unused files reduces the CSS bundle size.

<!-- Primary color themes (include only those you need) -->
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/red.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/rose.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/orange.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/amber.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/yellow.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/lime.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/green.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/emerald.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/teal.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/cyan.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/sky.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/blue.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/indigo.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/violet.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/purple.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/fuchsia.css" />
<link rel="stylesheet" href="_content/NeoUI.Blazor/css/themes/primary/pink.css" />

4 — Theme JavaScript

The theme script must load before Blazor initializes to avoid a flash of unstyled content (FOUC). Place both tags at the end of <head>, after the stylesheets.

<!-- Theme JS — must come before Blazor boots to prevent FOUC -->
<script src="_content/NeoUI.Blazor/js/theme.js"></script>

5 — Service registration

ThemeService is registered automatically when you call AddNeoUIComponents() in Program.cs.

// Program.cs
builder.Services.AddNeoUIPrimitives();
builder.Services.AddNeoUIComponents(); // registers ThemeService

Reconnecting...

Attempting to rejoin the server

Connection Lost

Retrying in seconds

Connection Failed

Failed to rejoin the server.
Please retry or reload the page.

Session Paused

The session has been paused by the server

Resume Failed

Failed to resume the session.
Please reload the page.