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.

Themed Label

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.

Theme Label (Navy) Explicit Red Label

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):

Outer Label

Inner theme (teal buttons, dark-green labels):

Inner Label

6. Without ThemeProvider

Components outside any ThemeProvider receive no theme and render with their default appearance.

No Theme Label

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

  1. Convert each .skin file entry to a ThemeConfiguration.AddSkin() call
  2. Wrap your app (or page section) in a <ThemeProvider>
  3. Remove asp: prefix and runat="server" from markup
  4. Keep SkinID attributes — they work identically
  5. 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 }
        });
    }
}