Avalonia’s styling system provides a flexible, CSS-like approach to defining the appearance of your application, with built-in support for themes, selectors, and dynamic styling.
Styling Fundamentals
Styles in Avalonia separate appearance from structure, allowing you to define visual properties centrally and apply them consistently.
Style Structure
A style consists of:
Selector : Defines which controls the style applies to
Setters : Define property values
Resources : Reusable values
< Window.Styles >
< Style Selector = "Button" >
< Setter Property = "Background" Value = "#007ACC" />
< Setter Property = "Foreground" Value = "White" />
< Setter Property = "Padding" Value = "12,6" />
< Setter Property = "CornerRadius" Value = "4" />
</ Style >
</ Window.Styles >
< Button Content = "Styled Button" />
Styles Class Implementation
Avalonia’s Styles class is a collection that manages style application:
Styles.cs (Simplified from source)
using Avalonia . Collections ;
using Avalonia . Styling ;
public class Styles : AvaloniaObject ,
IAvaloniaList < IStyle >,
IStyle ,
IResourceProvider
{
private readonly AvaloniaList < IStyle > _styles = new ();
private IResourceDictionary ? _resources ;
// Resource dictionary for this style collection
public IResourceDictionary Resources
{
get => _resources ?? ( Resources = new ResourceDictionary ());
set => _resources = value ;
}
// Add styles to collection
public void Add ( IStyle item ) => _styles . Add ( item );
// Try to find a resource
public bool TryGetResource ( object key , ThemeVariant ? theme , out object ? value )
{
// Check local resources first
if ( _resources != null && _resources . TryGetResource ( key , theme , out value ))
return true ;
// Then check child styles
for ( var i = Count - 1 ; i >= 0 ; -- i )
{
if ( this [ i ]. TryGetResource ( key , theme , out value ))
return true ;
}
value = null ;
return false ;
}
}
Selectors
Selectors determine which elements a style applies to, using CSS-like syntax:
Type Selectors
Basic Type
Class Selector
Multiple Classes
Name Selector
<!-- Applies to all Buttons -->
< Style Selector = "Button" >
< Setter Property = "Background" Value = "Blue" />
</ Style >
<!-- Applies to Buttons with 'primary' class -->
< Style Selector = "Button.primary" >
< Setter Property = "Background" Value = "#007ACC" />
</ Style >
< Button Classes = "primary" Content = "Primary" />
<!-- Requires both classes -->
< Style Selector = "Button.primary.large" >
< Setter Property = "FontSize" Value = "18" />
< Setter Property = "Padding" Value = "16,8" />
</ Style >
< Button Classes = "primary large" Content = "Large Primary" />
<!-- Applies to element with specific name -->
< Style Selector = "#SubmitButton" >
< Setter Property = "Background" Value = "Green" />
</ Style >
< Button Name = "SubmitButton" Content = "Submit" />
Pseudo-classes
Style elements based on their state:
< Window.Styles >
<!-- Normal state -->
< Style Selector = "Button.primary" >
< Setter Property = "Background" Value = "#007ACC" />
</ Style >
<!-- Hover state -->
< Style Selector = "Button.primary:pointerover" >
< Setter Property = "Background" Value = "#005A9E" />
</ Style >
<!-- Pressed state -->
< Style Selector = "Button.primary:pressed" >
< Setter Property = "Background" Value = "#004578" />
</ Style >
<!-- Disabled state -->
< Style Selector = "Button.primary:disabled" >
< Setter Property = "Opacity" Value = "0.5" />
</ Style >
<!-- Focused state -->
< Style Selector = "TextBox:focus" >
< Setter Property = "BorderBrush" Value = "#007ACC" />
< Setter Property = "BorderThickness" Value = "2" />
</ Style >
</ Window.Styles >
Common pseudo-classes: :pointerover, :pressed, :disabled, :focus, :selected, :checked
Descendant and Child Selectors
Descendant Selector (Space)
Direct Child Selector (>)
Template Selector (^)
<!-- Applies to TextBlocks anywhere inside StackPanel -->
< Style Selector = "StackPanel TextBlock" >
< Setter Property = "Margin" Value = "0,4" />
</ Style >
< StackPanel >
< TextBlock Text = "Styled" />
< Border >
< TextBlock Text = "Also styled" />
</ Border >
</ StackPanel >
Selector Combinators
<!-- OR selector -->
< Style Selector = "Button.primary, Button.secondary" >
< Setter Property = "Padding" Value = "12,6" />
</ Style >
<!-- AND with pseudo-class -->
< Style Selector = "Button.primary:pointerover" >
< Setter Property = "Background" Value = "#005A9E" />
</ Style >
<!-- NOT selector -->
< Style Selector = "Button:not(.primary)" >
< Setter Property = "Background" Value = "Gray" />
</ Style >
<!-- Nth-child -->
< Style Selector = "ListBoxItem:nth-child(2n)" >
< Setter Property = "Background" Value = "#F0F0F0" />
</ Style >
Resources
Resources are reusable values referenced throughout your application:
Resource Types
Colors & Brushes
Geometry & Paths
Templates
Complex Objects
< Window.Resources >
<!-- Color resource -->
< Color x:Key = "PrimaryColor" > #007ACC </ Color >
<!-- Brush resources -->
< SolidColorBrush x:Key = "PrimaryBrush" Color = "{StaticResource PrimaryColor}" />
< SolidColorBrush x:Key = "AccentBrush" Color = "#68217A" />
<!-- Gradient brush -->
< LinearGradientBrush x:Key = "GradientBrush" StartPoint = "0%,0%" EndPoint = "100%,100%" >
< GradientStop Color = "#007ACC" Offset = "0" />
< GradientStop Color = "#68217A" Offset = "1" />
</ LinearGradientBrush >
</ Window.Resources >
< Button Background = "{StaticResource PrimaryBrush}" />
< Window.Resources >
< StreamGeometry x:Key = "CheckIcon" > M 0,4 L 3,7 L 8,0 </ StreamGeometry >
< PathIcon x:Key = "SettingsIcon"
Data = "M12,8A4,4 0 0,1 8,12A4,4 0 0,1 4,8A4,4 0 0,1 8,4A4,4 0 0,1 12,8M8,0A8,8 0 0,0 0,8A8,8 0 0,0 8,16A8,8 0 0,0 16,8A8,8 0 0,0 8,0Z" />
</ Window.Resources >
< Path Data = "{StaticResource CheckIcon}" Fill = "Green" />
< Window.Resources >
< DataTemplate x:Key = "PersonTemplate" >
< StackPanel >
< TextBlock Text = "{Binding Name}" FontWeight = "Bold" />
< TextBlock Text = "{Binding Email}" />
</ StackPanel >
</ DataTemplate >
</ Window.Resources >
< ContentControl Content = "{Binding Person}"
ContentTemplate = "{StaticResource PersonTemplate}" />
< Window.Resources >
< Thickness x:Key = "StandardMargin" > 16 </ Thickness >
< CornerRadius x:Key = "ButtonCorners" > 4 </ CornerRadius >
< FontFamily x:Key = "HeaderFont" > Segoe UI </ FontFamily >
</ Window.Resources >
< Button Margin = "{StaticResource StandardMargin}"
CornerRadius = "{StaticResource ButtonCorners}" />
StaticResource vs DynamicResource
StaticResource
DynamicResource
<!-- Resolved once at load time -->
< Button Background = "{StaticResource PrimaryBrush}" />
<!-- Better performance -->
<!-- Use for resources that don't change -->
Use StaticResource for static values (colors, margins) and DynamicResource for theme-aware resources.
Resource Dictionaries
Organize resources in separate files:
Styles/Colors.axaml
App.axaml
< ResourceDictionary xmlns = "https://github.com/avaloniaui"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" >
<!-- Light theme colors -->
< Color x:Key = "BackgroundColor" > #FFFFFF </ Color >
< Color x:Key = "ForegroundColor" > #000000 </ Color >
< Color x:Key = "PrimaryColor" > #007ACC </ Color >
< SolidColorBrush x:Key = "BackgroundBrush" Color = "{StaticResource BackgroundColor}" />
< SolidColorBrush x:Key = "ForegroundBrush" Color = "{StaticResource ForegroundColor}" />
< SolidColorBrush x:Key = "PrimaryBrush" Color = "{StaticResource PrimaryColor}" />
</ ResourceDictionary >
Theme Support
Avalonia provides built-in light and dark theme support:
Theme Variants
public class ThemeVariant
{
public static readonly ThemeVariant Light ;
public static readonly ThemeVariant Dark ;
public static readonly ThemeVariant Default ; // System preference
}
Setting Application Theme
In XAML
In Code
System Default
< Application xmlns = "https://github.com/avaloniaui"
x:Class = "MyApp.App"
RequestedThemeVariant = "Dark" >
<!-- App content -->
</ Application >
using Avalonia ;
using Avalonia . Styling ;
// Set theme
if ( Application . Current is App app )
{
app . RequestedThemeVariant = ThemeVariant . Dark ;
}
// Get actual theme
var actualTheme = Application . Current ? . ActualThemeVariant ;
<!-- Follow system theme -->
< Application RequestedThemeVariant = "Default" >
<!-- Automatically switches with OS theme -->
</ Application >
Theme-Aware Resources
< ResourceDictionary >
<!-- Light theme -->
< ResourceDictionary.ThemeDictionaries >
< ResourceDictionary x:Key = "Light" >
< SolidColorBrush x:Key = "BackgroundBrush" > #FFFFFF </ SolidColorBrush >
< SolidColorBrush x:Key = "ForegroundBrush" > #000000 </ SolidColorBrush >
</ ResourceDictionary >
<!-- Dark theme -->
< ResourceDictionary x:Key = "Dark" >
< SolidColorBrush x:Key = "BackgroundBrush" > #1E1E1E </ SolidColorBrush >
< SolidColorBrush x:Key = "ForegroundBrush" > #FFFFFF </ SolidColorBrush >
</ ResourceDictionary >
</ ResourceDictionary.ThemeDictionaries >
</ ResourceDictionary >
<!-- Use with DynamicResource -->
< Border Background = "{DynamicResource BackgroundBrush}" />
Fluent Theme
Avalonia includes a Fluent Design theme:
< Application xmlns = "https://github.com/avaloniaui"
xmlns:sty = "using:Avalonia.Themes.Fluent"
x:Class = "MyApp.App" >
< Application.Styles >
< sty:FluentTheme />
</ Application.Styles >
</ Application >
Custom Theme
CustomTheme.axaml
App.axaml
< Styles xmlns = "https://github.com/avaloniaui"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" >
<!-- Define color palette -->
< Style >
< Style.Resources >
< ResourceDictionary >
< ResourceDictionary.ThemeDictionaries >
< ResourceDictionary x:Key = "Light" >
< Color x:Key = "SystemAccentColor" > #007ACC </ Color >
< SolidColorBrush x:Key = "SystemAccentBrush" Color = "{StaticResource SystemAccentColor}" />
</ ResourceDictionary >
< ResourceDictionary x:Key = "Dark" >
< Color x:Key = "SystemAccentColor" > #0098FF </ Color >
< SolidColorBrush x:Key = "SystemAccentBrush" Color = "{StaticResource SystemAccentColor}" />
</ ResourceDictionary >
</ ResourceDictionary.ThemeDictionaries >
</ ResourceDictionary >
</ Style.Resources >
</ Style >
<!-- Apply styles -->
< Style Selector = "Button" >
< Setter Property = "Background" Value = "{DynamicResource SystemAccentBrush}" />
</ Style >
</ Styles >
Control Themes
Control themes provide complete visual definitions:
< Styles >
< ControlTheme x:Key = "CustomButtonTheme" TargetType = "Button" >
<!-- Default setters -->
< Setter Property = "Background" Value = "#007ACC" />
< Setter Property = "Foreground" Value = "White" />
< Setter Property = "Padding" Value = "12,6" />
< Setter Property = "CornerRadius" Value = "4" />
<!-- Template -->
< Setter Property = "Template" >
< ControlTemplate >
< Border Background = "{TemplateBinding Background}"
CornerRadius = "{TemplateBinding CornerRadius}"
Padding = "{TemplateBinding Padding}" >
< ContentPresenter Content = "{TemplateBinding Content}" />
</ Border >
</ ControlTemplate >
</ Setter >
<!-- State styles -->
< Style Selector = "^:pointerover" >
< Setter Property = "Background" Value = "#005A9E" />
</ Style >
< Style Selector = "^:pressed" >
< Setter Property = "Background" Value = "#004578" />
</ Style >
</ ControlTheme >
</ Styles >
<!-- Apply theme -->
< Button Theme = "{StaticResource CustomButtonTheme}" Content = "Themed Button" />
Style Precedence
Styles apply in this order (later wins):
Application styles (App.axaml)
Window/UserControl styles
Parent styles (inherited)
Local styles (on the control)
Inline properties (highest priority)
<!-- Application style -->
< Application.Styles >
< Style Selector = "Button" >
< Setter Property = "Background" Value = "Blue" />
</ Style >
</ Application.Styles >
<!-- Window style (overrides application) -->
< Window.Styles >
< Style Selector = "Button.important" >
< Setter Property = "Background" Value = "Red" />
</ Style >
</ Window.Styles >
<!-- Inline property (highest precedence) -->
< Button Classes = "important" Background = "Green" />
<!-- This button will be GREEN -->
Animations in Styles
< Style Selector = "Button:pointerover" >
< Style.Animations >
< Animation Duration = "0:0:0.2" FillMode = "Forward" >
< KeyFrame Cue = "0%" >
< Setter Property = "Background" Value = "#007ACC" />
</ KeyFrame >
< KeyFrame Cue = "100%" >
< Setter Property = "Background" Value = "#005A9E" />
</ KeyFrame >
</ Animation >
</ Style.Animations >
</ Style >
Best Practices
Use resource dictionaries
Organize colors, brushes, and styles in separate resource dictionary files.
Prefer classes over names
Use class selectors (.primary) instead of name selectors (#SubmitButton) for reusability.
Theme-aware resources
Use ThemeDictionaries and DynamicResource for proper light/dark theme support.
Keep selectors simple
Avoid overly complex selectors for better performance and maintainability.
Test both themes
Always test your app in both light and dark themes.