Skins & Themes PoC
This sample demonstrates the Skins & Themes system, which emulates
ASP.NET Web Forms' .skin file behavior using Blazor's cascading values.
A ThemeProvider wraps components and delivers a ThemeConfiguration
via CascadingValue. Each control automatically picks up its matching skin.
1. Default Skins
All controls inside the ThemeProvider receive the default skin for their
control type — no extra attributes needed.
2. Named Skins (SkinID)
Set SkinID on a control to select a named skin instead of the default.
This maps directly to the Web Forms SkinID attribute.
3. Explicit Values Override Theme
The theme uses StyleSheetTheme semantics: properties you set explicitly in markup always win. The theme only fills in defaults for properties left unset.
4. EnableTheming Opt-Out
Set EnableTheming="false" on any control to skip theme application entirely.
The control renders with only its explicit properties.
5. Nested ThemeProviders
An inner ThemeProvider overrides the outer theme for its subtree.
This lets you apply different themes to different sections of a page.
Outer theme (blue buttons, navy labels):
Inner theme (teal buttons, dark-green labels):
6. Without ThemeProvider
Components outside any ThemeProvider receive no theme and render
with their default appearance.
Migration Guide — Before & After
The Skins & Themes system maps directly from ASP.NET Web Forms concepts.
Before (Web Forms)
App_Themes/Corporate/controls.skin:
<asp:Button runat="server"
BackColor="#336699" ForeColor="White"
Font-Names="Segoe UI" Font-Size="9pt"
BorderStyle="None" />
<asp:Button runat="server" SkinID="danger"
BackColor="#CC3333" ForeColor="White"
Font-Bold="True" />
web.config:
<pages theme="Corporate" />
Products.aspx:
<asp:Button ID="btnSave" runat="server" Text="Save" />
<asp:Button ID="btnDelete" runat="server" Text="Delete" SkinID="danger" />
After (Blazor)
// CorporateTheme.cs
var theme = new ThemeConfiguration();
theme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.FromHtml("#336699"),
ForeColor = WebColor.FromName("White"),
Font = new FontInfo { Name = "Segoe UI", Size = new FontUnit("9pt") }
});
theme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.FromHtml("#CC3333"),
ForeColor = WebColor.FromName("White"),
Font = new FontInfo { Bold = true }
}, skinId: "danger");
@* App.razor or layout *@
<ThemeProvider Theme="@corporateTheme">
<Router ... />
</ThemeProvider>
@* Products.razor — markup barely changes *@
<Button Text="Save" />
<Button Text="Delete" SkinID="danger" />
Migration Steps
- Convert each
.skinfile entry to aThemeConfiguration.AddSkin()call - Wrap your app (or page section) in a
<ThemeProvider> - Remove
asp:prefix andrunat="server"from markup - Keep
SkinIDattributes — they work identically - Use
EnableTheming="false"for controls that should ignore the theme
Source Code
@page "/ControlSamples/Theming"
@using BlazorWebFormsComponents.Theming
<ThemeProvider Theme="@SampleTheme">
<!-- Default skins applied automatically -->
<Button Text="Themed Button" />
<Label Text="Themed Label" />
<TextBox Text="Themed TextBox" />
<!-- Named skins via SkinID -->
<Button Text="Danger" SkinID="Danger" />
<Button Text="Success" SkinID="Success" />
<!-- Explicit values override theme -->
<Button Text="Explicit Green" BackColor="WebColor.Green" ForeColor="WebColor.White" />
<!-- Opt out of theming entirely -->
<Button Text="Opted Out" EnableTheming="false" />
</ThemeProvider>
<!-- Nested ThemeProvider overrides outer theme -->
<ThemeProvider Theme="@SampleTheme">
<ThemeProvider Theme="@AlternateTheme">
<Button Text="Inner Theme" />
</ThemeProvider>
</ThemeProvider>
@code {
private ThemeConfiguration SampleTheme;
private ThemeConfiguration AlternateTheme;
protected override void OnInitialized()
{
SampleTheme = new ThemeConfiguration();
// Default Button skin
SampleTheme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.Blue,
ForeColor = WebColor.White
});
// Named skins
SampleTheme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.Red,
ForeColor = WebColor.White
}, "Danger");
SampleTheme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.FromHtml("#006633"),
ForeColor = WebColor.White
}, "Success");
// Label and TextBox skins
SampleTheme.AddSkin("Label", new ControlSkin
{
ForeColor = WebColor.Navy,
Font = new FontInfo { Bold = true }
});
SampleTheme.AddSkin("TextBox", new ControlSkin
{
BorderColor = WebColor.Blue,
BorderStyle = BorderStyle.Solid
});
// Alternate theme for nesting demo
AlternateTheme = new ThemeConfiguration();
AlternateTheme.AddSkin("Button", new ControlSkin
{
BackColor = WebColor.Teal,
ForeColor = WebColor.White
});
AlternateTheme.AddSkin("Label", new ControlSkin
{
ForeColor = WebColor.FromHtml("#006633"),
Font = new FontInfo { Italic = true }
});
}
}