From 9b80bb09661d3f4da47e73a66588cfa5bde8af4e Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz csharpfritz" Date: Sat, 28 Mar 2026 11:45:33 -0400 Subject: [PATCH 1/5] feat(theming): Full Skins & Themes implementation (#369) Wave 1 - Core Theme Fidelity: - ThemeMode enum (StyleSheetTheme/Theme) with dual-mode ApplyThemeSkin - Sub-component style theming (SubStyles on ControlSkin, SkinBuilder.SubStyle()) - 5 data controls override ApplyThemeSkin: GridView, DetailsView, FormView, DataGrid, DataList - Container-level EnableTheming propagation via ancestor chain walk - Runtime theme switching via ThemeProvider Mode parameter - Fix generic type name lookup (GridView1 -> GridView) for theme skin matching Wave 2 - Migration Accelerators: - .skin file parser (SkinFileParser) - reads Web Forms .skin files into ThemeConfiguration - JSON theme format (JsonThemeLoader) - load/save themes as JSON with custom converters - CSS file bundling - ThemeProvider renders elements via HeadContent Wave 3 - Diagnostics: - ThemeDiagnostics with validation rules for unknown controls, sub-styles, empty skins - Runtime SkinID mismatch logging in BaseWebFormsComponent Tests: 120 theming tests (72 Wave 1 + 48 Wave 2), 2685 total tests passing Docs: themes-and-skins.md with migration guide, API reference, quick start Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/themes-and-skins.md | 469 ++++++++++++++++ mkdocs.yml | 1 + .../Theming/ContainerPropagationTests.razor | 240 +++++++++ .../Theming/CssBundlingTests.razor | 249 +++++++++ .../Theming/JsonThemeLoaderTests.cs | 505 ++++++++++++++++++ .../Theming/RuntimeThemeSwitchTests.razor | 297 ++++++++++ .../Theming/SkinFileParserTests.cs | 367 +++++++++++++ .../Theming/SubStyleTests.razor | 285 ++++++++++ .../Theming/ThemeModeTests.razor | 244 +++++++++ .../BaseStyledComponent.cs | 124 +++-- .../BaseWebFormsComponent.cs | 122 ++++- .../DataGrid.razor.cs | 31 ++ .../DataList.razor.cs | 25 + .../DetailsView.razor.cs | 40 ++ .../FormView.razor.cs | 31 ++ .../GridView.razor.cs | 34 ++ .../Theming/ControlSkin.cs | 7 + .../Theming/JsonThemeLoader.cs | 412 ++++++++++++++ .../Theming/SkinBuilder.cs | 13 + .../Theming/SkinFileParser.cs | 397 ++++++++++++++ .../Theming/ThemeConfiguration.cs | 48 ++ .../Theming/ThemeDiagnostics.cs | 262 +++++++++ .../Theming/ThemeMode.cs | 21 + .../Theming/ThemeProvider.razor | 30 ++ 24 files changed, 4215 insertions(+), 39 deletions(-) create mode 100644 docs/themes-and-skins.md create mode 100644 src/BlazorWebFormsComponents.Test/Theming/ContainerPropagationTests.razor create mode 100644 src/BlazorWebFormsComponents.Test/Theming/CssBundlingTests.razor create mode 100644 src/BlazorWebFormsComponents.Test/Theming/JsonThemeLoaderTests.cs create mode 100644 src/BlazorWebFormsComponents.Test/Theming/RuntimeThemeSwitchTests.razor create mode 100644 src/BlazorWebFormsComponents.Test/Theming/SkinFileParserTests.cs create mode 100644 src/BlazorWebFormsComponents.Test/Theming/SubStyleTests.razor create mode 100644 src/BlazorWebFormsComponents.Test/Theming/ThemeModeTests.razor create mode 100644 src/BlazorWebFormsComponents/Theming/JsonThemeLoader.cs create mode 100644 src/BlazorWebFormsComponents/Theming/SkinFileParser.cs create mode 100644 src/BlazorWebFormsComponents/Theming/ThemeDiagnostics.cs create mode 100644 src/BlazorWebFormsComponents/Theming/ThemeMode.cs diff --git a/docs/themes-and-skins.md b/docs/themes-and-skins.md new file mode 100644 index 000000000..aa5c3dc7d --- /dev/null +++ b/docs/themes-and-skins.md @@ -0,0 +1,469 @@ +# Themes and Skins + +The **Themes and Skins** feature enables you to centrally define and apply consistent styling to all BlazorWebFormsComponents throughout your application—just as you would in ASP.NET Web Forms. This guide explains how to use themes, apply them at runtime, and migrate from Web Forms theme files. + +!!! note "Feature Status" + Themes and Skins are implemented in the `BlazorWebFormsComponents.Theming` namespace and fully available for use. + +--- + +## Overview + +### What Themes and Skins Provide + +**Themes** and **Skins** solve a common problem: maintaining consistent, centralized styling across your entire application without duplicating style definitions on every control. + +In Web Forms, you would create `.skin` files containing control definitions with appearance properties: + +```xml + + +``` + +Then apply the theme to your page—and every Button would automatically adopt that styling. + +With BlazorWebFormsComponents, you achieve the same result using a **ThemeConfiguration** object and the **ThemeProvider** wrapper component. + +### Key Concepts + +- **ThemeConfiguration** — A builder object that defines styling rules for controls +- **ThemeProvider** — A wrapper component that applies a theme to all child controls +- **ControlSkin** — The styling definition for a single control type (e.g., all Buttons) +- **SubStyle** — Named style definitions for parts of complex controls (e.g., GridView headers, rows) +- **SkinID** — A named skin variant for a control (optional; enables multiple skins per control) +- **EnableTheming** — A control-level property to opt-out of theming +- **ThemeMode** — Either `StyleSheetTheme` (defaults) or `Theme` (overrides) + +--- + +## Quick Start + +### 1. Define Your Theme + +Create a `ThemeConfiguration` object that describes how your components should look: + +```csharp +// In App.razor, _Layout.razor, or a dedicated service +var myTheme = new ThemeConfiguration() + .ForControl("Button", skin => skin + .Set(s => s.BackColor, WebColor.FromHtml("#507CD1")) + .Set(s => s.ForeColor, WebColor.FromHtml("#FFFFFF")) + .Set(s => s.Font.Bold, true)) + .ForControl("Label", skin => skin + .Set(s => s.ForeColor, WebColor.FromHtml("#333333")) + .Set(s => s.Font.Names, "Arial, sans-serif")); +``` + +### 2. Apply the Theme + +Wrap your content with `` to apply the theme: + +```html + + + + + + +