Goal
ResourceInclude, StyleInclude, ThemeVariantScope, and ControlTheme for clean reuse.Why this matters
Prerequisites
App.axaml, windows, and user controls (Chapters 3-6).Avalonia ships with Fluent 2 based resources and templates. The theme lives under src/Avalonia.Themes.Fluent. Templates reference resource keys (brushes, thicknesses, typography) that resolve per theme variant.
App.axaml typically looks like this:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ThemePlayground.App"
RequestedThemeVariant="Light">
<Application.Styles>
<FluentTheme Mode="Light"/>
</Application.Styles>
</Application>
RequestedThemeVariant controls the global variant (ThemeVariant.Light, ThemeVariant.Dark, ThemeVariant.HighContrast).FluentTheme can be configured with Mode="Light", Mode="Dark", or Mode="Default" (auto based on OS hints). Source: FluentTheme.cs.Split large resource sets into dedicated files. Create Styles/Colors.axaml:
<ResourceDictionary xmlns="https://github.com/avaloniaui">
<Color x:Key="BrandPrimaryColor">#2563EB</Color>
<Color x:Key="BrandPrimaryHover">#1D4ED8</Color>
<SolidColorBrush x:Key="BrandPrimaryBrush"
Color="{DynamicResource BrandPrimaryColor}"/>
<SolidColorBrush x:Key="BrandPrimaryHoverBrush"
Color="{DynamicResource BrandPrimaryHover}"/>
</ResourceDictionary>
Then create Styles/Controls.axaml:
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="Button.primary">
<Setter Property="Background" Value="{DynamicResource BrandPrimaryBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="CornerRadius" Value="6"/>
</Style>
<Style Selector="Button.primary:pointerover">
<Setter Property="Background" Value="{DynamicResource BrandPrimaryHoverBrush}"/>
</Style>
</Styles>
Include them in App.axaml:
<Application ...>
<Application.Resources>
<ResourceInclude Source="avares://ThemePlayground/Styles/Colors.axaml"/>
</Application.Resources>
<Application.Styles>
<FluentTheme Mode="Default"/>
<StyleInclude Source="avares://ThemePlayground/Styles/Controls.axaml"/>
</Application.Styles>
</Application>
ResourceInclude expects a ResourceDictionary root and merges it into the resource lookup chain. Use it for brushes, colors, converters, and typography resources.StyleInclude expects Styles (or a single Style) and registers selectors. Use avares://Assembly/Path.axaml URIs to include styles from other assemblies (for example, avares://Avalonia.Themes.Fluent/Controls/Button.xaml).Source URI; missing includes surface as XamlLoadException during startup.StaticResource resolves once during load. Use it for values that never change (fonts, corner radius constants).DynamicResource re-evaluates when the resource is replaced at runtime--essential for theme switching.<Border CornerRadius="{StaticResource CornerRadiusMedium}"
Background="{DynamicResource BrandPrimaryBrush}"/>
Resource lookup order:
this.Resources).Application.Resources.FluentTheme (light/dark/high contrast).The implementation lives in ResourceDictionary.cs. DevTools -> Resources panel shows the chain and which dictionary satisfied a lookup.
When you need to change Fluent defaults globally (for example, switch accent colors or typography), supply variant-specific dictionaries. Place these under Application.Resources with a ThemeVariant attribute so they override the theme-provided value only for matching variants.
<Application.Resources>
<ResourceInclude Source="avares://ThemePlayground/Styles/Colors.axaml"/>
<ResourceDictionary ThemeVariant="Light">
<SolidColorBrush x:Key="SystemAccentColor" Color="#2563EB"/>
</ResourceDictionary>
<ResourceDictionary ThemeVariant="Dark">
<SolidColorBrush x:Key="SystemAccentColor" Color="#60A5FA"/>
</ResourceDictionary>
</Application.Resources>
SystemAccentColor, SystemControlBackgroundBaseLowBrush, etc.) override the defaults only for the specified variant.FluentTheme with SimpleTheme if you want the simple default look.ResourceDictionary files and create ControlTheme overrides for specific controls rather than editing Fluent templates in place.ThemeVariantScope lets you apply a specific theme to part of the UI. Implementation: ThemeVariantScope.cs.
<ThemeVariantScope RequestedThemeVariant="Dark">
<Border Padding="16">
<StackPanel>
<TextBlock Classes="h2" Text="Dark section"/>
<Button Content="Dark themed button" Classes="primary"/>
</StackPanel>
</Border>
</ThemeVariantScope>
Everything inside the scope resolves resources as if the app were using ThemeVariant.Dark. Useful for popovers or modal sheets.
Add a toggle to your main view:
<ToggleSwitch Content="Dark mode" IsChecked="{Binding IsDark}"/>
In the view model:
using Avalonia;
using Avalonia.Styling;
public sealed class ShellViewModel : ObservableObject
{
private bool _isDark;
public bool IsDark
{
get => _isDark;
set
{
if (SetProperty(ref _isDark, value))
{
Application.Current!.RequestedThemeVariant = value ? ThemeVariant.Dark : ThemeVariant.Light;
}
}
}
}
Because button styles use DynamicResource, they respond immediately. For per-window overrides set RequestedThemeVariant on the window itself or wrap content in ThemeVariantScope.
ControlThemeControlTheme lets you replace a control's default template and resources without subclassing. Source: ControlTheme.cs.
Example: create a pill-shaped toggle button theme in Styles/ToggleButton.axaml:
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="clr-namespace:Avalonia.Themes.Fluent;assembly=Avalonia.Themes.Fluent">
<ControlTheme x:Key="PillToggleTheme" TargetType="ToggleButton">
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="PART_Root"
Background="{TemplateBinding Background}"
CornerRadius="20"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>
Apply it:
<ToggleButton Content="Pill" Theme="{StaticResource PillToggleTheme}" padding="12,6"/>
To inherit Fluent visual states, you can base your theme on existing resources by referencing themes:ToggleButtonTheme. Inspect templates in src/Avalonia.Themes.Fluent/Controls for structure and named parts.
Use pseudo-classes to target interaction states. Example for ToggleSwitch:
<Style Selector="ToggleSwitch:checked">
<Setter Property="ThumbBrush" Value="{DynamicResource BrandPrimaryBrush}"/>
</Style>
<Style Selector="ToggleSwitch:checked:focus">
<Setter Property="BorderBrush" Value="{DynamicResource BrandPrimaryHoverBrush}"/>
</Style>
| Pseudo-class | Applies when |
|---|---|
:pointerover |
Pointer hovers over the control |
:pressed |
Pointer is pressed / command triggered |
:checked |
Toggleable control is on (CheckBox, ToggleSwitch, RadioButton) |
:focus / :focus-within |
Control (or a descendant) has keyboard focus |
:disabled |
IsEnabled = false |
:invalid |
A binding reports validation errors |
Pseudo-class documentation lives in Selectors.md and runtime code under Selector.cs. Combine pseudo-classes with style classes (e.g., Button.primary:pointerover) to keep state-specific visuals consistent and accessible.
Fluent ships high contrast resources. Switch by setting RequestedThemeVariant="HighContrast".
DynamicResource for all brushes so high contrast palettes propagate automatically.ThemeVariant.HighContrast.Example dictionary addition:
<ResourceDictionary ThemeVariant="HighContrast"
xmlns="https://github.com/avaloniaui">
<SolidColorBrush x:Key="BrandPrimaryBrush" Color="#00AACC"/>
<SolidColorBrush x:Key="BrandPrimaryHoverBrush" Color="#007C99"/>
</ResourceDictionary>
ThemeVariant-specific dictionaries override defaults when the variant matches.
Press F12 to open DevTools -> Styles panel:
ThemeVariant dropdown in DevTools (bottom) to preview Light/Dark/HighContrast variants.Enable style diagnostics via logging:
AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace(LogEventLevel.Debug, new[] { LogArea.Binding, LogArea.Styling })
.StartWithClassicDesktopLifetime(args);
ThemeVariantScope RequestedThemeVariant="Dark" to preview dual-theme experiences.ControlTheme for Button that changes the visual tree (e.g., adds an icon placeholder) and apply it selectively.ToggleSwitch or menu command to flip between Light/Dark; ensure all custom brushes use DynamicResource.ToggleSwitch and verify your custom styles apply in :checked and :focus states.ThemeVariantScope.csControlTheme.cs, Style.csSelector.cssrc/Avalonia.Themes.Fluent/ControlsThemeVariant.csResourceInclude and StyleInclude differ, and what root elements do they expect?ThemeVariantScope versus changing RequestedThemeVariant on the application?ControlTheme give over subclassing a control?DynamicResource for brushes that change with theme switches?ToggleSwitch or ComboBox?What's next